그래픽스
miniRT: 레이 트레이서
Phong shading / reflective CPU ray tracer

과제 제약상 GPU 사용이 금지된 환경에서 C로 구현한 CPU ray tracer입니다. 카메라에서 광선을 발사하고, 기본 도형과의 교차점을 구한 뒤 조명과 그림자를 계산하는 전체 과정을 직접 구현했습니다.
레이 트레이싱
- 구, 평면, 원기둥, 삼각형과의 ray 교차
- 거울 표면을 위한 제한 깊이의 재귀 반사
- 광원별 가시성을 검사하는 그림자 ray
- 가장 가까운 교차점과 재질을 선택하는 교차 판정 흐름
Phong shading
표면의 최종 색은 세 가지 조명 성분을 조합해 계산합니다.
- Ambient: 직접광이 없는 영역의 기본 밝기
- Diffuse: Lambert 코사인 법칙 기반 확산광
- Specular: 시선과 반사 벡터를 이용한 강조광
카메라
시야각을 기준으로 화면 화소마다 카메라 광선을 생성합니다. 로컬, 월드, 카메라 좌표계 변환을 구성하고 키보드 입력으로 카메라 위치와 방향을 실시간 조작할 수 있게 했습니다.
SIMD 최적화
Why?
- 과제 제약상 GPU를 사용할 수 없어 ray intersection과 shading에서 반복되는 vector, matrix 연산을 CPU에서 줄여야 했음.
- CPU 최적화 기법들을 찾다 보니 SIMD가 적절할 것이라 판단
- 하지만 SIMD는 register 활용도, 수평 합산 비용, 함수 호출 경계에 따라 짧은 연산에서 scalar보다 느려질 수 있었음.
- 즉, 제대로 이해하고 잘 써야 했음
How?
- 4차원 vector와 4×4 matrix를 SSE/AVX register에 어떻게 배치하고 aligned load 조건을 보장할지 고민했음.
- 모든 연산이 빨라진다고 가정하는 방식을 배제하고, compiler auto-vectorization을 끈 scalar 구현과 같은 입력으로 비교했음.
Solution
- Vector는
__m128하나에xyzw를 담고 matrix는__m256두 묶음으로 처리했으며, 시작 주소를 64 byte로 정렬해 aligned load 조건을 보장했음. - Ryzen 7 7800X3D, GCC 6.3,
-O3에서 각 연산을 2,000,000회 실행하고 5회 반복한 중앙값을 비교했음. - Matrix × matrix는 117.72 ns에서 9.61 ns로 12.25배, matrix × vector는 10.27 ns에서 7.12 ns로 1.44배 빨라졌지만 4차원 dot product는 7.92 ns에서 20.20 ns로 느려졌음.
소소하게 해결한 문제들
원기둥 구현
무한히 늘어나는 원기둥의 위, 아래 뚜껑, 옆면을 별도로 계산했습니다.
옆면은 높이 범위로 잘라내고, 모든 후보 중 가장 가까운 양의 t를 선택했습니다.
SIMD 메모리 정렬하기
32바이트 정렬을 요구하는 AVX 정렬 불러오기에 조건을 충족하지 않는 주소를 전달하면 잘못된 메모리 접근으로 이어질 수 있기 때문에, 구조체와 동적 할당 주소의 정렬을 보장해 정렬 불러오기의 전제 조건을 맞췄고, 64바이트로 정렬해 행렬 크기와 캐시 라인에 맞게 구현
배운 점
- 그래픽스 기초 입문
- SIMD, 메모리 정렬, 캐시를 고려한 low-level 최적화