본문 바로가기

개발/개발 일기

2021.7.16 개발 일기 - 레이트레이서를 만들 때, Whitted 방식의 레이트레이서를 유지하자

이전부터 레이트레이싱에 관심이 있어, 직접 레이트레이서를 활용한 렌더러를 개발하는 사이드 프로젝트를 진행하고 있었다. 최근 DXR, RTX 등의 등장으로 이전보다는 정보의 양이 많이 늘었다고 하지만, 현재에도 인터넷을 검색하면 자료가 많이 없다는 생각이 드는데, 이전에는 얼마나 없었을지 상상이 가지 않는다.

 

아무튼, 자료의 양이 늘었다고 하더라도, 내가 진행하는 오프라인 렌더레에 관한 자료는 전체적인 양이 부족하다는 생각이 든다. 물론 간단하게 레이트레이싱을 설명한 Raytracing in one weekend 등의 짧은 책도 있지만, 이 책은 정말 레이트레이서를 따라하면서 만들어 보자는 의미의 책이기도 하고, 구현에 대한 설명도 개인적으로는 매우 빈약하다고 생각이 든다. 실제로도 레이트레이서를 개발하는 동안 이 책을 참고한 적은 없다. 

 

물론 책을 따라서 만드는 것도 충분한 공부가 된다고 생각이 들지만, 모든 것은 실제로 만들어보지 않으면 제대로 익힐 수가 없다고 생각을 하기 때문에, 레이트레이서를 직접 설계하고 제작하고 있었다(물론 막히는 부분이 생기면, 책을 참고하기도 한다). 레이트레이서를 떠나서 어떠한 프로그램을 작성할 때, 누군가 완성한 코드를 배끼는 것이 아니라 직접 만들다 보면, 중간 과정에서 수없이 많은 버그를 만들게 되고, 정확한 정답도 없는 디버깅을 해야하는 경우가 많이 존재한다. 

 

특히 이전에 진행했던 프로젝트와 다르게 레이트레이서를 제작하면서 생기는 많은 수의 버그들은 단순 초기화나 하드웨어 설정에서 문제가 아닌, 정말 내가 작성한 로직의 문제였기 때문에 디버깅을 하는 과정이 항상 힘들었었다. 

 

최근에 발생한 버그 중 하나는 레이트레이서에 패스트레이싱 방식의 적분기를 추가하고, 렌더링 속도 향상을 위해서 BVH 가속 구조를 추가하면서 만나게 된 버그이다. 이미지의 차이는 아래와 같다.

 

그림 1) BVH 가속 구조를 이용한 패스트레이싱 이미지
그림 2) 가속 구조 없이 렌더링한 패스트레이싱 이미지

 

위 두 사진을 보면 그림 1의 가운데의 굴절된 이미지를 보면 초록색 원의 특정 부분이 갈라져 있는 것을 볼 수 있다. 하지만 가속 구조 없이 렌더링을 하면 이러한 현상이 사라진다. 처음에는 이 문제가 가속 구조를 추가하면서 생겼기 때문에, Ray - AABB 사이의 교차 검사 코드에 문제가 있다고 생각하고, 이 부분을 유심히 살펴보았다.

 

하지만, 특별한 문제를 발견하지 못했고, 그렇다면 Ray - AABB 사이의 교차 검사 코드에서 부동소수점 계산 중의 오차 누적으로 인한 버그라고 생각을 했다. Physically Based Rendering이란 책에도, Ray - AABB 사이의 교차 검사 중 부동소수점 오차를 수정하는 부분이 있기 때문에, 이 부분을 수정해보기도 했다. 

 

하지만, 이 부분도 문제는 아니었다. 또한 레이트레이서에서 사용하는 Epsilon의 값을 수정해보기도 했지만, 이는 갈라진 부분의 영역을 줄여주기는 했지만, 근본적인 해결책은 아니었다(이 과정에서 버그를 수정했다고 착각하기도 했다). 

 

계속 디버깅을 하다가 원인을 찾을 수가 없어서, 디버거를 통해서 버그가 생긴 영역의 픽셀을 조사하고, 패스트레이싱 과정을 조사하면서 버그를 찾아보려고 했지만, 이 방법도 실패했다.

 

기본적으로 패스트레이싱은 영역 광원을 만나기 전까지 랜덤하게 샘플링하기 때문에(물론 랜덤 샘플링이 아닌 영역 광원 샘플링, BRDF 샘플링 등 여러 방법을 적용할 수도 있다), 하나의 픽셀에서 문제가 발생하더라도, 단순히 샘플링 과정에서 물체가 없는 위치를 샘플링한 것인지 아니면, 버그로 인해서 충돌 검사가 제대로 일어나지 않았는지 판단하기가 매우 어렵다. 

 

이와 같은 문제로 인해, 3일동안 이 버그를 해결하지 못한 채로 다른 작업들을 하고 있었다. 그러다가 테스트용으로 작성한 Whitted 방식의 적분기를 사용하면, 패스트레이싱의 방식과 다르게 굴절, 반사 재질이 아닌 매트한 재질인 이상 랜덤한 위치로 광선을 다시 발사하는 과정이 없기 때문에 버그를 조금 더 직관적으로 볼 수 있을 것이라는 생각이 들었고, 렌더링 방식을 Whitted로 바꿨다. 결과는 매우 좋았다.

 

그림 3) Whitted 방식으로 렌더링한 이미지

패스트레이싱의 방식과 다르게 버그가 발생하는 영역이 매우 극단적으로 나타났다. 이런 식으로 영역이 극단적으로 나타나게 되면 VS 디버거를 통해서 버그가 어디서 발생하는지 쉽게 추적이 가능하다. 디버거를 통해서 코드를 따라가다 보니, 버그는 예상치 못한 곳에서 발생했다.

 

그림 4. 버그가 발생한 코드

 

위 코드를 보면 shape의 교차 검사를 하기도 전에 해당 기본체의 재질 정보와 영역광 정보를 업데이트를 하고 있다. 이로 인해 교차 검사에 실패했지만, 표면 정보는 업데이트되어서 잘못된 이미지가 렌더링되고 있었던 것이다. 

 

물론 실수도 실력이지만 3일동안 고생한 버그가 매우 사소한 실수였다. 또한 가속 구조가 없는 경우에는 모든 기본체와 교차 검사를 하고 순서에 따라 표면 정보를 스왑했기 때문에 이와 같은 버그가 생기지 않았지만, BVH 가속 구조에서는 표면 정보를 스왑하는 코드가 없었기 때문에 버그가 발생했던 것이다. 

 

그림 5) 수정 후

버그가 발생하는 코드를 위와 같이 수정해주고 렌더링을 해주면, 제대로 된 이미지가 나온다.

 

그림 6) 버그 수정 후 BVH 가속 구조를 이용한 패스트레이싱 렌더링 이미지

사실 레이트레이서를 개발하는 과정에서 Whitted 방식의 적분기는 초반에 빠른 구현을 위해서 만들어 놓고 추후에는 활용을 거의 하지 않는다. 하지만 패스트레이싱에 비해서 픽셀의 색을 구하는 과정이 단순하기 때문에 디버깅의 용도라도 만들어놓고 유지하는 편이 좋다.