HBAO+ 3.1 버젼을 기준으로 글이 작성되었습니다.

이전 hbao plus analysis 0 글에서 HBAO+ 을 알기위한 기본적인 개념들에 대해서 살펴보았다. 이번 글에서는 HBAO+ 의 구조와 Linearize DepthDeinterleaved Texturing 에 대해서 알아보겠다.

HBAO+ Pipeline


hbao+ with input normal

출처 : NVIDIA HBAO+


hbao+ without input normal

출처 : NVIDIA HBAO+


그림이 두개가 있다. 하나는 GBuffer 를 사용할 시 World-Space Normal 버퍼와 Depth Buffer 를 넘겨주어 계산하는 방식과 입력으로 Depth Buffer 만 넘겨서 Normal 데이터를 계산하는 두가지 방식에 대한 파이프라인이다. 두가지의 차이는 Normal 데이터에 대한 처리방식만 다르다. 나머지 계산은 다를게 없다.

Linearize Depths

코드를 보면 가장 처음에 시작하는 단계는 바로 Linearize Depths 다. 이는 꽤나 알려진 방법이다. 하지만 필자는 HBAO+ 를 볼때 처음 봤기에 어느 정도의 설명을 해놓아야겠다. Linearize Depths 를 알기 위해선 입력된 정점의 위치를 Clipping-Space 로 변환하는 방법이 어떻게 이루어지는지 알고 있어야 한다.

일반적인 오브젝트를 렌더링 할때는 Shader 에 입력으로 들어오는 정점의 기준 공간은 Model-Space(또는 Local-Space) Position 이다. 그래서 MVP 변환을 통해 Rasterizer 가 처리할 수 있도록 Clipping-SpaceRasterizer 로 넘어가기 전에 변환해주어야 한다.(전체적인 내용은 Model, View, Projection 변환 에서 확인할 수 있다. ) 그래서 Pixel Shader 로 넘어간 데이터들은 픽셀별로 들어가고, 픽셀별로 들어간 정점들의 위치는 Clipping-Space 로 되어있다. 여기까지 이해했으면 아래 그림을 보자.


frustum vs Clipping

출처 : 3D Graphics with OpenGL Basic Theory


위 그림은 View frustumClipping Volume 을 보여준다. View frustumPerspective 방식으로 카메라가 실제로 보여주는 공간을 시각화 한것이고, Clipping VolumeMVP 변환에서 Projection 행렬을 사용할시 View frustum 에서 Clipping Volume 으로 변환되는 볼륨을 시각화 한것이다. Projection 변환은 아래와 같다.


perspective projection matrix

출처 : Stackoverflow : Getting the true z value from the depth buffer


Perspective Projectionfrustum 기준 위치를 Cube 기준 위치로 바꾸는 연산이기 때문에 실제 좌표의 왜곡이 발생한다. 우리는 Z(Detph) 값이 어떤식으로 왜곡되는지 알아야 한다. 우선 Clipping-Space 로 변환할때, Perspective 형식의 View frustumzNear, zFar 사이의 Z 값을 [0~1] 값으로 매핑한다. 그러면 zNear, zFar 값을에 따라서 실제 좌표가 바뀐다. 그리고 값 자체가 실제 Z 값과 선형적으로 매핑되지 않는다. 아래 그림을 보자.


non linear depth

출처 : Computer Graphics StackExchange : How am I able to perform perspective projection without a near plane?


그림이 조금 헷갈릴수도 있다. 세로축의 d 값은 Projection 을 한 Z, Depth 값이고 가로축은 World-Space 의 Z 값이다. 조금 헷갈릴수도 있는 부분은 세로축의 기준값이 윗부분이 0이고 아랫부분이 1이다. 이 부분은 신경써서 봐야한다. 이해했다면 변경된 Depth 값은 실제 Z 값과 선형적인 관계가 아니고, 실제 Z 값으로 복원하려면 여러 연산을 해야하기에 HBAO+ 에서는 Depth 값들을 Linearize 하는 과정을 맨 처음에 넣은 것이다. 실제 Z 값으로 복원하는 이유는 간단하다. Linear 하지 않은 Depth 값을 연산시에 사용하면 보다 부정확한 결과가 나오기 때문이다. 특히 SSAO 연산을 할때는 Depth 값이 기본이 되기 때문에 해주어야 한다.

이 단계에서의 결론은 간단하다. Clipping-SpaceDepth 값을 View-Space 의 Z 값으로 변환하는 단계다. 처리하는 코드는 다른 단계에 비해 짧다. 만약에 넘겨준 Depth 데이터들이 View-Space 인 경우에는 옵션을 통해 처리할 수 있다.

Deintereaved Texturing

위의 그림에는 Generate HBAO+ 라고 단순히 뭉뚱그려서 표현했지만 그 안에는 단순한 Horizon based ambient occlusion(HBAO) 계산만 있지는 않다. Deintereaved Texturing 이라는 테크닉과 함께 HBAO 를 계산한다. Computer Engineering 분야의 지식을 응용한 이론으로 개인적으로 이 이론을 접했을 떄 꽤나 충격이였다. 자세한 설명은 GDC2013 : Particle Shadows & Cache-Efficient Post-Processing 슬라이드의 몇장과 함께 보자.


gdc2013_ParticleShadowsAndCacheEfficientPost_51

출처 : GDC2013 : Particle Shadows & Cache-Efficient Post-Processing


Deintereaved Texturing 의 방법은 간단하다. 텍스쳐를 여러장으로 나누어 샘플링을 한 후 각각의 나눠진 텍스쳐를 샘플링한 결과를 하나로 합친다. 슬라이드에는 Post-Processing 을 기준으로 설명이 되어있다. 이점은 생각하면서 보자.


gdc2013_ParticleShadowsAndCacheEfficientPost_52

출처 : GDC2013 : Particle Shadows & Cache-Efficient Post-Processing


한 텍스쳐를 여러장으로 나누는건 Multiple Render Target 을 사용해서 나눈다. 슬라이드는 4개를 기준으로 설명했지만 DirectX10 부터는 최대 8개까지 지원하기 때문에 16개로 나누어 샘플링한다.


gdc2013_ParticleShadowsAndCacheEfficientPost_53

출처 : GDC2013 : Particle Shadows & Cache-Efficient Post-Processing


다음은 나누어진 각각의 텍스쳐를 샘플링하여 원하는 알고리즘으로 결과를 낸다. 조각난 텍스쳐 한개당 한번 DrawCall 을 걸어준다.


gdc2013_ParticleShadowsAndCacheEfficientPost_54

출처 : GDC2013 : Particle Shadows & Cache-Efficient Post-Processing


Deintereave 를 하기전까지는 넓은 범위의 텍스쳐를 샘플링하여 캐시 효율이 많이 떨어졌지만 텍스쳐를 나누어 각각 할때마다 처리를 하게되니 캐시 효율의 이득을 얻었다. 또한 각각의 DrawCall 마다 텍스쳐의 용량이 조금만 필요하게 되니 대역폭의 이득도 얻게 된다.


gdc2013_ParticleShadowsAndCacheEfficientPost_55

출처 : GDC2013 : Particle Shadows & Cache-Efficient Post-Processing


한번의 DrawCall 로 나누어진 결과들을 합친다. Deintereaved Texturing 은 여기서 끝이다. 실제로 HBAO+ 는 16개의 텍스쳐로 나누어 샘플링한다. Multiple Render Target 이 8개까지 지원되어 16개로 Deintereave 하려면 2번 DrawCall 을 해야한다. 또한 샘플링은 16번 DrawCall 을 하여 계산한다. 그래서 한번 Deintereaved Texturing 을 사용하여 Post-Processing 처리하려면 약 20번의 DrawCall 을 계산해야 한다. 절대적으로 큰 숫자가 아니기 때문에 크게 신경쓸 필요는 없어보인다.


gdc2013_ParticleShadowsAndCacheEfficientPost_62

출처 : GDC2013 : Particle Shadows & Cache-Efficient Post-Processing


엄청난 성과를 거둔게 보인다. 캐시 히트 확률이 굉장히 올라갔고, 시간도 많이 절약했다. HBAO+ 의 성능향상을 시켜준 것이 이 Deinterleaved Texturing 인듯하다.

Reconstruction of Normal

HBAO+ 는 기본적으로 DepthNormal 을 통해서 계산한다. 그렇기 때문에 외부에서 Normal 데이터를 넣어주거나 직접 만들어야 한다. 보통 Deffered Rendering 을 차용하는 시스템들은 간단하게 GBufferNormal 데이터만 넣어주면 된다. Normal 데이터를 가져오는 코드가 있으니 조금만 수정하여 사용하면 된다.

Normal 데이터가 없는 경우에는 라이브러리 내에 직접 계산한다. 계산하는 픽셀을 기준으로 상하,좌우별로 Depth 와 화면상의 좌표계를 이용하여 View-Space 의 위치를 구한다음 위치가 상하, 좌우별로 가까운 픽셀의 위치 오프셋을 사용해 외적하여 Normal 값을 구한다.

참조 자료