Search
Duplicate
📦

(4) Raytracing One Weekend 식 이해하기! 1

간단소개
팔만코딩경 컨트리뷰터
ContributorNotionAccount
주제 / 분류
42seoul
Scrap
태그
9 more properties
minirt 뽀개기!

Raytracing in one weekend 식 이해하기!

Raytracing in one weekend 문서를 보다보면 코드는 알겠는데 대체 이 코드가 무엇을 의미하는지 이해하기 어려울 때가 많다.. 대부분 수학식을 코드로 변경한건데 이전 벡터에 대한 지식이 없으면 이 코드들의 역할도 이해하기 어렵다!
아직 벡터에 대해 조금 어려운 부분이 있다면 한번 더 복습하고 아래 내용을 참고하길 바란다! 이제 한줄 한줄 Raytracing in one weekend(이하 ROW)의 식을 이해해보자!

The vec3 Class

ROW 문서의 3장인 vec3 Class 에서는 이후 우리가 사용할 벡터들과 해당 벡터들을 연사하는데 필요한 함수들에대해 나와있다. 한 줄 한 줄 해당 함수가 어떤 역할을 하는지 알아보자!
C++ 문법에 익숙하지 않다면 minirt를 위한 C++ 문법 간단 정리! 페이지를 참고하길 바란다!! 해당 문서를 읽고 온것으로 간주하고 아래 내용을 진행하겠다!
class vec3 { public: vec3() : e{0,0,0} {} vec3(double e0, double e1, double e2) : e{e0, e1, e2} {} double x() const { return e[0]; } double y() const { return e[1]; } double z() const { return e[2]; } vec3 operator-() const { return vec3(-e[0], -e[1], -e[2]); } double operator[](int i) const { return e[i]; } double& operator[](int i) { return e[i]; } ... public: double e[3];
C++
복사

1) 벡터의 성분

vec3 클래스는 x, y, z 성분을 가진다. 책에서는 double[3] 배열을 선언해서 사용하고 있다. 이 성분들은 한 벡터를 각 축 위의 성분들로 분해한 값들을 가지고 있는 것이다.
즉, A 벡터는 vec3(x, y, z)로 표현할 수 있다.
이제 벡터 성분을 포함한 코드를 C로 작성해보면 아래와 같다.
typedef struct s_vec { double x; double y; double z; } t_vec;
C
복사
생성자를 만들 순 없으니 초기화 함수를 만들어보자
t_vec vec(double x, double y, double z) { t_vec out; out.x = x; out.y = y; out.z = z; return (out); }
C++
복사
아래와 같이 활용할 수 있다.
t_vec origin = vec(0, 0, 0);
C
복사

2) 벡터의 사칙연산

아래 나오는 것들은 멤버함수로 벡터의 연산을 위한 함수들이다.
A = 자기자신 벡터 U = 벡터 1 V = 벡터 2 t = 특정 스칼라 값
벡터 A + 벡터 v
vec3& operator+=(const vec3 &v) { e[0] += v.e[0]; e[1] += v.e[1]; e[2] += v.e[2]; return *this; }
C++
복사
벡터 A x 스칼라 t
vec3& operator*=(const double t) { e[0] *= t; e[1] *= t; e[2] *= t; return *this; }
C++
복사
A÷tA \div t A×1tA \times{ 1 \over{t} }
vec3& operator/=(const double t) { return *this *= 1/t; }
C++
복사
|A| (A 벡터의 크기)
double length() const { return sqrt(length_squared()); } double length_squared() const { return e[0]*e[0] + e[1]*e[1] + e[2]*e[2]; }
C++
복사
피타고라스 정리를 활용해서 A벡터의 크기를 계산한다!
A=x2+y2+z2|A| = \sqrt{x^2 + y^2 + z^2}
이제 위 식들을 이해했으니 C 언어로 변경해보자!
t_vec vec_add(t_vec *v1, t_vec *v2) { t_vec out; out.x = v1->x + v2->x; out.y = v1->y + v2->y; out.z = v1->z + v2->z; return (out); } t_vec vec_sub(t_vec *v1, t_vec *v2) { t_vec out; out.x = v1->x - v2->x; out.y = v1->y - v2->y; out.z = v1->z - v2->z; return (out); } t_vec vec_mul(t_vec *v1, double t) { t_vec out; out.x = v1->x * t; out.y = v1->y * t; out.z = v1->z * t; return (out); } ... 이런 방식으로 나머지 함수들도 구현해보자!
C
복사
이런식으로 한번에 연산을 처리할 수도 있다..
아래 부분은 vec3클래스를 재활용 하는 부분이다!
using point3 = vec3; // 3D point using color = vec3; // RGB color
C++
복사
point3의 경우 3차원 좌표계 위의 특정 한 점을 나타낸다! 이 경우에도 double[3] 배열로 표현할 수 있다. (어차피 좌표가 (x, y, z)이기 때문에!)
color의 경우에도 vec3클래스를 재활용할 수 있다. x = R, y = G, z = B 로 생각하고 사용하면 된다!
이렇게 하면 따로 color 클래스, point3클래스를 만들지 않아도 코드 재활용이 가능하다!

3) vec3 Utility Function

아래 나오는 utility function은 멤버 함수와 겹치는 것들이 있다. C로 변환하는 과정에서 필요없는 부분과 이미 위의 멤버함수 에서 중복되는 부분은 설명을 넘어가도록 하겠다!
벡터의 내적
// C++ inline double dot(const vec3 &u, const vec3 &v) { return u.e[0] * v.e[0] + u.e[1] * v.e[1] + u.e[2] * v.e[2]; } // C double vec_dot(t_vec *v1, t_vec *v2) { return (v1->x * v2->x + v1->y * v2->y + v1->z * v2->z); }
C++
복사
벡터의 외적
// C++ inline vec3 cross(const vec3 &u, const vec3 &v) { return vec3(u.e[1] * v.e[2] - u.e[2] * v.e[1], u.e[2] * v.e[0] - u.e[0] * v.e[2], u.e[0] * v.e[1] - u.e[1] * v.e[0]); } // C t_vec vec_cross(t_vec *v1, t_vec *v2) { t_vec out; out.x = v1->y * v2->z + v1->z * v2->y; out.y = v1->z * v2->x + v1->x * v2->z; out.z = v1->x * v2->y + v1->y * v2->x; return (out); }
C++
복사
단위벡터 만들기
// C++ inline vec3 unit_vector(vec3 v) { return v / v.length(); } // C t_vec vec_unit(t_vec v) { double len; len = vec_length(v); return (vec(v.x / len ,v.y / len ,v.z / len)); }
C++
복사
내적과 외적, 단위벡터에 대한 설명은 (3) 벡터의 연산! 에 모두 정리해 두었으니 혹시 기억이 나지 않는다면 다시 보도록 하자!

4) Color Utility Function

ROW와 minirt의 차이점이 조금 있는데 우리 과제는 바로 mlx를 활용해서 화면을 만들어야 한다는 것이다. mlx 에서는 my_mlx_pixel_put(&image, x, y, color) 함수를 활용해서 이미지에 색을 표현하는데 이때 나오는 color의 값은 int 이다.
본문에서는 R G B 형식으로 출력을 하게 되어있지만 우리는 이 R, G, B 값을 하나의 int 값으로 만들어야 한다. 따라서 조금 식을 수정한 형태로 설명을 하겠다. 아래는 기존 본문의 코드이다.
void write_color(std::ostream &out, color pixel_color) { // Write the translated [0,255] value of each color component. out << static_cast<int>(255.999 * pixel_color.x()) << ' ' << static_cast<int>(255.999 * pixel_color.y()) << ' ' << static_cast<int>(255.999 * pixel_color.z()) << '\n'; }
C++
복사
위 코드를 변경해서 RGB를 하나의 int 로 만들려면 아래와 같이 사용하면 된다.
int write_color(int t, t_color *pixel_color) { return (t << 24 | (255 * pixel_color->x) << 16 | (255 * pixel_color->y) << 8 | (255 * pixel_color-z)); }
C++
복사
아래와 같이 사용할 수 있다.
my_mlx_pixel_put(&image, x, y, write_color(t, *pixel_color));
C++
복사

Prev

Reference