본문 바로가기

개발/개발 일기

2021.07.29 개발 일기 - 잘못된 패스트레이싱과 러시안 룰렛의 적용

이 부분은 잘못된 정보를 담고 있습니다. 

더보기

최근에 코넬박스를 구현하고 나서, 직접광에 관련된 기능을 구현하려고 보니, pbrt에서는 영역광이 발산하는 방사량에 Inverse-Squared 법칙을 적용하지 않는다는 사실을 알았다. 이 부분이 잘 이해가 되지 않아서, 어디를 놓쳤는지 책의 이곳저곳을 확인하고 있었다. 

 

그러다가 매우 충격적인 사실을 알게 되었다. 내가 구현한 패스트레이싱 코드(pbrt를 참고하지 않은 코드)는 완전 틀린 코드였다는 사실이었다...

 

패스트레이싱 개념에 대해서 어느정도 이해하고 있었고, 렌더링 방정식에 대해서도 어느정도 이해하고 있었기 때문에 혼자서 자신있게  코드를 작성했다. 또한 결과 이미지로 봤을 때에는 동작도 제대로 하는 것처럼 보였기에, 제대로 구현했다고 생각을 했다.

 

우선 이전에 구현한 방식에 대해서 간단하게 설명하면, 카메라에서 출발한 광선이 물체와 교차하게 되면, 해당 교차점에서의 bsdf를 계산하고 새로운 광선의 방향을 샘플링하고 다시 교차한다면, 이전의 계산한 bsdf의 값에다가 현재 교차점에서의 bsdf 값을 곱 누적을 반복한다. 그러다가 영역광에 만나게 된다면, 현재 누적된 값에 영역광이 방출하는 방사량을 곱하고 최종적인 픽셀의 값을 결정하는 것이었다.

 

그리고 새롭게 작성한 코드는, 광선과 교차한 교차점으로 입사되는 직접광의 입사량을 계산하고, 다시 새로운 광선의 방향을 샘플링하는 과정을 반복하는 것이다. 이전에 작성한 코드는 설정한 최대 깊이 동안에 영역광에 도달하지 못하게 된다면, 해당 픽셀의 값이 (0, 0, 0)이 되어버리지만, 새로 작성한 코드는 교차점에서 직접 직접광의 입사량을 계산하기 때문에, 이런 문제가 발생하지 않았다.

 

이전의 방식은 애초에 잘못된 공식을 적용한 것이지만, 그 부분을 차치하고 새롭게 작성한 코드는 교차점에서 직접광의 입사량을 계산하고, 광선이 샘플링되는 경로 중에 영역광이 존재하지 않더라도 픽셀값의 색상을 제대로 설정할 수 있었다. 이 부분으로 인해서 이미지의 분산이 눈에 띄게 줄었다. 

 

 

사진 1) (1) 이전 방식 (픽셀당 50샘플) (2) 이전 방식 (픽셀당 256 샘플) (3) 새로운 방식 (픽셀당 50 샘플)

 

새로운 방식은 이전 방식을 적용하고 픽셀당 256개의 샘플링을 진행한 것보다 훨씬 분산이 낮았다. 하지만 그 대가로, 같은 샘플링을 렌더링할 시에 렌더링 타임이 증가했다. 물론 렌더링 타임만 짧은 뿐, 분산으로 인해 최종 이미지의 차이는 컸다. 이전 방식으로 새로운 방식의 이미지와 비슷하게 하려면 렌더링 타임이 훨씬 많이 걸릴 것이다.

 

물론 이 부분은 애초에 잘못된 코드를 작성했던 것이기 때문에, 렌더링 타임이 증가했다기보다는 이전의 방식이 생략된 부분이 많았던 것이다.

 

사실 새로운 방식을 적용하고 나서 이전 방식으로 다시 렌더링하기 전에는 이미지의 분산을 떠나서, 최종 이미지의 색이나 밝기 등이 달라질 줄 알았다. 하지만 이 부분의 변화가 크지 않다는 것에 조금 놀랐다. 아마 광원이 하나이기 때문에 그런 것 같다. 여러 개의 광원을 사용하게 된다면 최종 이미지에 변화가 생길 것 같다.

 

이전의 방식은 Specular Reflection과 Specular Transmission을 지원했지만, 아직 새로운 방식은 직접광의 입사량을 계산하는 부분의 코드가 완성되지 않았기 때문에, Lambertian Reflection brdf를 가진 머티리얼만 지원하고 있다. 

 

 

아무튼, 패스트레이싱 알고리즘을 수정하고, 그 후에는 러시안 룰렛 알고리즘을 적용했다. 이 알고리즘은 이미지의 분산에는 큰 영향을 주지는 않지만, 경로를 추적할 때 기여양이 특정 이하로 낮아지면, 더 이상의 경로 추적을 하지 않는 알고리즘이다. 적용 결과는 아래와 같다.

 

 

사진 2)  (1) 러시안 룰렛 적용 X 패스트레이싱 (픽셀당 50 샘플) (2) 러시안 룰렛 적용 O 패스트레이싱 (픽셀당 50 샘플)  

 

 

렌더링 타임이 약 23%정도 향상되었고, 최종 이미지의 분산은 큰 차이가 없다. 최종 이미지는 아래와 같다.

 

사진 3) (1) 러시안 룰렛 적용 X 최종 이미지 (2) 러시안 룰렛 적용 O 최종 이미지 

 

이렇게 해서 이전까지 사용하던 이상한 패스트레이싱 알고리즘을 버리고, 렌더링 방정식에 기반을 한 패스트레이싱 알고리즘을 적용했다. 이 작업을 통해서 렌더링 방정식에 대한 이해도가 더욱 높아지었기 때문에, 이전에 작성했던 렌더링 방정식에 대한 글을 새로 작성해야겠다. 다시 읽어보니 영.... 

 

이젠 다시 Mirror와 Glass 머티리얼이 제대로 동작하게, 코드를 손 좀 봐야겠다. 이 작업이 끝나면, PBR기반의 머티리얼을 구현할까 고민 중이다. 물론 텍스처랑 삼각형 메쉬도 제대로 지원해야하는데, 무엇부터 해야할 지~~