| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- rendering
- scattering
- Shadow
- UE5
- Study
- VGPR
- SGPR
- unrealengine
- scalar
- GPU
- RayTracing
- deferred
- vulkan
- optimization
- ShadowMap
- Nanite
- GPU Driven Rendering
- SIMD
- Wavefront
- forward
- DX12
- atmospheric
- hzb
- wave
- 번역
- DirectX12
- Graphics
- texture
- ue4
- shader
- Today
- Total
RenderLog
DeepShadowMap(DSM) 본문
DeepShadowMap(DSM)
최초작성 : 2020-02-24
마지막수정 : 2020-02-24
최재호
소개
Volumetric shadow 기법
머리카락과 같은 물체에 의해서 차폐되어 그림자가 생기는 경우를 고민해보자. 이 경우 그림자는 즉시 완전히 차폐 되는 것이 아니라 일부 빛은 머리카락을 통과하여 그림자가 서서히 진해지게 된다. DSM을 통해서 이 부분을 표현할 수 있다.

- 링크드리스트로 Depth 정보를 연결하여 해당 픽셀의 Depth에서의 투과율 정보를 Alpha로 저장함. 아래 이미지처럼, 가려지는 횟수가 많아질 수록 Lit 값이 점점 떨어지게 됨. 일반 ShadowMap의 경우는 가려지는 즉시 해당 ShadowMap의 Depth 값 보다 카메라에서 더 멀리 있는 물체는 모두 그림자를 만들지만 DSM의 경우는 Alpha 가중치를 주어서 투과율을 떨어뜨리는게 차이점.

- 아래 이미지는 ShadowMap 특정 한 픽셀에 대해서 깊이 값에 따라 다양한 Alpha 값을 갖고 있는 모습을 보여줌

- Deferred Rendering 과 함께 사용하는 경우 성능이 좋다. 왜냐하면 DSM은 그림자 여부를 판단하는 연산을 포스트프로세스 과정에서 처리하게 되면, 이때는 이미 G-Buffer를 생성한 후라서 해당 픽셀을 중복해 그려지지 않기 때문에 더 빠르게 그림자 여부를 판단할 수 있다.
- DSM의 경우는 위의 이미지 처럼(Figure 1.4), Depth 값을 얻어온 후에 링크드리스트로 알맞은 Depth를 추적해야 하므로 연산이 비싸다. 만약 Deferred Rendering이 아니라면, 보이지 않는 픽셀의 그림자 여부까지 이런 방식으로 판별하게 되므로 연산이 많이 느려지게 된다.
2. 링크드리스트의 깊이는 이 구현에서는 50개로 제한됨. 이 깊이가 깊어질수록 성능이 저하됨.
DSM 구현 프로세스
ShadowMap 생성
- 먼저 ShadowMap의 픽셀크기와 동일한 버퍼를 준비한다. 이 버퍼는 픽셀당 Int32 데이터형을 저장함.
- 아래 코드의 StartElementBufEntry
- 깊이 값과 해당 깊이에서의 투과율(Alpha) 수치를 저장할 링크드리스트 노드를 준비한다.
- 아래 코드의 NodeData
- 링크드리스트 노드를 생성할 버퍼를 SSBO(Shader Storage Buffer Object) (DirectX에서는 UAV)를 준비한다.
- 아래코드의 LinkedListEntryDepthAlphaNext

- 링크드리스트 노드를 생성하기 위해서 Atomic Counter를 사용한다. (Fragment Shader는 각각의 Fragment마다 비동기로 작동하므로 새로운 노드를 생성할 때 이 Atomic Conter를 기준으로 해야함)

- 나머지 코드는 아래와 같다. ShadowMap의 현재 UV 위치에 새로운 링크드리스트 노드를 추가해준다. 이때 이미 만들어진 노드가 있다면 새로 만든 노드로 교체해주고 이전에 있었던 노드를 Next로 연결한다.

- 현재 과정에서는 한 개의 노드가 고정된 양의 투과율(Alpha) 값을 가지도록 하고, 이 후 과정에서 Depth가 누적된 경우의 투과율 값을 계산하도록 한다.
Sort / Link Neighbor (Compute Shader 사용)
- 이전에 만든 Shadow 정보는 정렬되어 있지 않을 것이다. 어떤 Geometry를 먼저 그렸나? 여부에 따라서 뒤죽박죽 섞여 있을 것이다. 그래서 깊이 값 기준으로 정렬해줄 필요가 있다. 또한 정렬을 마치고 나서 누적된 투과율 값을 계산하는 것 또한 처리한다.
- 먼저 Depth 값을 기준으로 Linked list를 정렬한다. Array로 옮긴 후 Selection Sort 사용

- 정렬된 링크드리스트 노드에 투과율 값을 누적시킨다. 링크드리스트의 Next로 넘어 갈수록 더 어두워져야 하므로 투과율 (Alpha) 값을 계속해서 감소시킨다.

- 그 다음으로 DSM Shadow의 AntiAliasing을 처리하기 위해서 이웃 노드들의 정보를 생성한다. (PCF 방식을 사용함.)
- 아래와 같은 형태로 노드의 이웃을 연결해 줄 예정이다.

- 먼저 이웃 노드를 저장하기 위한 자료구조는 아래와 같다. NeighborData에는 현재 노드의 위/오른쪽 노드의 인덱스를 저장한다. 그리고 모든 이웃 데이터는 NeighborListData 에 저장되며, ShadowMap 생성 과정에서 만든 링크드리스트 노드의 인덱스를 사용하여 접근가능하다.

- 아래와 같이 현재위치와 위/오른쪽의 노드 인덱스를 구한다.

- 그리고 아래와 같이 현재 노드의 Depth와 가장 근접한 위/오른쪽 노드를 찾는다. 가까운 노드라고는 했지만 그냥 현재의 Depth 보다 더 카메라에 가까운 Depth를 가진 노드를 찾는 것이다.

Draw Shadow (In Postprocess)
- 이제 그림자 여부를 판단하는 부분이다. 아래의 deepSearch 함수가 핵심이다. outShading가 0.0 에 가까울 수록 그림자가 완전히 드리운 것이다. 이 함수는 현재 주어진 NodeData와 Depth 값을 기준으로 이 값에 가장 가까운 링크드리스트 노드를 얻어낸다.

- 이 값을 기준으로 Filter 크기만큼 주변의 픽셀에 대해서 outShading 값을 얻은다음 outShading의 평균값을 얻어내면 PCF 처리가 완료된다. 이 부분은 코드가 너무 길이에 비해서 DSM을 이해하는데 핵심적인 사항은 아라고 생각되어서, 필요하다면 이 문서 하단에 첨부되어 있는 Github 링크에 fs_deepshadow.glsl 을 참고하면 된다.
구현결과
아래 이미지는 DeepShadowMap을 OpenGL에서 직접 구현해본 예제이다. GPU Pro4 에서 참고한 예제소스를 기반으로 OpenGL에서 구동되도록 하였다. 예제와는 다르게 Bloom, Tonemap 그리고 Skylight 등 추가적인 사항이 반영되어 조금 달라 보일 수 있으며, 예제소스에서는 헤어라이팅(Kajiya-Kay)의 알고리즘에서 라이팅 처리가 잘못된 부분이 있어서 해당 부분을 반영한 결과이다. (이 부분은 정확하지 않을 수 있습니다. 추후 따로 Kajiya-Kay 부분을 소개하며 다루겠습니다)
좌측에서부터 링크드리스트 노드의 초기 Alpha 값이 0.1 -> 0.3 -> 1.0 인 경우다. Alpha 값이 더 높을 수록 차폐시키는 물체를 만날 때마다 투과율이 더 크게 낮아짐.



구현코드
https://github.com/scahp/Shadows
참고자료
GPU Pro 4: Advanced Rendering Techniques, edited by Wolfgang Engel, Real-Time DeepShadowMaps
'Graphics > Graphics' 카테고리의 다른 글
| Light Indexed Deferred Rendering (0) | 2020.04.03 |
|---|---|
| Tangent Space, Tanget Vector 생성 (0) | 2020.03.28 |
| Dual Paraboloid ShadowMap (0) | 2020.02.18 |
| DeferredRenderingMSAA (0) | 2020.02.12 |
| Hi-Z Occlusion Culling (3) | 2020.02.04 |