Programming/D3D12

[책공부] Ambient Occlusion + chap 21 연습 문제

Dorasima 2024. 3. 8. 13:32

(사실 이해했는지 모르겠다. 어렴풋이 느껴질 뿐... 그래도 그 느낌을 정리한다.)

1. Ambient Occlusion

- 냅다 모든 모델 픽셀에 일정량의 빛을 더해주었던 Ambient Light를 좀 더 그럴싸하게 바꾸는 챕터이다.

- 아이디어는 일단 타겟픽셀 주변에 뭔가 많으면, 그림자가 드리워져서 혹은 빛이 가려져서 Ambient Light를 어둡게 설정하는 것이다.

- 타겟 픽셀의 노멀 방향 반구로 냅다 Ray를 쏴서, 뭔가 닿는것이 많을 수록 어두워지는 것이다.

- 하지만 모든 모델, 모든 픽셀에 대해서 Ray를 쏘는건 일단 말이 안되고, 좀 더 가벼운 방법이 필요하다.

 

- 그래서 나온게 SSAO (Screen Space Ambient Occlusion) 이다. 화면에 보이는 부분에 대해서만, 그리고 렌더링을 할 때 얻을 수 있는 정보만을 가지고 Occlusion을 계산하는 것이다. 

 

2. SSAO 아이디어

- 일단 현재 렌더링하는 view의 Depth와 Normal 정보를 텍스쳐에 (nx, ny, nz, d)으로 기록한다.

- 이제 이 텍스쳐를 SSAO 결과(ambient를 어느정도로 어둡게 할 건지)를 렌더링하는 쉐이더에서 사용한다.

나름 잘 그린것 같은데?

 

- 일단 타겟 픽셀의 월드 위치를 InvView와 InvProj를 이용해서 구하고, 그 위치에서 노멀 방향 반구로 무작위 직선을 여러개 쏘는 것이다. (여기서 노멀맵을 이용해서 해당 픽셀의 view(screen)의 월드를 구한다.)

- 계산을 간단하게 하기 위해 여기에 부딪히는 픽셀은 기록된 Depth Map에 해당하는 픽셀로만 한다. 

 

- 그림의 두꺼운 검정색이 Depth Map이고, 초록색이 Normal Map이다.

- n 방향 반구로 노란색의 무작위 Ray를 쏘고 그것이 Depth Map에 기록된 픽셀과 거리에 따라 Occlusion 정도를 구하는 것이다.

- 타겟 픽셀과 무작위 Ray에 맞은 Depth Map에 해당하는 픽셀 위치를 View 공간으로 변환한 뒤에 Occlusion을 계산한다.

- 이를 위해서 Ray - Direction의 성질과, NDC, Proj, View 변환을 거꾸로 해서 해당 좌표를 구한다.

 

3. 주의사항 및 추가 기법

- 꽤 많은 Ray를 생성해서 Occlusion을 판단하지만, 그래도 해상도 보다는 여전히 부족하다. 그래서 적절히 ssao map에 blur를 먹인다. 교재에서는 edge를 살리는 blur를 사용하였다.

 

- 무작위 Ray에 맞은 Depth Map위의 점을 계산할 때, 그 픽셀과 타겟 픽셀의 거리만으로 계산하면 오류가 생길 수 있다.

- 두 픽셀이 view 공간 상에서 한 평면 위에 (n, 타겟 픽셀) 존재할 수 있기 때문이다. 이런 경우는 해당 픽셀이 타겟 픽셀을 가린다고 판정하면 안된다.

- 이럴 때를 대비해서, 노멀 평면과 (무작위로 선정된 픽셀 - 타겟 픽셀)의 내적을 수식에 넣어서 자동으로 scaling이 되도록 하면 된다.

 

 

4. App에서 해줘야 하는 일 

- 일단 Depth 와 Normal을 현재 view(카메라)에 대해 렌더링 요청한다.

- 그리고 ssao 쉐이더를 이용해서 ssao map을 렌더링 요청한다.

- 이제 ssao map을 기본 쉐이더에 바인드 해준다. 그렇게 하면 현재 vertex에 대해 NDC, Proj, View 변환을 이용해서 ssao map에 기록된 정보를 뽑아내고, 그 ambient 값으로 밝기가 정해진다.

-

5. 연습 문제

 

(클릭하면 커집니다.)

 

연습 문제 1

- K-D Tree와 사진 트리(Quadtree) (or 팔진 트리(Octree))에 대해 조사해 보는 문제다.

- 일단 K-D Tree에 대해 적어보자면... Binary Tree를 K 개의 Dimension에 대해 수행하는 느낌?

- float3는 (x, y, z)를 가지고 있으니까 각 차원에 대해서 차례대로 분할을 하는 것이다. 

- 아래 점들을 x, y, z 순으로 k-d tree에 대충 넣어보면

(3, 5, 8) (1, 8, 4) (9, 3, 7) (3, 5, 8) (7, 7, 3) (8, 1, 5) (4, 3, 9) (9, 2, 4) (0, 5, 3) (9, 4, 4)

1) x >= 5를 기준으로 나눈다 (루트 느낌쓰)

x < 5

(3, 5, 8)  (1, 8, 4) (3, 5, 8) (4, 3, 9) (0, 5, 3)

x >= 5

(9, 4, 4) (7, 7, 3) (9, 3, 7) (8, 1, 5) (9, 2, 4)

2) 그 다음 y >= 5를 기준으로 나눈다.

(깊이가 한개 내려왔으니 각 노드에서 y >= 3, y >= 7로 해도 되는데... 그냥 똑같이 한다.)

x < 5
	y < 5
(4, 3, 9) 
	y >= 5
(0, 5, 3) (3, 5, 8) (1, 8, 4) (3, 5, 8)

x >= 5
	y < 5
(9, 3, 7) (8, 1, 5) (9, 2, 4) (9, 4, 4)
	y >= 5
(7, 7, 3)

3) 그 다음 z >= 5를 기준으로 나눈다.

(여기도  깊이가 하나 더 내려왔으니 각 노드를 양분하는 4개의 z 값을 다 다르게 해도 되는데.. 나는 그냥 똑같이 한다.)

x < 5 
	y < 5  
        z < 5 
x 
		z >= 5 
(4, 3, 9) 
	y >= 5 
    	z < 5 
(0, 5, 3) 
(1, 8, 4) 
		z >= 5 
(3, 5, 8)  (3, 5, 8) 

x >= 5 
	y < 5 
    	z < 5 
(9, 2, 4) (9, 4, 4) 
		z >= 5 
(9, 3, 7) (8, 1, 5)  
	y >= 5 
    	z < 5 
(7, 7, 3) 
		z >= 5	 
x

- 요런 느낌쓰가 K-D Tree 인듯 하다.

 

- 이제 Quadtree에 대해 좀 적어보면 (Octree도 똑같다.) 어떤 데이터를 찾는  메타데이터를 엄청 많이 사용해서 탐색 속도를 빠르게 하는 느낌인데, 그게 본인의 위계 위치(?) 느낌인 것이다.

- Screen을 예로 들면 (x, y) 화면 전체를 4사분면으로 나누고, 각 점들 p(x', y')이 각 사분면들 끼리 모아놓는 것이다.

- 그리고 그 아래 hierarchy는 그 사분면 끼리 모인점들을 또 4사분면으로 나눠서 끼리끼리 모아놓는 것이다.

빨강 - depth 1 / 초록 - depth 2 / 파랑 - depth 3

 

- 깊이를 많이 내려갈 수록 저장용량을 엄청 많이 사용하는데, 그만큼 탐색 속도는 더 빠를 것이다.

(옥트리는 4개가 아니라 8개로 나뉜다는 점 빼고는 똑같은 알고리듬을 사용한다.)

 

연습 문제 2

- Ssao Blur를 edge preserve를 하지 않고, 그냥 가우시안 블러 결과를 확인하는 문제다.

그냥 가우시안 블러                                                                                                       edge preserve blur

- 무슨 차이가 있는지 잘 모르겠다 ㅋㅋㅋㅋ

 

연습 문제 3

- Ssao 과정을 Compute Shader에서 어떻게 구현할지 알고리듬을 개괄적으로 생각해보는 문제다.

- 일단 기존 예제의 방식은 쉐이더를 구분을 해서 + ssao 결과를 Texture로 캐싱해서 넘겨주는 방법을 사용한다.

 

- Compute Shader에서 작업하는 것도... 비슷하지 않을까?

- DrawIndexedInstanced 대신 Dispatch를 사용하고

- Texture2D 대신에 RWTexture2D을 사용하고...

- ID3D12Resource는 2D 일거고 view만 따로 연결해서 사용하면 되지 않을까 상상해본다. 

(물론 이걸 직접하면 구현하면 사용법 미숙으로 인한 잔 버그들이 엄청나게 발생하겠지만 ... ㄹㅇㅋㅋ)

 

 

연습 문제 4

- r이 (p, n)평면 포함 판정을 하는 자기 교차(self-intersection)을 하지 않았을 때 결과를 확인 하는 문제다.

결과

 

6. 메모용

- Texture2D 역할을 하는 ID3D12Resource의 Buffer 사이즈 구하기

- GetRequiredIntermediateSize()

- msdn : GetRequiredIntermediateSize 함수(D3dx12.h) - Win32 apps | Microsoft Learn

 

GetRequiredIntermediateSize 함수(D3dx12.h) - Win32 apps

데이터 업로드에 사용할 버퍼의 필요한 크기를 반환합니다.

learn.microsoft.com

 

- K-D Tree 참고자료

kNN.15 K-d tree algorithm (youtube.com)

 

더보기

책 : DirectX 12를 이용한 3D 게임 프로그래밍 입문