Raytracing One Weekend 식 이해하기! 5
이번 시간에는 Antialiasing에 대해 알아볼 것이다!!! 안티앨리어싱!! 어디선가 많이 들어보지 않았던가?
게임을 해봤다면 그래픽 옵션에 안티앨리어싱 ON/OFF 속성이 있는 것을 한번쯤은 봤을 것이다!!
무슨게임인진 모르겠지만 일단 들고와봤다...
그렇다면 안티앨리어싱이 뭘까? 한국말로 번역하면 계단 현상 방지라고 한다.. 결과부터 보고가자!
왼쪽의 사진은 안티앨리어싱을 하지 않은 상태, 오른쪽은 최대 수준으로 안티 앨리어싱을 했을 때이다!
차이가 많이 느껴진다! 확실히 오른쪽사진의 계단현상(?)이 줄어들었다!! 하지만 역시 직접 해보기 전까지는 애매하다! 우리가 만든 구를 활용해서 antialiasing을 적용해보자!!
Antialiasing
그렇다면 안티앨리어싱을 도대체 어떻게 만들까???
책에서는 아주 간단한 해답을 제시한다! 아래 그림을 보자!
기존의 방식으론 위의 사진처럼 구 위의 한 점으로 한번의 ray만 쏜다.
하지만 우리는 해당 지점 주변에 다량의 ray를 보내서 주변의 색깔들의 평균치를 구할 것이다!
이때 ray를 쏠 수 있는 범위는 기존 ray에서 [0, 1) 값을 더한 값 까지이다.
그래서 우리는 랜덤으로 ray를 쏘게 도와줄 random함수를 만들 것이다! 본문에는 아래와 같이 나와있고,
#include <cstdlib>
...
inline double random_double() {
// Returns a random real in [0,1).
return rand() / (RAND_MAX + 1.0);
}
inline double random_double(double min, double max) {
// Returns a random real in [min,max).
return min + (max-min)*random_double();
}
C++
복사
C로는 아래와 같이 만들 수 있다. (0 ~ 1 사이 값을 얻기 위해서 rand() 함수를 RAND_MAX값으로 나눠주었다.)
#include <stdlib.h>
double rand_num()
{
return ((double)rand() / (double)RAND_MAX);
}
C
복사
여기서 한가지 문제가 발생하는데, ray를 random으로 쏜 후 나오는 color값을 더하는 과정에서 각 color의 값들이 255을 넘을 수 있다는 것이다! 그래서 책에서는 한가지 해법을 제공한다. 바로 clamp함수를 사용해서 각 성분의 값이 0보다 작거나 0.999보다 크다면, 0과 0.999로 값을 고정시켜버리는 것이다.
inline double clamp(double x, double min, double max) {
if (x < min) return min;
if (x > max) return max;
return x;
}
C
복사
이것을 적용한 본문의 코드는 아래와 같다.
// C++ code
void write_color(std::ostream &out, color pixel_color, int samples_per_pixel) {
auto r = pixel_color.x();
auto g = pixel_color.y();
auto b = pixel_color.z();
// Divide the color by the number of samples.
auto scale = 1.0 / samples_per_pixel;
r *= scale;
g *= scale;
b *= scale;
// Write the translated [0,255] value of each color component.
out << static_cast<int>(256 * clamp(r, 0.0, 0.999)) << ' '
<< static_cast<int>(256 * clamp(g, 0.0, 0.999)) << ' '
<< static_cast<int>(256 * clamp(b, 0.0, 0.999)) << '\n';
}
C++
복사
우리는 rgb를 출력하는 것이 아니라 메모리에 기록해야하기 때문에 아래와 같이 함수를 작성하면 된다!
이때, anti값이 0이 들어오면 scale = 1 / (double)anti; 부분에서 예상치 못한 에러가 발생할 수 있기 때문에 anti가 0일때, 1로 만들어주는 부분이 필요하다!
// C code
static int trgb_anti(t_vec *colors, int anti)
{
double scale;
if (anti == 0)
anti = 1;
scale = 1 / (double)anti;
colors->x = clamp((colors->x * scale), 0, 0.999);
colors->y = clamp((colors->y * scale), 0, 0.999);
colors->z = clamp((colors->z * scale), 0, 0.999);
return trgb(0, colors->x * 256, colors->y * 256, colors->z * 256);
}
C
복사
이제 Render 부분에서 u, v를 random함수를 활용해서 조정해주기만 하면 된다!
// C++ code
color pixel_color(0, 0, 0);
for (int s = 0; s < samples_per_pixel; ++s)
{
auto u = (i + random_double()) / (image_width-1);
auto v = (j + random_double()) / (image_height-1);
ray r = cam.get_ray(u, v);
pixel_color += ray_color(r, world);
}
write_color(std::cout, pixel_color, samples_per_pixel);
C++
복사
이때 주의해야할 부분이 있는데 만약 antialiasing을 0번 하려고 했을때, 구가 삐뚤어지게 나오는 현상을 볼 수 있다. 위의 코드는 antialiasing을 하던 말던 무조건 random좌표의 색을 가져오기 때문이다!
우리는 0번 했을때의 기댓값을 정확하게 가져오기 위해 아래와 같은 추가 조건을 넣어줄 것이다!
#include <stdlib.h>
double rand_num(int anti)
{
if (anti == 0) // anti 횟수를 변수로 받아와서 anti가 0 이라면 return 0;
return (0);
else
return ((double)rand() / (double)RAND_MAX);
}
C
복사
Make!
이것을 토대로 코드를 조금만 따라해보면 우리도 드디어 antialiasing을 적용시킬 수 있다!!!
한번 100번까지 도전해보자! (100번하면 시간이 100배로 걸린다..)