Frame at 0.00s
# 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를 구현하여 성능을 극대화함.