# Raytracer 제작기: 1년 간의 여정
## 1. Raytracing이란?
* 빛이 물체에서 반사되는 방식을 시뮬레이션하여 매우 사실적인 이미지를 생성하는 기술
* 실제 세계: 태양/램프 → 빛 방출 → 물체 반사 → 눈으로 도달 → 뇌 해석 → 시각 인지
* **비효율성**: 모든 빛의 경로를 추적하는 것은 비효율적 (대부분의 빛이 눈에 도달하지 않기 때문)
* **Raytracing의 아이디어**: 눈(카메라)에서 시작하여 빛을 쏘고, 반사되어 광원에 도달하면 해당 픽셀에 빛이 켜짐.
* 모든 픽셀은 고유한 Ray와 연결됨.
* Ray의 색상은 반사된 물체의 색상에 따라 결정됨.
* 빨간 큐브에 반사 → 빨간 픽셀
* 빛에 도달하지 못함 → 검은색 픽셀
## 2. 초기 목표
* **실시간 렌더링**: 초당 3프레임에서 수십만 개의 삼각형 동시 렌더링 목표
* **Full HD 해상도**: 1920x1080 픽셀
## 3. Raytracer 구현 과정
### 3.1. 카메라 구현
* **핵심 아이디어**: 3D 공간의 카메라 위치에서 시작하는 세 개의 직교 벡터 (x, y, z 방향)
* **벡터**: 방향과 원점(시작점)을 가지는 화살표. 3개의 컴포넌트(x, y, z)로 3D 벡터 표현.
* **벡터 연산**: 덧셈, 뺄셈, 곱셈 가능.
* **정규화 (Normalization)**: 벡터의 방향은 유지하면서 길이를 1로 만드는 과정 (피타고라스 정리 활용).
* Forward, Right, Up 벡터로 명명.
* **카메라 회전**:
* **쿼터니언**: 복잡하여 사용하지 않음.
* **삼각 함수 (Trigonometry)**: Sine, Cosine을 활용하여 벡터 회전.
* 회전 행렬(Rotation Matrix)을 사용하여 벡터를 특정 축으로 회전.
* **회전 순서 중요**: Pitch(z축 회전) → Yaw(y축 회전)
* 카메라 위치가 0,0,0일 때 회전 후 실제 위치로 이동.
* **카메라 종류**:
* **일반 카메라**: Forward 키를 누르면 카메라가 Forward 벡터 방향으로 이동.
* **비행 시뮬레이터 카메라**: Forward 키를 누르면 카메라가 바라보는 방향으로 이동. Roll(세 번째 자유도) 제어 가능.
### 3.2. Ray 생성 및 관리
* **Ray**: 카메라 원점에서 시작하는 벡터.
* **카메라 속성**:
* **종횡비 (Aspect Ratio)**: 화면의 가로세로 비율 (예: 16:9, 4:3).
* **시야각 (Field of View, FOV)**: 카메라에서 화면 가장자리까지 연결하는 두 선 사이의 각도. 초점 거리 결정.
* **Ray 방향 계산**:
1. Full HD 해상도, 종횡비, 초점 거리를 이용하여 화면 상의 각 픽셀의 3D 좌표 결정.
2. 카메라 위치와 픽셀 좌표를 이용해 Ray 방향 계산 (픽셀 좌표 - 카메라 위치).
3. Ray 방향 정규화.
* **Local to World Matrix (Model Matrix)**:
* Forward, Up, Right 벡터를 이용해 카메라 회전 및 위치를 반영하여 Ray를 월드 공간으로 변환.
* 이 행렬을 각 Ray에 적용.
### 3.3. Python에서의 초기 구현 및 성능 문제
* **Python 코드**: 각 Ray에 대한 기본 방향 계산 후 Model Matrix로 회전 및 변환.
* **성능**: 프레임당 18분 소요 (매우 비효율적).
### 3.4. C++ 전환 및 성능 향상
* **C++ 코드**:
* 픽셀 색상을 Ray 방향으로 설정하여 시각화.
* Ray의 X, Y, Z 컴포넌트를 RGB 값으로 매핑. (음수 값은 0으로 처리)
* **성능**: 초당 25프레임 달성 (Python 대비 대폭 향상). Full HD에서는 초당 3프레임.
### 3.5. GPU 활용 (OpenGL & GLSL)
* **GPU의 역할**: 그래픽 처리에 특화되어 병렬 처리에 강점.
* **CPU vs GPU**:
* **CPU**: 순차적 처리. 2백만 개 이상의 픽셀에 대한 Ray 계산에 병목 발생.
* **GPU**: 병렬 처리. 수많은 Ray를 동시에 처리하여 속도 향상.
* **Shader**: GPU에서 실행되는 프로그램.
* **GLSL (OpenGL Shading Language)**: C++와 유사.
* **Fragment Shader (Pixel Shader)**: 개별 픽셀 색상 결정.
* 기존 CPU 코드의 Ray 계산 로직을 Fragment Shader로 이전.
* 대규모 Ray 병렬 처리 가능.
* **성능**: CPU의 거대한 for 루프 제거 후 GPU에서 병렬 처리로 속도 대폭 향상.
### 3.6. 객체 렌더링: Sphere
* **Sphere 정의**: 중심점(Origin)과 반지름(Radius).
* **Ray-Sphere Intersection**:
* Ray가 Sphere와 충돌하는지 확인하는 함수 구현 (Sebastian Lake의 영상 참고).
* **Hit Info Struct**: 충돌 여부, 충돌 거리, 충돌 지점(Hit Point), 법선 벡터(Normal Vector) 정보 포함.
* **초기 렌더링**: 충돌 시 흰색, 미충돌 시 검은색으로 렌더링.
* **다중 Sphere 렌더링**:
* 가장 가까운 Sphere를 찾아 렌더링 (먼 Sphere가 가까운 Sphere를 가리지 않도록).
* Sphere마다 다른 색상 적용.
### 3.7. 재질 (Materials) 및 광원
* **물질의 표면**:
* **매끄러운 표면**: 빛이 규칙적으로 반사 (Specular Reflection). 예: 물 표면.
* **거친 표면**: 빛이 무작위로 산란 (Diffuse Reflection).
* **색상**: 물체가 특정 파장의 빛을 흡수하고 반사하는 방식.
* 파란색 물체: 빨강, 초록 흡수, 파랑 반사.
* 흰색 물체: 모든 파장 반사.
* 검은색 물체: 모든 파장 흡수 (태양열 더 빠르게 흡수).
* **Material Struct**:
* **Color**: 물체의 기본 색상.
* **Emission Strength & Emission Color**: 광원 역할을 하는 경우.
* **Ray Tracing 로직**:
1. 카메라에서 Ray 시작 (초기 색상: 흰색).
2. **Loop**:
* 충돌 확인. 충돌 없으면 break.
* 충돌 시:
* Ray 색상과 물체 Material 색상 곱하여 새로운 Ray 색상 계산 (빛 흡수 시뮬레이션).
* 충돌 지점을 새로운 Ray의 원점으로 하여 재귀적 Bounce.
* **Diffuse Reflection**: 반구 내 랜덤 방향으로 Ray 재발사.
3. **최종 픽셀 색상**:
* **Brightness Score**: Ray 색상과 물체의 Emission Light(Emission Strength * Emission Color)를 곱하여 누적.
* 최대 Bounce 횟수 도달 또는 충돌 없을 시 Brightness Score 반환.
### 3.8. 성능 최적화: Accumulation & UI
* **Accumulation**: 여러 프레임의 결과를 누적하여 노이즈 감소.
* 두 개의 텍스처 사용 (현재 프레임 렌더링, 이전 프레임 기반).
* 카메라 이동 시 Accumulation 초기화.
* **UI (IMGUI)**:
* 카메라 위치, 회전, FOV, 종횡비, Ray 수, Bounce 수, Accumulation 토글 등 설정 제어.
* Viewport 창 크기 조절로 해상도 변경 가능.
* 프레임 레이트 및 ms/frame 측정.
### 3.9. Skybox 구현
* **기존 문제**: Ray가 아무것도 충돌하지 않으면 검은색으로 렌더링.
* **Skybox**: Ray가 아무것도 충돌하지 않을 때, 주변 환경의 색상(Ray 색상과 곱해짐)을 배경으로 사용.
* 색상 일관성 향상 및 깊이감 부여.
### 3.10. Arbitrary Shape 렌더링: Triangle
* **Triangle 정의**: 3개의 점(Vertices) + 3개의 법선 벡터 + Material.
* **Ray-Triangle Intersection**:
* **Moliere-Turmbor 알고리즘** 사용 (빠른 교차 검사).
* Sphere와 동일하게 충돌 시 색상 변경.
* **수백 개 Triangle 렌더링**:
* **성능 문제**: 랙 발생.
* **Graphics Pipeline & Compute Shader**:
* **Fragment Shader**: Triangle의 픽셀 색상만 결정 가능.
* **Vertex Shader**: 화면 크기의 Quad를 생성하는 역할.
* **Compute Shader**: 그래픽 파이프라인과 독립적인 GPU 프로그램. Ray tracing에 더욱 적합.
* CPU 코드 단순화 및 성능 대폭 향상.
### 3.11. 성능 최적화: Bounding Volume Hierarchy (BVH)
* **문제**: 수많은 Primitive (Sphere, Triangle)과의 Ray 교차 검사가 병목.
* 232개 Primitive x (1920x1080) 픽셀 x 5 Bounces = 25억 개 이상의 교차 검사/프레임.
* **BVH 아이디어**: 공간을 계층적으로 분할하여 불필요한 교차 검사 감소.
* **AABB (Axis Aligned Bounding Box)**: 각 노드는 축에 정렬된 경계 상자(AABB)를 가짐.
* **BVH (Bounding Volume Hierarchy)**: AABB들을 트리 형태로 구성.
* Ray가 AABB와 충돌하지 않으면, 해당 AABB 내의 모든 Primitive 검사 생략.
* **BVH 구조**:
* **Node**: AABB 포함.
* **Non-leaf Node**: 다른 BVH Node 참조.
* **Leaf Node**: Triangle, Sphere 등의 Primitive 포함.
* **BVH Construction**:
* **Root Node**: 전체 Scene을 포함하는 AABB.
* **Splitting Strategy**: AABB를 더 작은 AABB로 분할.
* **Midpoint Split**: 가장 긴 축을 따라 중간 지점에서 분할.
* **Median Split**: Triangle Centroid를 기준으로 중앙값에서 분할 (더 균형 잡힌 트리 생성).
* **Surface Area Heuristic (SAH)**: 분할 비용(계산 시간)을 최소화하는 방식으로 분할.
* `Cost = PA * SA_A * (T_isect_A * N_A + T_traverse) + PB * SA_B * (T_isect_B * N_B + T_traverse)`
* `PA, PB`: AABB 내에서 Child A, B에 교차할 확률.
* `SA_A, SA_B`: Child A, B의 표면적.
* `T_isect_A, T_isect_B`: Primitive 교차 검사 시간.
* `N_A, N_B`: AABB 내 Primitive 개수.
* `T_traverse`: AABB 교차 검사 시간.
* **Bucket Method**: 대규모 데이터셋의 빠른 BVH 구축을 위해 적용. AABB를 여러 버킷으로 나누어 분할 비용 계산.
* **BVH Traversal**:
* Ray가 BVH 트리를 따라 탐색하며 충돌하는 Primitive 찾기.
* **Stack 사용**: 재귀 대신 반복적인 탐색.
* **GPU로 BVH 전송**:
* GPU 디버깅 어려움으로 인한 문제 발생.
* **성능**: 400 FPS (나무 모델 근접 시) 달성.
* **다양한 모델 렌더링**: Stanford Bunny (5,000 triangles), Stanford Dragon (20,000 triangles).
### 3.12. Intersection Count 시각화
* Ray가 Triangle을 찾기까지 수행한 교차 검사 횟수를 RGB 색상으로 시각화.
* **Gradient of Light Wavelengths**: BVH의 분할 비용을 히트맵 형태로 시각화.
* Surface Area Heuristic 기반 BVH가 Midpoint Split보다 더 많은 파란색 영역(적은 교차 검사)을 보여줌.
### 3.13. Sponza Scene
* 복잡한 Scene 렌더링 시도.
* **현재 Raytracer의 한계**: 대규모 Scene 렌더링에 어려움.
* **향후 계획**: 추가 최적화 진행 예정.
---
**결론**: 1년 간의 노력 끝에 Python에서 시작하여 C++, GLSL, Compute Shader, BVH 등의 최적화 기법을 적용하여 대규모 Scene을 렌더링할 수 있는 Raytracer를 구현. GPU의 강력한 병렬 처리 능력을 활용하고, 복잡한 가속 구조인 BVH를 구현하여 성능을 극대화함.