20.1 장면 깊이의 렌더링

광원 시점에서 본 장면의 깊이 값을 그림자 맵이라고 불리는 깊이 스텐실 버퍼에 기록한다.

그림자 맵의 해상도가 높을수록 그림자의 품질이 좋지만 장면을 렌더링하는 데 필요한 시간과 그림자 맵을 저장하는 데 필요한 메모리도 늘어난다.

 

그림자 매핑 알고리즘에는 두 번의 렌더링 패스가 필요하다. 첫 패스에서는 광원의 시점에서 본 장면 깊이를 그림자 맵에 렌더링하고 둘째 패스에서는 카메라에서 본 장면을 후면 버퍼에 렌더링하되, 그림자 맵을 셰이더의 한 입력으로 두어서 그림자 적용 알고리즘을 구현한다.

 

20.2 직교 투영(Orthographic projection)

물체가 멀리 있을때와 가까이 있을때 같은 크기로 보인다.

 

직교 투영의 시야 공간을 나타내는 시야 입체(viewing volume)는 절두체 모양이 아니라 직육면체의 형태이다.

 

원근투영 변환은 투영 행렬로 서술되는 선형변환 부분과 원근 나누기(비선형변환) 으로 구성되는데

직교투영 변환은 전부 선형 변환이다.

 

직교 투영 행렬

20.3 투영 텍스처 좌표

ㄴ 텍스처를 임의의 기하구조에 투영된 것처럼 보이기 하기 위해 텍스처 좌표를 생성하는 것

 

광원에 대한

세계 공간 -> 시야 공간 변환 (시야 행렬 V)

시야 공간 -> NDC 공간 변환 (투영 행렬 P) (원근 나누기 포함)

NDC 공간 -> 텍스처 공간 변환 (텍스처 행렬 T)

텍스처 행렬 T

합성변환 VPT는 세계 공간 좌표를 광원 혹은 영사기에 대한 투영 텍스처 좌표로 변환한다.

 

렌더링 파이프라인의 경우 절두체 바깥에 있는 기하구조는 절단되지만 투영 텍스처 좌표를 생성할 때는 절단이 일어나지 않기 때문에 [u, v] 가 [0, 1]을 벗어나는 좌표는 좌표지정 모드에 따라 작동한다.

 

직교투영이나 원근투영을 이용하여 투영 텍스처를 적용할 수 있다.

원근 투영 구현의 경우 픽셀 셰이더에서 좌표 값을 w값으로 직접 나눠줘야 (원근 나누기) z값이 [0, 1] 범위가 되지만(NDC) 직교투영은 직교 투영 행렬만 곱해주면 z값 범위가 [0, 1]이 되어 원근 나누기를 해줄 필요가 없다.

 

20.4 그림자 매핑

그림자 맵

ㄴ광원 시점에서 본 장면 깊이를 텍스처 대상 렌더링 기법을 이용해서 깊이 버퍼에 기록한 것

광원의 시점에서 장면을 렌더링하려면 광원 시야 행렬과 광원의 시야 입체를 정의하는 광원 투영 행렬을 정의해야 한다.

시야 입체는 원근 투영의 경우 절두체 형태이고 직교 투영의 경우 직육면체 형태가 될 수 있다.

 

20.4.2 편향과 앨리어싱

그림자 맵의 해상도가 유한하기 때문에 그림자 맵의 한 텍셀이 장면의 한 영역에 대응되어 그림자 여드름이라고도 부르는 앨리어싱 문제가 발생한다.

 

그림자가 없어야 할 곳에 줄무늬 그림자가 나타나는데 이는 그림자 맵 영역 중 장면의 일부 픽셀은 그림자 맵의 깊이보다 깊이가 작기 때문이다.

 

이에 대한 적절한 해결책은 그림자 맵에 적절한 편향을 더해서 가짜 그림자가 나타나지 않도록 만드는 것이다. 하지만 편향치를 너무 크게 잡으면 그림자와 물체가 분리되어 보이는 현상(피터 팬 효과)가 발생할 수 있기 때문에 기하구조마다 적절한 편향치를 계산하여 적용해야 한다.

 

기울기 비례 편향 속성 지정은 래스터화 상태 서술에서 지정한다.

1. 고정 편향치

2. 편향치 최대값

3. 기울기 비례계수

깊이 편향치는 래스터화 도중에 (절단 이후에) 적용되므로, 기하구조 절단에는 영향을 미치지 않는다.

 

20.4.3 비율 근접 필터링 (PCF)

픽셀이 그림자 안에 있는지 판정을 주변 4개의 텍스처 좌표의 깊이 값들로 진행하고, 판정 결과들을 보간한다.

PCF 계산 과정

 

9번의 SampleCmpLevelZero의 평균값을 사용한다.

셰이더에서 SampleCmpLevelZero 메서드를 통해서 서용할 수 있다. 이 메서드는 통상적인 표본추출기 객체를 사용하지 않고 비교 표본추출기라는 것을 사용한다.

 

20.4.4 그림자 맵 구축

광원의 시야 행렬(기본 광원으로부터 유도) 과 투영 행렬(광원의 좌표계와 시야 입체를 나타냄)을 정의

시야 입체는 장면 전체를 감싸는 경계구를 이용해서 구함

 

광원의 시점에서 본 장면을 그림자 맵에 렌더링

mCommandList->OMSetRenderTargets(0, nullptr, false, &mShadowMap->Dsv());  // 장면을 깊이 버퍼에만 렌더링할 것이므로 렌더 대상은 null로 설정한다.

 

smapPsoDesc.RTVFormats[0] = DXGI_FORMAT_UNKNOWN;
smapPsoDesc.NumRenderTargets = 0;  // PSO의 렌더 대상 개수를 0으로 지정한다.

 

20.4.5 그림자 계수

그림자 계수는 [0, 1] 구간의 스칼라 값이다

PCF를 사용하는 경우 점의 일부분만 그림자 안에 있을때 그림자 계수는 [0, 1] 사이의 값이 된다.

 

정점 셰이더에서 정점 좌표(world)를 투영 텍스처 좌표로 변환하고

픽셀 셰이더에서 원근 나누기를 진행한 뒤 좌표의 z값을 depth(점의 깊이)로 사용하고 x, y값을 그림자 맵에서 깊이 값을 추출하는데 사용한다.

 

연습문제

1.

장면에 투영할 텍스처
직교 투영 행렬을 사용했을 경우(좌), 원근 투영 행렬을 사용했을 경우(우)

 

2.

좌표 지정 모드를 border로 바꾸고 테두리 색을 검은색으로 사용한 모습

 

연습문제 파일

https://github.com/lemonyun/Directx12_study/tree/main/20

 

GitHub - lemonyun/Directx12_study: 2022/06/10

2022/06/10. Contribute to lemonyun/Directx12_study development by creating an account on GitHub.

github.com

'읽은 책 > DirectX 12를 이용한 3D 게임 프로그래밍 입문' 카테고리의 다른 글

22. 사원수 (quaternion)  (0) 2022.07.11
21. 주변광 차폐  (0) 2022.07.10
19. 법선 매핑  (0) 2022.07.04
18. 입방체 매핑  (0) 2022.07.02
17. 3차원 물체의 선택  (0) 2022.07.01

+ Recent posts