[구현하기] Volume Ray Casting 개념 및 구현
Volume Ray Casting 을 주제로 “Real-Time Volume Graphics” 책과 위키피디아를 참고해 작성한 글입니다.
최근 volume ray casting 을 구현하였는데, 구현하면서 필요하고, 열심히 공부했던 개념들을 한번 정리하고 싶어서 글을 작성하게 되었습니다. 혹시나 내용 중 잘못된 점이 있다면 편하게 메일 주시거나 코멘트 해주세요:)
Volume Ray Casting 이란?
Volume Ray Casting은 이미지 기반의 볼륨 랜더링 기술입니다. 여기서 볼륨 렌더링은 3차원 샘플링 데이터를 2차원 투시로 보여주는 것입니다.
아래 그림이 ray casting을 잘 설명해주고 있습니다. Eye 는 카메라로 현재 volume을 바라보는 위치와 ray의 방향이 정해집니다. 그리고, Image plane 은 3차원의 볼륨 데이터가 2차원으로 투영되는 이미지 입니다. Image의 각 픽셀에서 하나의 광선(ray)이 volume(data set)으로 투사됩니다. 이 광선이 물체의 표면에 닿았을 때 정지시키지 않고 계속 뚫고 나아가도록 해서 광선을 통해 물체를 샘플링하도록 합니다.
Pipeline
이를 구현하기 위한 몇가지 단계들이 있습니다. 우선, 공간상에서 ray의 위치와 방향을 정의해야겠죠. 그 다음, 볼륨 데이터 내에 있는 ray를 따라서 일정 간격으로 복셀 사이사이 샘플링 지점들이 선택됩니다. 이때, 복셀 사이에서 샘플링이 되기 때문에 interpolation 을 필요로 합니다. 샘플은 표면 방향과 광원의 위치에 따라 shading (음영)처리 됩니다. 모든 샘플링 지점이 음영처리 된 후에는 광선을 따라 compositing (합성)되어 현재 처리중인 픽셀의 최종 RGBA 값이 생성됩니다.
각 단계를 구체적으로 알아보도록 하겠습니다.
1. Ray Casting
우선, ray를 쏘는 카메라의 위치와 방향을 먼저 정의를 해야합니다. 우리는 360도 어느 방향에서든 이 볼륨을 바라보고 싶기 때문에, 구면좌표계를 한번 생각해볼 수 있습니다.
이러한 좌표계에서 O위치에 볼륨이 위치하고 P를 카메라 위치로 잡는다면, 카메라의 위치를 쉽게 정의할 수 있습니다. 참고
또한, 카메라에서 쏘는 ray의 방향을 정의하기 위해서는 벡터 개념이 필요합니다. 기본적인 벡터 개념들(정의나 벡터연산)은 이곳을 참고하면 될 것 같습니다.
이렇게 카메라의 위치와 방향을 정의를 하면, image plane에서도 각 픽셀의 위치, 즉 각 광선이 나가는 위치 역시도 계산을 할 수 있고, 이를 이용해 다음 단계인 샘플링을 하게 됩니다.
2. Sampling
위 그림처럼 각 픽셀에서 광선을 내보내는데, 이때 광선은 볼륨을 통과하면서 일정한 간격으로 복셀 데이터를 샘플링 합니다. 샘플링 할때 생각해야할 점이 두가지 있는데 Nyquist Sampling Theorem과 Interpolation 입니다.
Nyquist Sampling Theorem
sampling 주파수가 샘플링하려는 데이터 주파수의 두 배 이상이되어야 제대로 재구성 할수 있다는 이론입니다. 이 이론에 따라 볼륨 데이터의 크기와 간격을 계산하여 샘플링 해야 합니다. 이미지 샘플링에 관한 자세한 내용은 러너게인님 블로그를, 샘플링 이론에 관한 자세한 내용은 alanjshan님 블로그를 참고하면 좋을 것 같습니다.
Interpolation
위의 그림을 보시면 샘플링하는 위치와 복셀위치가 완벽하게 일치하지 않는 것을 볼 수 있습니다. 이때 필요한 것은 주변 복셀값으로부터 interpolation을 해주어야 합니다. 이 경우는 3차원이기 때문에, trilinear interpolation을 해주어야 합니다. 자세한 내용은 위키피디아를 참고하시기 바랍니다.
3. Compositing
위에 단계에서 샘플링을 잘 해주어 복셀의 값들을 얻었다면 이를 잘 합성하여 광선을 내보낸 픽셀의 RGBA 값을 잘 정의해주어야 합니다. 이때 필요한 것이 transfer function, shading, front-to-back compositing 입니다.
Transfer function
샘플링 된 각 값의 범위와 우리가 원하는 RGBA 값의 범위는 다릅니다. RGBA 값의 범위는 0~255 이지만, 일반적인 의료데이터의 경우 범위가 훨씬 넓습니다. 따라서 원하는 범위의 값을 RGBA 값으로 매핑해주어야 합니다.
“user have to decide how data should look like”
이때 classification, segment 등 여러가지를 고려할 수 있지만, 간단하게 linear function으로 매핑해주는 것을 생각할 수 있습니다.
이를 통해, 스칼라 값 s(샘플링을 통해 얻은 값)는 transfer function을 통해 RGBA 범위의 값으로 매핑됩니다.
Shading and illumination
각 매핑된 샘플링 값은 RGB(Emission)+A(Absorption) 값들로 Emission-Absorption Model 입니다. 여기에 scattering term을 고려해줌으로써 illumination effect 를 얻을 수 있습니다. illumination effect는 물체의 성질에 따라 light reflection을 모델링 해주는 것인데, 이때 scattering term을 정확히 구하는 것은 어렵기 때문에 대략적인 값을 구합니다.
이러한 local illumination은 phong reflection model (또는 phong shading)을 통해 구현될 수 있습니다. phong shadiing은 볼륨에 아래 그림과 같은 효과를 가져옵니다.
phong reflection model은 Ambient + Diffuse + Specular 로 정의될 수 있습니다. 이 부분은 홍정모님 블로그를 참고하였습니다.
\[I_{ambient} + I_{diffuse} + I_{specular} = k_aI_a + k_dI_dmax((N \cdot L), 0)+k_sI_s(R \cdot V)^n\]여기서 N은 노말벡터, L은 입사각으로 빛의 방향벡터, V는 카메라 방향벡터, R은 반사각으로 V+L (대락젹인 값)로 나타낼 수 있습니다. 여기서 L, V, R은 벡터연산으로 쉽게 구할 수 있지만, 노말벡터는 구하기 쉽지 않습니다. 일반적인 surface에서 illumination을 계산하는 경우에는 표면의 노말벡터이지만, 이 경우는 광선이 볼륨 안을 통과하기 때문에 각각의 샘플링된 복셀의 gradient를 구해주어야 합니다. 각 값(particle)들의 central finite difference를 구하여 이것으로 노말벡터를 계산합니다.
이 외에 k는 각 ambient, diffuse, specular 계수를 의미하고, I값은 빛의 밝기로 이전 단계에서 구한 값입니다. 이렇게 해서 샘플링된 값의 RGB 값을 정해주게 되었습니다.
Front-to-back compositing
광선을 통해 샘플링 된 각 값들의 RGB값을 구했다면 이를 합성해주는 과정도 필요합니다. 이것이 compositing 입니다. 일반적으로 front-to-back compositing 방법을 이용합니다.
카메라에서 가까운 방향부터 샘플링된 값을 합성해나가는 방법입니다. 여기서 C값이 앞에서 구해준 RGB 값이고, T값이 transparency 즉 (1-T) 값이 A가 됩니다. 그리고 F는 RGB값으로 \(F_0 = \alpha_0C_0\) 로 초기화 됩니다.
compositing을 하는 식은 다음과 같습니다.
\[F_{n+1} = F_n + \alpha_{n+1}C_{n+1}T_n \\ T_{n+1} = (1-\alpha_{n+1})T_n\]이 식을 통해 compositing이 되며, 최종적인 RGB값과 A값이 정해집니다. CT영상을 volume rendering 한 결과가 어떤지 보여드리기 위해 위키피디아에서 결과사진을 가져왔습니다. 위의 과정을 모두 거치면 모든 방향에서 입체적으로 볼 수 있게 잘 랜더링 된 것을 볼 수 있습니다.
오늘은 이전에 계속 포스팅 해왔던 딥러닝에서는 조금 벗어난 그래픽스 분야의 글을 작성하게 되었습니다. 처음 3차원 데이터를 다루다 보니 힘든점이 많았지만 (벡터...) 새롭게 배운점도 많고, 구현결과가 굉장히 신기했습니다.
Leave a comment