| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
- SIMD
- UE5
- VGPR
- DirectX12
- Study
- ShadowMap
- Wavefront
- ue4
- GPU
- optimization
- DX12
- SGPR
- Graphics
- GPU Driven Rendering
- atmospheric
- wave
- texture
- deferred
- RayTracing
- scalar
- Shadow
- 번역
- vulkan
- hzb
- shader
- unrealengine
- scattering
- forward
- Nanite
- rendering
- Today
- Total
RenderLog
Hi-Z Occlusion Culling 본문
Hierarchical-Z Map Occlusion Culling
작성일자 : 2020-02-04
최재호
목표
Hi-Z Map Occlusion Culling을 이해와 실제로 구현 및 결과를 확인하는 것을 목표로 함
기존 방식 비교
H/W Occlusion Culling
실제로 렌더링 한 결과로 그려진 픽셀의 개수를 체크하여 몇 개의 픽셀이 그려졌는지 확인
보통은 바운드 박스같은 간단한 프리미티브로 그려진 픽셀 수를 확인
아래와 같이 CPU <-> GPU 간에 핑퐁 발생
1. CPU : 바운드박스 렌더링 요청
2. GPU : 바운드박스 렌더링 후 그려진 픽셀수 계산
3. CPU : 그려진 픽셀이 있는지 확인 후 원본 프리미티브 렌더링 요청
단점으로는 미리 그려본 프리미티브가 Rasterizer -> PixelShader -> DepthTest 실행하고 해당 픽셀정보는 버려진다. 즉, 그려지지 않을 객체에 추가적인 연산으로 인해 불필요한 부하를 가중시킴. 간단한 프리미티브는 괜찮겠지만 렌더링할 양이 늘어날수록 CPU가 그려진 픽셀 개수를 얻기 위해서 Stall 하는 시간이 길어진다.
Hierarchical-Z Occlusion Culling 소개
기본적인 방식은 하드웨어 오클루젼 컬링과 동일하다. CPU <-> GPU 간에 핑퐁 역시 발생한다.
다른 점은 Rasterizer 처리 전에 오클루젼 여부를 모두 결정한다. 이 방법으로 GPU의 부하를 줄여서 CPU가 Stall 되는 시간을 최소화 가능하다. 또한 더 적은 크기의 Texture를 사용하여 GPU 캐시효율을 증가 시킬 수 있다.
Hi-Z Occlusion Culling 은 2가지 단계로 진행된다. Hi-Z map 생성과 Hi-Z 오클루젼 처리다.
1. Hi-Z map 생성
Depth Buffer Texture로부터 Mipmap을 생성하여 Hi-Z map을 만들 수 있다. 이전 Mip-level의 4개 텍셀 중 가장 화면에서 멀리 떨어진 텍셀을 다음 Mip-level의 1개의 텍셀로 매핑된다. 이 과정을 1x1 Mip-level이 생성완료 될떄까지 반복한다. 보통 화면이 와이드이기 때문에 NPOT(Not Power Of Two) 텍스쳐를 사용한다. 그렇기 때문에 아래와 같은 공식으로 텍스쳐 사이즈를 줄여간다. (Mipmap 텍스쳐는 floor 규칙을 사용하여 mip-level 개수 확인)
int numLevels = 1 + (int)floorf(log2f(fmaxf(SCREEN_WIDTH, SCREEN_HEIGHT)));
생성된 결과는 아래와 같다.

Hi-Z map 생성 코드

2. Hi-z 오클루젼 처리
Vertex 혹은 Compute Shader에서 바운드박스의 Vertex들을 Screen space 로 Transform 시킨다. Screen Space에 렌더링 될 바운드사각형 영역을 구한 후 이 크기를 이용해서 어떤 Mip-level을 사용할지 결정한다. 결정된 Mip-level로 Hi-Z에서 바운드사각형의 4군대 꼭지점 위치부분의 Depth를 Fetch 하고, 가장 화면에서 멀리 떨어진 Depth를 선택한다. (이것을 A로 두자) 그리고 바운드사각형의 Z 값 중 화면과 가장 가까운 Depth를 선택한다.(이것을 B로 두자). 이때 A보다 B가 화면에서 더 멀리 떨어져 있다면 Occlusion 된 것이다.
아래 이미지는 참고자료 2번 [Hierachical Z Map Occlusion Culling / 유영천]에 있는 Mip-level 선택 방식이다.

여기서 Hi-Z에서 왜 4개의 텍셀을 Fetch하여 가장 화면과 멀리 있는 깊이 값을 구해내는지? 왜 한단계 더 높은 Mip-level에서 한 개의 Texel만 Fetch 하지 않는지에 대해 궁금할 수 있다. 아래 그림을 보자.
아래 그림에서 좌측 이미지는 Mip-level이 하나 더 높은 텍스쳐다. 이 경우 좌상단의 1 텍셀에서 Depth를 얻어올 것이다. 하지만 우측 이미지처럼 현재 Mip-level(앞의 예보다 한단계 낮은 level) 4군데의 텍셀에서 깊이를 얻어온다면 더 정확한 결과를 얻을 수 있다. 이런 이유로 텍셀을 4군데서 Fetch 한다. 또한 4개 이상 Fetch하게 되면 성능상 좋지 않으므로 4개로 제한한다.

Occlusion 여부를 판단한 후에는, Occlusion 된 경우 결과를 CPU에 돌려주기 위해서 Occlusion 여부에 따라 Geometry Shader에서 Primitive를 Emit 할지 말지를 결정한다. (Occlusion 되지 않은 경우만 Emit)
Primitive가 Emit 되었는지 여부는 OpenGL의 GL_PRIMITIVE_GENERATED 기능을 사용하여 확인 가능하다.
실제 Occlusion 처리 코드

Hi-Z Map Occlusion으로 기대할 수 있는 것
1. Rasterizer 단계전에 Occlusion을 처리할 수 있어서, CPU <-> GPU 간의 핑퐁 시간을 줄일 수 있다.
2. 바운드박스가 차지하는 화면공간 크기에 따라 적절한 Mip-level 텍스쳐의 사용으로 낮은 해상도의 Depth Map을 사용할 수 있으므로 GPU의 Cache를 더 효율적으로 사용 가능하다.
실제 구현 예
2개의 Cube을 렌더링하는데 하나는 지상에 하나는 지하에 렌더링
(지상에 있는 Cube는 비교를 위해 출력했으므로 신경 쓸 필요 없음)


지하에 있는 Cube의 위치를 옮겨서 Hi-Z Occlusion Culling 실행

아래 그림 처럼 PrimitiveGenerated 가 1개 있다는 것을 확인 할 수 있음.

구현 코드는 아래에서 확인 가능
https://github.com/scahp/Shadows/tree/HiZOcclusion
참고자료
1. [번역]Hierarchical-Z map based occlusion culling https://scahp.tistory.com/2
2. Hierachical Z Map Occlusion Culling / 유영천 https://www.slideshare.net/dgtman/hierachical-z-map-occlusion-culling?from_m_app=ios
'Graphics > Graphics' 카테고리의 다른 글
| Light Indexed Deferred Rendering (0) | 2020.04.03 |
|---|---|
| Tangent Space, Tanget Vector 생성 (0) | 2020.03.28 |
| DeepShadowMap(DSM) (0) | 2020.02.25 |
| Dual Paraboloid ShadowMap (0) | 2020.02.18 |
| DeferredRenderingMSAA (0) | 2020.02.12 |