감시자 패턴은 객체 사이에 일 대 다의 의존 관계를 정의해두어, 어떤 객체의 상태가 변할 때 그 객체에 의존성을 가진 다른 객체들이 그 변화를 통지 받고 자동으로 업데이트될 수 있게 만듭니다.

 

관찰차 패턴을 java에서는 java.util.Observer 라이브러리로 지원하고, C# 에서는 event 키워드로 지원한다.

4.1 업적 달성

업적의 종류가 보통 광범위하기 때문에 단순하게 생각한다면, 업적의 조건을 만족하게 되는 부분에 업적 달성 함수를 호출하게 된다면 코드가 지저분해질 것이다.

 

대상 객체는 자신을 관찰하는 모든 관찰자들 배열 (혹은 리스트)를 가지며, 특정 기준이나 사건 발생 시에 onNotify 함수를 호출시켜 알림을 모두에게 보낸다. 

순차적으로 onNotify 메서드를 호출하기 때문에 (동기적) 한 관찰자의 onNotify 반환이 늦으면 느려질 수 있다.

관찰자 포인터 배열을 사용하는 대신에 연결 리스트를 사용할 수도 있다.

공유를 통해 많은 수의 소립(fine-grained) 객체들을 효과적으로 지원하는 패턴

 

3.1 숲에 들어갈 나무들

나무들이 동일한 메시 정보(폴리곤, 정점), 텍스처를 사용한다면 이 특성들을 공유 데이터로써 GPU에 한 번만 보내야 한다.

3.2 수천 개의 인스턴스

나무 인스턴스를 그릴 때 공유 데이터를 사용하도록 한다.

 

Direct3D, OpenGL에서는 하드웨어적으로 인스턴스 렌더링을 지원한다.

https://lemonyun.tistory.com/57

 

16. 인스턴싱과 절두체 선별

인스턴싱 : 한 장면에서 같은 물체를 여러 번 그리는 것, 성능을 크게 향상할 수 있다. 절두체 선별 : 시야 절두체 바깥에 있는 일단의 삼각형들을 간단한 판정으로 골라내서 기각하는 기법 16.1

lemonyun.tistory.com

DirectX 12를 이용한 3D 게임 프로그래밍 입문 16장에서 다룬 인스턴싱에서는 아래와 같은 자료를 셰이더에 전달했다.

 

 

공유 데이터 : 재질 자료를 담은 구조적 버퍼, 텍스처 배열, 정점 버퍼 뷰, 인덱스 버퍼 뷰

개별 데이터 : 인스턴스별 자료 항목을 담은 구조적 버퍼 (material index, world matrix, textransform)

3.4 지형 정보 (경량 패턴 사용 예시)

맵이 격자 타일 형태라고 할때 

Terrain tiles_[WIDTH][HEIGHT] 형태의 배열을 사용하여 타일 정보를 관리할 수 있다.

ㄴ Terrain을 enum으로 사용하는 경우

ㄴ 지형 종류에 대한 데이터 (예를 들면 해당 지형에서의 기온)를 위한 함수가 별도로 필요함

 

지형 종류에 대한 데이터 캡슐화를 위해 Terrain을 클래스로 정의

하나씩 선언된 물, 풀, 용암에 대한 Terrain이 공유 데이터로써의 역할을 한다.

명령 패턴 - 간단하게 정리하자면 함수 호출을 매개변수화 한 것?

콜백 함수, 함수 포인터를 사용하여 구현할 수 있다.

 

2.1 입력키 변경

 

버튼을 눌렀을때 특정 메서드(행동)가 수행하도록 하는 코드가 있다면

 

1. 모든 행동을 실행하는 공통 상위 클래스 Command를 정의 (execute 함수가 있는 인터페이스)

2. 행동별로 상위 클래스를 상속받는 하위 클래스를 만들고 execute 함수를 정의 (execute에서 실제로 메서드를 호출)

3. 입력 핸들러 코드에는 버튼마다 상위 클래스 포인터를 저장

4. 입력 핸들러 초기화 시점에 버튼에 하위 클래스 객체를 바인드함

 

2.2 액터에게 지시하기

위의 예에서 execute 함수에 매개변수로 GameActor 객체를 받는다.

 

명령을 실행할 때 액터만 바꾸면 플레이어가 아닌 액터에 명령을 실행 할 수 있다.

플레이어와 같은 명령을 사용하는 AI 캐릭터가 있다면 적용할 수 있다.

 

Command 객체를 선택하는 부분 (입력 핸들러 코드)과 액터(누가 명령을 수행하는지)를 디커플링함으로써 코드가 유연해진다.

 

2.3 실행취소와 재실행

Command 클래스에 가상 함수 Undo()를 정의한다.

 

어떤 일을 하는지를 정의한 명령 객체(Command를 상속받은)를 반복해서 생성한다.

그 명령 객체는 이전 명령의 상태를 저장할 수 있어야 한다. (execute() 함수에서 현재 상태를 이전 상태에 기록하고 상태를 변경해야 한다.)

 

Undo() 함수는 현재 상태를 이전 상태로 바꿔야한다.

 

이전 명령의 상태를 저장하고 있어야 하므로 Command 자료형을 저장할 수 있는 배열이나 스택같은 곳에 명령 객체를 동적으로 생성해 저장하고 있어야 한다. (버튼 등의 입력으로 명령을 수행할 때 마다)

 

이전 상태로 돌아가기 위해서는 배열이나 스택의 포인터를 이동해가면서 포인터가 가리키는 곳의 명령 객체의 Undo() 함수를 호출하면 된다. 

 

재실행은 게임에서 잘 쓰이지 않을 수도 있지만, '리플레이'는 게임에서 자주 쓰인다.

매프레임마다 전체 게임 상태를 저장하는 대신 전체 개체가 실행하는 명령 모두를 매 프레임 저장한 뒤 리플레이 할때 저장한 명령들을 순서대로 실행해 게임을 시뮬레이션한다.

 

1.1 좋은 소프트웨어 구조란?

코드를 얼마나 쉽게 변경할 수 있느냐가 코드 설계를 평가하는 척도가 된다.

 

코드를 고치려면 고치려는 부분의 기존 코드를 이해해야 하기 때문에 작업에 관련된 코드의 양을 줄여야 한다.

이 관점에서 보면 커플링이 적은 코드가 곧 좋은 소프트웨어 구조를 만든다.

 

하지만 지나치게 확장성에 신경쓰다 보면 추상화를 위한 보조 코드가 더 많아져 실제 작업 코드를 찾기가 어려워질 수도 있고, 좋은 구조를 유지하는데에도 꾸준한 노력이 필요하기 때문에 비용이 추가적으로 발생할 수도 있다.

 

1.3 성능과 속도

프로그램의 유연성과 성능은 반비례 관계에 있다.

코드를 유연하게 만드는 많은 패턴이 가상함수, 인터페이스, 포인터, 메세지 같은 메커니즘에 의존하는데, 다들 어느정도의 런타임 비용을 요구하기 때문이다.

하지만 유연성이 좋아야 게임을 쉽게 변경할 수 있고, 개발 속도가 빨라진다.

 

최적화 기법은 구체적인 제한을 선호한다. 

ㄴ 인터페이스나 가상함수의 사용을 줄인다.

ㄴ 성능상 비용이 줄어든다.

 

처음에는 코드를 유연하게 유지하다가 기획이 확실해진 다음에 추상 계층을 제거하여 최적화를 진행하는 타협안이 있다.

 

1.4 나쁜 코드의 장점

기획 확인이 필요할때, 필요한 기능만 대강 돌아가도록 하는 코드는 적절하다.

이렇게 만든 버릴 코드는 확실히 버릴 수 있어야 한다.

 

23.1 뼈대 좌표계들의 계통구조

캐릭터의 골격은 계통구조로 만들어진다. 예를들면 팔은 상박, 하박 손으로 이루어지고 이들은 부모 자식 관계이기 때문에부모가 회전하면 자식들도 회전한다.

 

각 뼈대(상박, 하박, 손)의 기하구조는 자신의 국소 좌표계와 관절로 모형화된다.

물체의 회전을 편하게 하기 위해 관절은 물체 국소 좌표계의 원점에 둔다.

 

사람의 팔을 예로 들면 상박(위팔)이 하박(아래팔)의 부모가 된다.

 

23.2 메시 스키닝

결속 공간(전체 표피가 정의된 국소 좌표계) ----(오프셋 변환)----> (뼈대 국소 공간)

스키닝에서는 각 뼈대의 뿌리변환 (뼈대 국소 공간 -> 뿌리 공간)을 구한다.

 

뿌리변환 = 부모변환 * 부모의 뿌리변환으로 정의하기 때문에 하위 뼈대의 뿌리변환을 얻기 위해서는 부모의 뿌리변환이 반드시 정의되어 있어야한다. 그렇기 때문에 트리를 하향식으로 운영하여 부모의 뿌리변환이 항상 존재하도록 한다. 상향식으로 하게 되면 공통의 조상을 공유하는 뼈대들에 대해 동일한 행렬 곱셈을 중복해서 수행해야 한다.

 

23.3 정점 혼합

정점 혼합 : 골격을 감싸는 표피의 정점들을 애니메이션 하는 방법을 위한 알고리즘 

표피는 연속적인 메시인데 관절 같은 부위의 정점은 표피의 한 정점에 영향을 주는 뼈대가 여러 개일 수 있다. 이 경우 정점의 최종 위치는 영향을 주는 뼈대들의 최종 변환들의 가중 평균으로 결정된다. 이런 방식으로 정점을 혼합하면 관절 주변에서 정점들이 매끄럽게 전이되어서 적당히 탄력있는 모습의 표피가 만들어진다.

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

22. 사원수 (quaternion)  (0) 2022.07.11
21. 주변광 차폐  (0) 2022.07.10
20. 그림자 매핑  (0) 2022.07.08
19. 법선 매핑  (0) 2022.07.04
18. 입방체 매핑  (0) 2022.07.02

사원수를 쓰는 이유

x y z 축을 순서에 따라 회전시키게 되면 축이 겹치게 되는 현상(gimbal lock)이 생기기 때문

 

복소수와 사원수

특별한 사원수의 곱

사원수 곱셈은 교환법칙을 만족하지 않지만 결합법칙은 만족한다.

사원수 곱셈의 항등원 e = (0, 0, 0, 1)이다. (항등 사원수)

사원수 곱셈은 덧셈에 대한 분배법칙을 만족한다.

 

실수 s = (0, 0, 0, s)

벡터 u = (x, y, z, 0)

 

22.2.5 사원수의 켤레와 사원수의 크기

사원수 q = (q1, q2, q3, q4) = (u, q4)의 켤레를 q*로 표기한다.

q* = (-q1, -q2, -q3, q4) = (-u, q4)

 

켤레의 성질

1. (pq)* = q*p*

2. q와 q*를 더하면 실수가 된다. (0, 0, 0, 2q4)가 되므로

3. q와 q*의 곱은 사원수 크기의 제곱(실수)이다.

4. q q* = q* q = || q ||² = 사원수 q의 크기의 제곱

22.2.6 역 사원수

사원수 곱셈의 항등원 (0, 0, 0, 1)을 만드는 역 사원수

크기가 1인 단위사원수의 경우 역 사원수 =  켤레 사원수가 된다.

 

22.2.7 극형식

단위 사원수 q = (q1, q2, q3, q4) = (u, q4)에 대하여 ||u||² + q4² = 1 이다.

u와 같은 방향의 단위벡터를 n이라고 하면 u = sinθn 로 나타낼 수 있고, sin²θ n² + cos²θ = 1이기 때문에

q = (sinθn, cosθ)로 나타낼 수 있다.

 

θ 대신에 -θ를 대입할 경우 (n sin(-θ), cosθ) -> (-n sinθ, cosθ) = q *(켤레 사원수)

 

 

22.3 단위 사원수와 회전

회전 연산자

순사원수 p =(v, 0) (v = 3차원 점 또는 벡터)를 축 n에 대해 θ각도 만큼 회전시킨 사원수

 

q와 -q가 같은 결과를 반환하는데 이는 회전 방향의 차이이다.

 

회전행렬을 사원수 회전 연산자로 변환할 수 있고 사원수 회전 연산자를 회전행렬으로 변환할 수 있다.


22.4 사원수 보간

단위 사원수는 4차원 단위구 구면에 놓인 4차원 단위벡터이다.

 

b와 -b가 기하적으로는 같은 회전이지만, 4차원 단위구에서는 반대 방향을 뜻하고 

slerp(a, b, t)와 slerp(a, -b, t)는 짧은 호를 따라 보간될 것인지 긴 호를 따라 보간될 것인지를 결정한다.

||a - b||²와 ||a + b||² 중 ||a - b||²이 더 작은경우에는 a, b를 사용하는 것이 짧은 호를 따라 보간된다는 뜻이다.

a, -b를 사용하면 긴 호를 따라 보간된다.

 

연습문제

 

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

23. 캐릭터 애니메이션  (0) 2022.07.12
21. 주변광 차폐  (0) 2022.07.10
20. 그림자 매핑  (0) 2022.07.08
19. 법선 매핑  (0) 2022.07.04
18. 입방체 매핑  (0) 2022.07.02

 

21.1 반직선 투사를 통한 주변광 차폐

3차원 모형을 주변광 항으로만 조명하면 물체 전체에 하나의 색이 고르게 입혀진 모습이 나옴

주변이 얼마나 가려졌는지를 추정하여 차폐도(가려진 정도)를 계산한다.

 

물체 표면의 점 P에 대한 차폐도를 계산하는 방법 중 하나는 반구에 반직선을 투사(광선 투사)하는 방법이다.

P 중심의 반구에 무작위 반직선을 쏘아서 반구와 교차하지 않는 반직선의 비율을 차폐도로 사용한다. 

 

차폐도가 1에 가까우면 점 P가 거의 가려진 상태라는 뜻이다.

도달도 = accessibility, ambient access

차폐도 = 1 - 도달도

 

삼각형의 무게중심에서 뻗어나가는 무작위 반직선과 장면 메시(팔진트리 공간)의 교차여부를 확인하여 삼각형의 ambientaccess를 구하고 삼각형을 구성하는 3개의 정점 특성에 ambientAccess 값을 누적하여 저장한다.

 

삼각형 하나에 반직선대 삼각형 교차 판정이 많이 일어나므로 정적 모형에 대해서는 주변광 차폐를 계산하는 방식이 괜찮을 수 있지만, 애니메이션 되는 모형의 주변광 차폐를 동적으로 계산하는 것은 비현실적이다.

 

21.2.1 화면 공간 주변광 차폐(SSAO) - 법선과 깊이 렌더링 패스, 주변광 차폐 패스

화면 공간 정보를 이용해서 주변광 차폐를 실시간으로 계산하는 방법

 

1. 법선과 깊이 렌더링 패스

시야 공간 기준의 법선 벡터를 해상도가 화면 전체인 텍스처에 렌더링한다. (텍스처의 FORMAT이 DXGI_FORMAT_R16G16B16A16_FLOAT)

 

2. 주변광 차폐 패스

화면 전체를 덮는 사각형 하나(삼각형 두개로 이루어진)을 렌더링해서 화면의 픽셀마다 픽셀 셰이더가 실행되도록 만든다.

점 P의 시야공간 위치를 구했으면 반구 내부에 존재하는 시야공간의 무작위 표본점(q)의 위치를 구한다.

무작위 표본점 q들을 다시 투영시켜 q의 텍스처 좌표를 구한다. (투영행렬 x 텍스처 행렬)

텍스처 좌표를 사용하여 깊이 맵에서 깊이(NDC)를 추출한다.

표본점 P에 했던 과정(NDC 깊이 -> View 깊이)을 사용하여 q를 향한 반직선에 있는 가장 가까운 가시 픽셀의 깊이를 구한다.

가시 픽셀의 깊이와 q의 깊이의 비율을 이용하여 잠재적 차폐점 r의 시야공간 좌표를 구할 수 있다. (q와 r은 같은 반직선 위에 있으므로)

 

차폐 판정

1. 시야 공간 깊이 거리 | p.z - r.z | 

ㄴ 거리가 최대 거리를 넘어가면 차폐는 없는 것, 최대 거리 이내면 거리에 선형 비례하여 차폐도를 조정한다.

z0은 최소거리, z1은 최대거리

 

 

2. p의 법선 벡터와 r - p 벡터의 내적 (둘다 단위 벡터이므로 cosθ)

ㄴ 자기 교차를 방지하는 용도로 쓰인다. 두 벡터가 이루는 각이 90도에 가까우면 r과 p가 같은 평면에 있다는 뜻이므로 차폐도에 영향을 주면 안된다. 코사인 90은 0

 

두 조건에서 얻은 계수를 곱한 것이 해당 표본에서 얻은 차페도를 뜻하고, 모든 표본의 차폐도를 더한 뒤 표본 개수 (위의 예시에서는 14)로 나누어 평균을 구하면 해당 픽셀에서의 차페도를 구할 수 있다.

도달도를 특정한 지수로 거듭제곱하면 대비(contrast)를 높일 수 있다.

 

21.2.3 화면 공간 주변광 차폐(SSAO) - 흐리기 패스

적은 표본점으로 만든 차폐도 맵은 잡음이 많다.

표본 수를 늘리면 잡음이 사라지겠지만 실시간 응용 프로그램에서는 성능 때문에 비현실적이다.

SSAO 맵에 가장자리 보존 흐리기 (물체의 가장자리 변들에 대해서는 흐리기를 적용하지 않는다. 가장자리는 법선 맵과 깊이 맵을 이용해서 검출한다) 를 적용한다.

 

흐려진 SSAO맵을 장면에 적용하는 방법

ㄴ 분산광, 반영광 항에는 적용되면 안되므로 알파 혼합을 사용해서 후면 버퍼를 수정하는 방법은 안된다.

ㄴ SSAO 맵을 셰이더 입력으로 묶어 픽셀 셰이더에서 투영 텍스처 좌표 (투영행렬 x 텍스처 행렬)를 사용하여 SSAO 맵의 표본을 추출해 주변광 계산에 도달도로 사용한다.

 

SSAO 맵을 사용하여 장면을 그리는 렌더링 패스에서는 깊이 판정을 할 필요가 없으므로 깊이 비교 함수를 Equal로 바꾸면 겹쳐 그리기를 방지 할 수 있다.

 

가장자리 보존 흐리기 

흐리기를 수행할 픽셀의 색상을 계산할 때 흐리기 필터의 크기만큼 표본을 사용하게 되는데, 중앙 픽셀의 법선 벡터와 이웃 픽셀(표본) 의 법선 벡터 사이 각도가 지나치게 크거나 ( 예제에서는 두 벡터의 내적 값 < 0.8f ) 두 픽셀의 깊이 값 차이가 크면 ( 깊이 > 0.2f ) 표본이 불연속 경계에 걸쳐 있는 것으로 간주하여 표본에서 제외시킨다.

 

물체의 가장자리 픽셀의 흐리기 계산을 하게 된다면 이러한 조건들에 의해 대부분의 표본들이 제외되어 흐리기 과정을 거쳐도 기존 색상을 유지할 확률이 높게 된다.

 

연습문제

2.

양방향 흐리기 대신 가우스 흐리기를 사용하기 위해 ssaoBlur.hlsl를 수정한다

법선과 깊이의 조건을 무시하고 표본을 모두 사용하게 바꾼다.

가장자리 보존 흐리기
가우스 흐리기

체감이 잘 안되긴 하지만 차폐가 많이 일어나는 경계 부분에서(구 아래 부분) 차이가 난다. 

 

4.

자기 교차 검증 부분을 제거한 가장자리 보존 흐리기

자기 교차 점검을 하지 않으니 차폐가 없는 픽셀 (바닥 메시)도 어둡게 보인다.

 

연습문제 파일

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

 

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 게임 프로그래밍 입문' 카테고리의 다른 글

23. 캐릭터 애니메이션  (0) 2022.07.12
22. 사원수 (quaternion)  (0) 2022.07.11
20. 그림자 매핑  (0) 2022.07.08
19. 법선 매핑  (0) 2022.07.04
18. 입방체 매핑  (0) 2022.07.02

 

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