Path-tracing 도중 Geometric Buffer 추출하기

Theme
Mitsuba
Type
테마글
Status
Post
분야
중요도
2 more properties
이전 글인 Simple Path Tracer 짜기 에서는 각 ray sample의 interaction이 벌어지는 main loop만을 짜면 됐기 때문에 Integrator() class의 sample()이라는 함수만 수정하면 되었다. 해당 함수는 각 sample의 radiance와 각 sample의 weight를 return하여, 추후 Integrator() class의 render() 함수에서 sample들의 radiance를 각 pixel마다, 그리고 주어진 filter (e.g., box filter)를 거쳐 최종 이미지를 만들게 된다. 참고로 render() 함수는 SamplingIntegrator() 클래스에서 이미 정의가 되어 있다.
그러나 각 sample의 geometric buffer (e.g., texture, normal, depth)를 뽑아내기 위해서는 여러 함수들을 수정해야 한다. 그 이유들은 아래와 같다.
Sample의 radiance 이외의 정보를 효과적으로 저장하고 이를 이미지 (Mitsuba의 Film()과 ImageBlock() 클래스) 에 병합(aggregate)하기 위해서는 기존 Mitsuba3와 Dr.Jit의 기능들을 활용해야 한다. 기존 Mistuba는 부가정보를 저장하기 위한 AOV array를 지원한다. 이를 활용하기 위해서는 initialization단계에서 정확한 크기를 할당해주어야 한다. 그러므로 Initialization에 관여하는 함수들인 Integrator() 클래스의 prepare()와 sample_ray() 함수를 수정하여야 한다.
Sample의 geometric buffer를 추출하였다면, 이를 최종 이미지 텐서 뒤에 병합하여야 한다. 그러므로 이를 담당하는 Integrator() 클래스의 render()함수를 수정해야 한다.
우선 Initialization 단계에서는 prepare()와 sample_ray() 함수를 수정할 필요는 없다. 다만 우리가 한 sample당 몇 채널의 부가정보를 추출할 것인지를 Integrator() 클래스의 aov() 함수가 인지하도록 해야 한다. 그래서 Integrator 선언 때 input으로 아래와 같은 부가정보 list를 주도록 하였다. 각 string은 “{feature_name}:{# channel}{type}”으로 구성되어 있다. 예를 들어, 3차원 normal을 뽑고 싶으면 “normal:3f”, 1차원 depth를 뽑고 싶으면 “depth:1f”를 쓰면 된다. Type까지 지정한 이유는 기본적인 float뿐만 아니라 가끔씩 boolean형식이 필요할 때가 있기 때문이다. aov()함수는 해당 인풋을 parsing하여 총 몇개의 채널이 필요한지를 선언하고, Mitsuba의 film()함수는 이에 맞추어 알맞는 크기의 ImageBlock()을 생성한다.
Geometric feature들은 sample() 함수의 loop안에서 뽑으면 된다. Normal과 depth는 ray의 intersection 변수에 저장되어 있고 (각각 si.n, si.t 로 추출 가능), 텍스처는 intersection의 bsdf에 저장되어 있다. 참고로 텍스처는 mitsuba에서 diffuse_reflectance로서 정의되고 사용된다. 다만, 우리는 모든 loop의 feature를 필요로 하지 않고 첫번째 intersection과 첫번째 non-specular intersection에서의 feature만을 필요로 한다. 첫번째 intersection은 ray의 depth가 0일 때를 기준으로 쉽게 처리할 수 있다. 두번째 경우는 specular hit을 기록하는 boolean 변수와 이 non-specular hit이 처음인지를 확인하는 변수를 통해 처리하였다. Specular hit은 intersection의 bsdf이 delta함수를 가지고 있는지를 판단하는 Flag를 통해서 확인할 수 있었다. 이를 통해 first intersection과 first non-specular intersection의 텍스처, 노말, 깊이를 얻을 수 있다.
그리고 여러 denoiser를 위해 diffuse radiance또한 기록해야 했는데, 이는 기존 각 intersection의 throughput을 누적하던 변수와 비슷한 변수를 정의하고 각 intersection의 diffuse reflectance만을 누적하도록 하였다. 다만, 첫 반사들이 specular일 경우를 대비하여 첫 non-specular hit이 나올때까지는 기존 throughput을 누적하도록 하였다.
마지막으로 각 sample마다 얻은 정보는 ImageBlock()에 넣어 병합할 수 있다. 그러나 병합 과정에서는 antialiasing을 위한 filter를 거치기 때문에 완전히 unbiased한 결과를 얻을 수 있다. 이를 막기 위해 radiance나 aov를 바로 return한다면 각 sample의 정보를 모두 얻어 활용하였다. (이때 최종 텐서는 H x W x spp x C 의 크기를 가진다). Sample-wise 정보를 활용하고 싶다면 해당 텐서를 직접 사용할 수 있고, pixel-wise 정보를 활용하고 싶다면 spp-dimension의 평균값을 얻어 사용하면 된다.
이를 통해 현재 KPCN, AdvMCD, AFGSA와 같이 pixel-wise geometric feature를 사용하는 디노이저들과 NDLE와 같은 sample-wise geometric feature를 사용하는 디노이저를 위한 데이터를 추출할 수 있다. 추후에는 SBMC, WCMC가 사용하는 path-feature를 추출하는 방법을 알아보고자 한다.