동적 색인화는 Direct3D 12에만 존재하는 새로운 기법이다.

Texture2DArray와는 달리 크기와 형식이 서로 다른 텍스처들을 담을 수 있다.

 

15.2 Camera 클래스

right 벡터, lookat 벡터, up 벡터

카메라 위치 벡터

시야 행렬, 투영 행렬

수직 시야각, 종횡비, 가까운 평면 거리, 먼 평면 거리

ㄴ 멤버 변수로 모두 저장하고 있음

 

setLens() // 투영 행렬 갱신

UpdateViewMatrix() // 시야 행렬 갱신

ㄴ 멤버 변수 값들을 사용하여 행렬 변수에 계산된 값을 저장

 

함수 이름

yaw = 좌우 둘러보기 (y축 회전)pitch = 위아래 끄덕 (카메라 right축 회전)roll = 갸웃갸웃 (카메라 lookat축 회전)

 

15.5 동적 색인화

1. 상수 버퍼의 한 요소를 색인으로 사용할 수 있다.

2. SV_PrimitiveID와 같은 시스템 ID를 색인으로 사용할 수 있다.

3. 셰이더 프로그램 안에서 수행한 어떤 계산의 결과를 색인으로 사용할 수 있다.

4. 텍스처에서 추출한 표본의 값을 색인으로 사용할 수 있다.

5. 정점 구조체의 한 성분을 색인으로 사용할 수 있다.

 

예제에서의 사용법

모든 재질 자료를 담은 재질 구조체의 배열 (구조적 버퍼 자원) 

텍스처 배열 (서술자 테이블)

객체 별 상수 버퍼에 재질 인덱스 정보만 넘기면 셰이더 파일에서 텍스처와 재질에 접근할 수 있다. (1번 사용법) 

 

연습문제

2.

Q와 E키로 회전하도록 만들었다.

mLook ( 카메라 기준 Z 축 )에 대한 회전행렬을 만들고

mRight 벡터와 mUp 벡터에 회전 행렬을 곱해 값을 갱신한다.

연습문제 파일

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

 

GitHub - lemonyun/Directx12_study: 2022/06/10

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

github.com

 

 

테셀레이션을 사용하는 이유

1. GPU상의 동적 LOD

 

2. 효율적인 물리 및 애니메이션 계산

ㄴ 물리와 애니메이션을 저다각형 메시에 대해 수행하고, 그 저다각형 메시를 테셀레이션해서 고다각형 버전을 만들면 물리 및 애니메이션 계산을 낮은 세부도에서 수행함으로써 계산량을 줄일 수 있다.

 

3. 메모리 절약

ㄴ 메모리에는 저다각형 버전을 담아두고 GPU 테셀레이션으로 즉석에서 고다각형 버전을 생성하는 방식을 사용하면 메모리를 절약할 수 있다.

 

테셀레이션 단계들은 정점 셰이더 단계와 기하 셰이더 단계 사이에 위치할 수 있으며, 생략 가능하다.

1. 덮개 셰이더 단계

2. 테셀레이터 단계

3. 영역 셰이더 단계

 

14.1 테셀레이션 기본도형 위상구조

IASetPrimitiveTopology 메서드로 삼각형이 아닌 제어점들로 이루어진 패치를 입력으로 설정

D3D_PRIMITIVE_TOPOLOGY_N_CONTROL_POINT_PATCHLIST (N은 1부터 32 까지 있음)

제어점이 5개 이상인 패치는 보통 곡면을 구현하기 위해 사용된다.

 

14.2 덮개 셰이더

두 종류의 셰이더로 구성됨

1. 상수 덮개 셰이더

struct PatchTess
{
    float EdgeTess[4] :  SV_TessFactor;
    float InsideTess[2] : SV_InsideTessFactor;
}

사각형 패치에 대한 테셀레이션의 경우 6개의 테셀레이션 개수를 지정할 수 있다.

변 테셀레이션 계수 4개와 내부 테셀레이션 계수 2개

 

모든 테셀레이션 계수가 0이면 그 패치는 폐기된다.

 

테셀레이션 정도를 결정하는데 흔히 쓰이는 측정치

1. 카메라와의 거리

2. 화면 영역 포괄도 (물체가 화면의 픽셀을 몇개나 덮는지)

3. 방향 (시점에 따라 물체의 윤곽선으로 보이는 삼각형들은 다른 삼각형들보다 세분하면 자연스럽다.)

4. 표면 거칠기 (표면 거칠기가 높은 세부사항이 많은 표면은 테셀레이션 정도를 높일 필요가 있다.)

 

2. 제어점 덮개 셰이더

제어점을 받아서 제어점을 출력하는데, 제어점 개수를 증강할 수 있다.

struct HullOut
{
	float3 PosL : POSITION;
};

[domain("quad")] // 패치의 종류 tri, quad, isoline 중 하나
[partitioning("integer")] // 테셀레이션 단위가 정수 // 분수는 fractional_even 또는 fractional_odd
[outputtopology("triangle_cw")] // 세분으로 만들어지는 삼각형의 정점 감김 순서
[outputcontrolpoints(4)] // 하나의 입력 패치에 대해 출력할 제어점 개수 (= 덮개 셰이더의 실행 횟수)
[patchconstantfunc("ConstantHS")] // 상수 덮개 셰이더 함수의 이름
[maxtessfactor(64.0f)] // 셰이더가 사용할 테셀레이션 계수의 최댓값 (하드웨어는 이 정보로 최적화)
HullOut HS(InputPatch<VertexOut, 4> p,
	uint i : SV_OutputControlPointID,
        uint patchId : SV_PrimitiveID)
{
	HullOut hout;
    hout.PosL = p[i].PosL;
    return hout;
}

14.3 테셀레이터 단계

상수 덮개 셰이더가 출력한  테셀레이션 계수들에 기초해서 패치들을 테셀레이션하고 정점들을 생성해 출력한다.

 

14.4 영역 셰이더

테셀레이터 단계에서 출력된 정점마다 한 번씩 호출됨, 동차 절단 공간으로 변환하는 역할

세 가지 종류의 입력을 받는다.

1. 테셀레이션된 정점 위치의 매개변수화된 좌표 (u, v)

2. 제어점 덮개 셰이더가 출력한 제어점

3. 상수 덮개 셰이더가 출력한 테셀레이션 계수들

struct DomainOut
{
	float4 PosH : SV_POSITION;
};

[domain("quad")]
DomainOut DS(PatchTess patchTess, // 상수 덮개 셰이더가 출력한 테셀레이션 계수들
		float2 uv : SV_DomainLocation, // 테셀레이터 단계에서 얻은 정점의 매개변수화된 좌표
           	const OutputPatch<HullOut, 4> quad) // 제어점 덮개 셰이더가 출력한 제어점 패치
{
	DomainOut dout;
    
    float3 v1 = lerp(quad[0].PosL, quad[1].PosL, uv.x);
    float3 v2 = lerp(quad[2].posL, quad[3].PosL, uv.x);
    float3 p = lerp(v1, v2, uv.y);
    
    float4 posW = mul(float4(p, 1.0f), gWorld); //
    dout.PosH = mul(posW, gViewProj); 			// 동차 좌표계로 변환
    
    return dout;
}

14.6 삼차 베지에 사각형 패치

다수의 제어점을 이용해서 곡면을 생성하는 방법

삼차 베지어 매개변수 공식
삼차 베지에 곡선
삼차 베지에 곡선의 도함수 (곡선의 접선 벡터를 계산할 때 유용)

14.6.2. 삼차 베지에 곡면

영역 셰이더에서 계산이 일어남

 

테셀레이터 단계에서 만들어진 정점의 좌표 uv의 0과 1사이의 v.x 와 uv.y 값을 삼차 베지에 공식의 t에 집어 넣음

테셀레이션되어 생성된 정점들 각각의 위치 (정점 한 개) 를 계산하는데 16개의 제어점이 모두 쓰인다.

 

1.

사각형 패치 대신 삼각형 패치를 테셀레이션 한 결과
삼각형 패치를 입력으로 받는 영역 셰이더 함수
정점 버퍼에 넣을 정점 배열도 수정한다.

5.

2차 베지에 곡면에 대한 매개변수 방정식 p(u, v)

7. 8.

제어점이 아홉 개인 이차 베지에 곡면

베지에 곡면 예제를 제어점이 아홉 개인 이차 베지에 곡면을 사용하도록 만들고, 곡면에 색조와 조명을 적용했다.

기존 3차 베지에 곡면 구현을 위해 16개의 제어점이 사용되던 것을 9개로 줄였다.

베지에 곡면 관련 함수를 수정하였다.

픽셀 셰이더에서 조명을 계산하기 위해 법선 벡터를 u, v에 대한 편미분 계수를 외적함으로써 구했고, uv 값은 텍스처 좌표값에 대응되도록 하였다.

 

연습문제 파일

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

 

GitHub - lemonyun/Directx12_study: 2022/06/10

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

github.com

 

 

GPU는 임의 메모리 접근을 염두에 두고 설계된 CPU와 대조적으로 하나의 저장 장소 또는 연속된 저장 장소들에서 대량의 메모리를 읽어서 처리하는 작업에 최적화되어 있다.

 

계산 셰이더는 렌더링 파이프라인 옆에 따로 존재하며, GPU 자원의 자료를 직접 읽어 들이거나 GPU 자원에 직접 자료를 기록할 수 있다.

 

페르미 아키텍처는 SM을 16개 사용

SM(Streaming Multiprocessor) = 다중 처리기

ㄴ SP 32개를 사용

 

SP(Streaming Processor) = CUDA 코어

ㄴ 코어 하나당 스레드하나 담당

 

GPGPU 프로그래밍 (general purpose GPU) 

ㄴ GPU를 비그래픽 분야에 응용하는 것

ㄴ GPU가 계산한 결과를 CPU에서 읽어야 하는 경우가 많다.

ㄴ 자료 병렬성이 있는 알고리즘을 사용해야 한다.

 

13.1 스레드와 스레드 그룹

다중 처리기당 적어도 두 개의 스레드 그룹을 두는 것이 바람직

각 스레드 그룹에는 그룹의 모든 스레드가 접근할 수 있는 공유 메모리가 주어짐

다중 처리기는 하나의 워프(32개 스레드)를 SIMD32 단위로 처리함.

스레드 그룹의 크기는 워프 크기(32)의 배수인 것이 바람직하다. (AMD의 경우 웨이브프런트(64)의 배수)

 

13.2 계산 셰이더

스레드 그룹을 격자 형태로 분배 (cmdlist->Dispatch)

스레드 그룹 하나의 스레드 개수 구성 (계산 셰이더)

계산 셰이더는 D3D12_COMPUTE_PIPELINE_STATE_DESC 구조체를 서술

 

텍스처자원은 cmdlist->SetGraphicsRootDescriptorTable

UAV자원은 cmdlist->SetComputRootDescriptorTable

 

계산 셰이더는 각 스레드에서 실행된다.

 

13.3 자료 입력 자원과 출력 자원

SRV는 읽기 전용 서술자이다.

출력 자원을 계산 셰이더에 묶으려면

UAV 자원을 ALLOW_UNORDERED_ACCESS 플래그를 지정해서 만들고 D3D12_UNORDERED_ACCESS_VIEW_DESC 구조체를 서술해 UAV 서술자를 만들어야 한다. 

 

출력 자원은 RW 접두사가 붙으며 출력 원소의 형식과 차원을 < > 으로 지정해야한다.

ㄴ 예시 : RWTexture2D<int2> gOutput;

 

 

텍스처 필터링을 거친 표본을 사용할 경우 문제점

1. Sample 메서드를 사용할 수 없어서 SampleLevel 메서드를 사용하여 텍스처 필터링을 통해서 텍스처의 표본을 추출해야 한다.

2. 정수 색인이 아닌 [0, 1] 구간으로 정규화된 텍스처 좌표를 사용해야 한다. [ x / 텍스처 너비, y / 텍스처 높이 ] 를 사용해야 한다.

 

13.3.4 구조적 버퍼 자원

SRV 버퍼 자원은 정점 버퍼 자원을 기본 힙에 만들어 올리는 것과 동일한 방법으로 하면 된다.

UAV 버퍼 자원은 생성할때 자원 상태와 플래그를 UNORDERED_ACCESS로 해줘야 한다

 

struct Data

{

float3 v1;

float2 v2;

}

 

(.hlsl)

StructuredBuffer<Data> gInputA : register(t0);

StructuredBuffer<Data> gInputB : register(t1);

StructuredBuffer<Data> gOutput : register(u0);

 

(응용 프로그램)

slotRootParameter[0].InitAsShaderResourceView(0);

slotRootParameter[1].InitAsShaderResourceView(1);

slotRootParameter[2].InitAsUnorderedAccessView(0);

 

루트 서술자들을 받는 루트 서명은 서술자 힙을 거치지 않고 자원의 가상 주소를 루트 인수로서 직접 전달할 수 있는데 이 방법은 텍스처가 아니라 버퍼 자원에 대한 SRV와 UAV에만 가능하다.

 

13.3.5 계산 셰이더의 결과를 시스템 메모리에 복사

계산 셰이더에서 UAV 버퍼 자원에 결과값을 저장하고 메모리에 복사하기 위해서는 힙 속성이 READBACK인 자원(UAV 버퍼와 자료 형식과 크기가 동일) 을 만든 뒤에 cmdlist->CopyResource 메서드를 이용해 자원을 READBACK 버퍼에 복사한다. 이후 Map 함수로 자료를 매핑하여 CPU에서 읽을 수 있도록 한다.

 

13.4 스레드 식별 시스템 값

그룹 ID (SV_GroupID) : 그룹 ID

그룹 스레드 ID (SV_GroupThreadID) : 그룹 내에서 각 스레드의 ID

배분 스레드 ID : SV_DispatchThreadID

3가지 식별 값 모두 int3의 자료형을 가진다.

 

13.5 추가 버퍼와 소비 버퍼

자료를 계산하는 순서가 중요하지 않은 경우 추가 버퍼와 소비 버퍼라고 불리는 종류의 구조적 버퍼를 사용하는 것이 좋다.

 

ConsumeStructuredBuffer<Particle> gInput;

AppendStructuredBuffer<Particle> gOutput;

 

Particle  p = gInput.Consume();

 

gOutput.Append(p);

 

13.6 공유 메모리와 동기화

groupshared float gCache[256]; // 스레드 그룹마다 하나씩 존재

공유 메모리의 흔한 용도는 텍스처 값을 저장하는 것이다. 동일한 텍스처를 여러 번 추출하는 것은 느리기 때문에 

 

스레드 그룹 내 각 스레드가 gCache (공유 메모리)에 값을 저장을 완료하기 전에 gCache 내용을 사용하려 하면 안되기 때문에, GroupMemoryBarrierWithGroupSync() 함수를 사용해 스레드가 모두 공유 메모리에 값을 기록하기 전까지 대기하도록 만들어야 한다.

 

13.7.2 텍스처 대상 렌더링 기법

원래 후면버퍼를 화면에 출력하는 방법

후면버퍼 = 텍스처

텍스처에 대한 rtv 서술자를 출력 병합기 단계에 묶고(OMSetRenderTargets) Present 함수 호출하여 화면에 후면 버퍼 용 출력

 

또 다른 텍스처를 생성하고 그에 대한 rtv 를 만들어서 똑같이 진행해도 가능함 (화면 밖 텍스처 대상 렌더링 방법)

텍스처 대상 렌더링 기법의 용도

1. 그림자 매핑

2. 화면 공간 주변광 차폐 (SSAO)

3. 입방체 맵을 이용한 동적 반사

 

연습문제

1. 2. 3.

1번 2번 문제는 해결했으나 3번 문제는 해결하지 못했다.

계산 셰이더와 출력 결과값 파일
UAV로 사용할 버퍼 자원을 만들고 입력 자료로 사용할 자료를 임시 업로드 버퍼를 사용하여 복사했다. (정점 버퍼 올리는 방법과 동일)
DX11의 UAV FLAG
DX12의 UAV FLAG

DX11에서는 버퍼에 대한 UAV를 만들 때 D3D11_BUFFER_UAV_FLAG_APPEND를 지정하면 해당 버퍼를 셰이더에서 추가 버퍼 혹은 소비 버퍼로 인식하게 만들 수 있는 거 같은데 책과 인터넷을 찾아봐도 DX12에서 추가 소비 버퍼의 사용 예제가 보이질 않는다. FLAG_NONE을 대체하여 사용해봤는데 계산 셰이더가 제대로 작동하지 않는다. 책에서도 이에 대한 설명이 부실하여 일단은 넘어가도록 해야겠다.

D3D12_BUFFER_UAV_FLAG_APPEND는 없다..?

UAV_FLAG_NONE으로 설정하고 셰이더 파일에서 추가 소비 버퍼를 사용하는 대신에 구조적 버퍼(RWStructuredBuffer)로 바꿔주기만 해도 정상적으로 결과가 나온다.

 

5.

예제가 이미 만들어져 있어서 성능 비교만 했다. 격자 크기 512x512 시뮬레이션 대신에 256x256으로 진행하였다.(512로 하면 CPU 버전에서 파도가 렌더링되지 않음)

CPU에서 파도를 처리하는 경우
계산 셰이더(GPU) 에서 파도를 처리하는 경우

GPU에서 계산을 수행하는 것이 CPU에서 수행하는 것보다 처리가 확연하게 빠름을 알 수 있었다.

CPU를 사용한 프로그램은 괜찮았는데 계산 셰이더를 사용한 프로그램을 돌리니 GPU에서 평소에 듣지 못하던 소음이 발생했다.

 

동작 과정:

Disturb가 발생할 때 mCurrsolUav 서술자가 가리키는 버퍼에 계산 셰이더를 이용해서 파도를 일으키면 (계산 셰이더1)

Update마다 mCurrsolUav와 mPrevSolUav를 이용해 mNextSolUav에 다음 정점 위치를 계산하여 기록한다 (계산 셰이더2)

Update가 끝나면 srv, uav, 자원을 서로바꿔준다. (prev를 next로 curr을 prev로 next를 curr로)

 

연습문제 파일

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

 

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

15. 1인칭 카메라 구축과 동적 색인화  (1) 2022.06.29
14. 테셀레이션 단계들  (0) 2022.06.29
12. 기하 셰이더  (0) 2022.06.24
11. 스텐실 적용  (0) 2022.06.22
10. 혼합  (0) 2022.06.22

 

기하 셰이더는 기본도형을 입력받는다. 기본도형 마다 기하 셰이더가 실행된다.

기하 셰이더는 기하구조를 새로 생성하거나 폐기할 수 있다.

기하 셰이더에서 나오는 정점 위치들은 반드시 동차 절단 공간으로 변환된 것이어야 한다.

12.1 기하 셰이더 프로그래밍

기하셰이더 한번의 호출로 출력할 최대 정점의 개수를 함수 정의 앞에 붙여야 한다.

[maxvertexCount(N)]

ㄴ N이 지나치게 크면 성능상의 문제가 생길 수 있다.

 

기하 셰이더의 입력 매개변수

ㄴ 입력되는 기본도형의 종류를 명시해야 한다. (point, line, triangle, lineadj, triangleadj)

기하 셰이더의 출력 매개변수

ㄴ inout 붙여야 한다.

ㄴ 기하 셰이더가 출력하는 기본도형의 정점들을 담는 스트림 형식의 객체를 사용한다. 

ㄴ 스트림 형식에 따라 기하 셰이더에서 나온 정점들이 만드는 기본도형의 종류가 달라진다. (PointStream, LineStream, TriangleStream)

 

TriangleStream 같은 경우 삼각형 띠를 출력하는데, 삼각형 목록을 출력하고 싶으면 RestartStrip을 사용하여 흉내낼 수 있다.

 

12.2 빌보드 기법

멀리 있는 물체를 3차원으로 렌더링 하는 대신 2차원 텍스처로 렌더링하고 카메라를 바라보도록 만드는 방법

 

 

기하 셰이더의 입력으로 들어오는 부호 없는 정수 매개변수의 의미소 형식 SV_PrimitiveID

ㄴ 입력 조립기 단계에서 기본도형마다 ID를 부여한다. ( 0, 1, 2 ... )

ㄴ 기하 셰이더나 픽셀 셰이더에서 사용할 수 있다.

 

12.3 텍스처 배열

텍스처 배열은 하나의 자원이다. 자원 서술 구조체에 DepthOrArraySize라는 필드가 있는데 원하는 배열 크기를 여기에 적으면 된다.

 

일반 텍스처 (Texture2D)

gDiffuseMap.Sample(gsamAnisotropicWrap, pin.TexC) * gDiffuseAlbedo;

 

텍스처 배열 (Texture2DArray)

float3 uvw = float3(pin.TexC, pin.PrimID%3);
float4 diffuseAlbedo = gTreeMapArray.Sample(gsamAnisotropicWrap, uvw) * gDiffuseAlbedo;

 

한번의 텍스처 배열 설정과 한번의 그리기 호출로 여러 기본도형에 서로 다른 텍스처를 그릴 수 있다. 

 

12.3.3 텍스처 배열 적재

밉맵 수준이 하나인 dds들을 합쳐서 텍스처 배열을 담은 dds 파일 하나를 만든다. (texassemble 도구)

그 후에 밉맵 생성, 픽셀 형식 변경 할 수 있다. (texconv 도구)

 

12.3.4 텍스처 부분 자원

텍스처 배열의 부분자원에 부여된 색인의 순서는 이렇게 된다.

12.4 알파 포괄도 변환

혼합 설정에서 AlphaToCoverage을 켜게 되면 MSAA 포괄도 계산시에 다각형 기준의 부분픽셀 중심의 위치만 고려하는 것이 아니라 알파 채널도 고려하게 된다.

일반적으로 불규칙한 형태를 알파 채널로 오려내느 식으로 활용하는 텍스처에 대해 사용하는 것이 바람직하다.

기본적으로 MSAA가 활성화 되어있어야 한다.

 

연습문제

1.

7장 예제 프로그램을 변형하여 진행했다.

GeometryGenerator에 xz평면에 만들어지는 원 기하구조를 생성하는 함수를 만들었다.
Default.hlsl의 정점셰이더와 픽셀셰이더로 그린 원의 기하구조
circle.hlsl의 기하 셰이더 함수

기본도형으로 line을 입력받아 2개의 정점을 linestream(선 띠) 형태로 출력하도록 만들었다.

출력되는 정점의 첫 번째 정점은 입력받은 line이 이루는 두 정점의 가운데 점이고

두번째 정점은 그 점에 y 값을 + 4.0f 해준 값이다.

결과

2.

시점과의 거리에 따른 세부 수준(LOD) 변화 구현

기하구조의 중심의 위치는 원점

시점과 기하구조의 거리에 따라 다른 스트림을 출력하도록 기하 셰이더 내부에서 다른 함수들을 사용한다.

4.

메시의 정점 법선 시각화
모든 renderitem의 정점 법선을 시각화 한다.
정점 법선 시각화를 위한 pso
정점 법선 기하 셰이더

정점을 기본도형으로 입력받아 정점 두개로 이루어진 line (법선)을 출력하는 기하 셰이더이다.

 

연습문제 파일

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

 

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

14. 테셀레이션 단계들  (0) 2022.06.29
13. 계산 셰이더  (0) 2022.06.28
11. 스텐실 적용  (0) 2022.06.22
10. 혼합  (0) 2022.06.22
9. 텍스처 적용  (0) 2022.06.21

 

스텐실 버퍼는 후면 버퍼, 깊이 버퍼와 해상도가 같다.

스텐실 버퍼는 특정 픽셀 단편들이 후면 버퍼에 기록되지 못하게 하는 역할을 한다.

PSO 에 D3D12_DEPTH_STENCIL_DESC 구조체를 채워 설정할 수 있다.

 

11.1 깊이 스텐실 버퍼의 자료 형식과 버퍼 지우기

사용할 수 있는 자료 형식

1. DXGI_FORMAT_D32_FLOAT_S8X24_UINT : 텍셀 하나에 64비트 사용, 32비트는 부동소수점 깊이값, 8비트는 정수 스텐실 값 나머지는 빈칸

2. DXGI_FORMAT_D24_UNORM_S8_UINT : 텍셀 하나에 32비트 사용, 24비트는 부호 없는 깊이 값, 8비트는 정수 스텐실 값

 

스텐실 값은 [0, 255]로 깊이 값은 [0, 1]로 사상된다는 공통점이 있다.

 

매 프레임마다 draw 하기전에 깊이 스텐실 버퍼를 ClearDepthStencilView 함수를 사용하여 지워줄 수 있다.

 

11.2 스텐실 판정

스텐실 판정은 픽셀이 래스터화되는 과정에서 (출력 병합기 단계)에서 일어난다.

 

11.3 깊이 스텐실 상태의 서술

D3D12_DEPTH_STENCIL_DESC 구조체

ㄴ 깊이 버퍼링 활성화 여부 (비활성화 하면 깊이 판정이 일어나지 않아 깊이에 관계없이 렌더링 순서에 따라 그려짐)

ㄴ 깊이 버퍼 쓰기(기록) 활성화 여부 (비활성화 하면 깊이 판정은 일어나지만 깊이 버퍼에 쓰는 것은 안됨)

ㄴ 깊이 판정에 쓰는 비교 함수

 

ㄴ 스텐실 판정 활성화 여부

ㄴ 스텐실 readmask

ㄴ 스텐실 writemask

ㄴ 후면 삼각형에 대해서 어떻게 처리 할것인지

ㄴ 전면 삼각형에 대해서 어떻게 처리 할것인지

 

11.4 평면 거울 구현 

1. 일반 물체를 그린다.

 

2. 거울을 구성하는 픽셀들의 후면 버퍼에는 아무것도 기록하지 않고 (혼합 상태 설정에서 렌더 대상 쓰기 마스크를 0으로)

스텐실 비교함수를 ALWAYS로 지정 (스텐실 판정 항상 통과)

스텐실 판정과 깊이 판정 모두 통과한 경우 픽셀에 1을 쓰기 위해 스텐실 기준값(StencilRef)를 1로 설정하고, 스텐실 기준값을 버퍼에 덮어쓰는 연산인 REPLACE을 지정해준다.

 

3. 거울에 의해 반사된 물체를 그린다.

스텐실 판정만 켜놓고 일반 물체를 그리는 설정과 동일하게 한다.

스텐실 버퍼에 있는 값이 1인 경우에만 판정을 통과하게 하게 만들기 위해

스텐실 기준값(StencilRef)을 1로 설정하고, 스텐실 비교함수를 EQUAL로 지정해준다.

판정 후 스텐실 버퍼를 변경할 필요가 없으므로 모든 경우(stencil만 통과, depth만 통과, 모두 통과) KEEP 연산을 지정해준다.

 

 

거울 안의 반사된 물체를 그릴 때 사용되는 조명은 따로 상수버퍼를 하나 더 추가해서 저장해야 한다.

래스터라이저 상태 객체에 시계방향으로 감긴 삼각형을 후면 삼각형으로 간주하라고 해야한다. (메시를 반사해도 법선은 뒤집히지 않기 때문에)

 

11.5 평면 그림자의 구현

평행광 그림자

n과 L의 내적이 음수가 되어 (투영된 점의 w 성분이 음수가 되어) 절단되는 상황(시야 공간 바깥에 있는 판정)을 막기 위해서 L은 빛이 나아가는 방향의 반대 방향을 사용한다. 

 

점광 그림자

평행광 그림자 식의 L 자리에 정점으로부터 광원으로 향하는 벡터를 넣으면 된다.

 

범용 그림자 행렬

ㄴ 점광과 평행광 모두에 적용할 수 있는 범용 그림자 행렬

ㄴ L 벡터의 w값을 1로 지정하면 점광을 사용하며  xyz가 점광의 위치를 뜻한다.

ㄴ L 벡터의 w값을 0으로 지정하면 평행광을 사용하며 xyz가 빛이 나아가는 방향의 반대 방향 벡터를 뜻한다.

 

그림자 이중 혼합 문제를 스텐실 버퍼를 이용해 해결하는 방법

1. 0으로 초기화 되어 있는 그림자 메시의 스텐실 버퍼 값이 0인 픽셀만 판정에 성공하도록 해야한다.

2. 스텐실 기준값(StencilRef)을 0으로 설정하고 스텐실 비교 함수를 EQUAL로 한다.

깊이판정과 스텐실판정 모두 성공한 경우 INCR연산을 하게 만들어 해당 픽셀의 스텐실 버퍼 값이 1이 되도록 한다.

 

연습문제

3. 4. 스텐실 판정 안하면 된다.

5.

벽의 깊이 판정이 일어나지 않아 나중에 그려진 반사된 해골픽셀이 벽을 덮어쓴다.
깊이 판정을 정상적으로 한 경우 (반사된 해골의 스텐실 판정은 끈 상태)

7.

bmp 파일을 텍스처로 로드하기 위해서 외부 라이브러리를 사용한다.

https://github.com/Microsoft/DirectXTK12/wiki/WICTextureLoader

 

GitHub - microsoft/DirectXTK12: The DirectX Tool Kit (aka DirectXTK12) is a collection of helper classes for writing DirectX 12

The DirectX Tool Kit (aka DirectXTK12) is a collection of helper classes for writing DirectX 12 code in C++ - GitHub - microsoft/DirectXTK12: The DirectX Tool Kit (aka DirectXTK12) is a collection ...

github.com

원래 Visual studio 2017를 사용하고 있었는데 이 라이브러리를 사용하려면 2019 버전에서 작업해야 한다는 것을 늦게 알아서 몇 시간을 허비했다..

 

Bmp 파일들로 부터 텍스처 자원을 생성하고 srv서술자를 만드는 함수인 LoadBmpTextures() 를 구현했다.

void BlendApp::LoadBmpTextures() {

	HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);

	CD3DX12_CPU_DESCRIPTOR_HANDLE hDescriptor(mSrvDescriptorHeap->GetCPUDescriptorHandleForHeapStart());

	hDescriptor.Offset(3, mCbvSrvDescriptorSize);

	for (int i = 1; i <= 60; i++) {
		auto boltAnimTex = std::make_unique<Texture>();

		std::unique_ptr<uint8_t[]> decodedData;
		D3D12_SUBRESOURCE_DATA subresource;

		wchar_t str[30] = L"../../BoltAnim/Bolt";
		wchar_t num[4];
		wchar_t* extention = L".bmp";
		swprintf(num, 4, L"%03d", i);
		wcscat_s(str, num);
		wcscat_s(str, extention);


		std::wstring ws(str);
		std::string name(ws.begin(), ws.end());

		//str texName 
		std::wstring numws(num);
		std::string numstr(numws.begin(), numws.end());

		boltAnimTex->Name = std::string("Bolt" + numstr);

		ThrowIfFailed(LoadWICTextureFromFile(md3dDevice.Get(), str, boltAnimTex->Resource.GetAddressOf(), decodedData, subresource));

		const UINT64 uploadBufferSize = GetRequiredIntermediateSize(boltAnimTex->Resource.Get(), 0, 1);

		CD3DX12_HEAP_PROPERTIES heapProps(D3D12_HEAP_TYPE_UPLOAD);

		auto desc = CD3DX12_RESOURCE_DESC::Buffer(uploadBufferSize);

		md3dDevice->CreateCommittedResource(
			&heapProps,
			D3D12_HEAP_FLAG_NONE,
			&desc,
			D3D12_RESOURCE_STATE_GENERIC_READ,
			nullptr,
			IID_PPV_ARGS(boltAnimTex->UploadHeap.GetAddressOf()));

		UpdateSubresources(mCommandList.Get(), boltAnimTex->Resource.Get(), boltAnimTex->UploadHeap.Get(),
			0, 0, 1, &subresource);

		auto barrier = CD3DX12_RESOURCE_BARRIER::Transition(boltAnimTex->Resource.Get(),
			D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
		mCommandList->ResourceBarrier(1, &barrier);

		
		D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
		srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
		srvDesc.Format = boltAnimTex->Resource.Get()->GetDesc().Format;
		srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
		srvDesc.Texture2D.MostDetailedMip = 0;
		srvDesc.Texture2D.MipLevels = -1;
		md3dDevice->CreateShaderResourceView(boltAnimTex->Resource.Get(), &srvDesc, hDescriptor);

		hDescriptor.Offset(1, mCbvSrvDescriptorSize);

		mTextures[boltAnimTex->Name] = std::move(boltAnimTex);
	}

}

 

기존에 사용되던 srvheap의 0번 1번 2번 인덱스에는 풀, 물, 철망을 위한 Material 정보가 들어있는데,

60프레임 짜리 애니메이션을 저장해야되므로 srvheap의 크기를 63으로 늘리고 3번 ~ 62번 인덱스에 서술자를 저장한다.

 

가산 혼합을 위한 pso를 따로 만들어주었다.

텍스처를 1 / 60초마다 변경하는 것은 gt.TotalTime()의 소수점 부분을 1 / 60 으로 나눠서 3번 ~ 62번중에 몇번 서술자를 쓸 것인지 지정하도록 만들었다.

결과물

연습문제 파일

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

 

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

13. 계산 셰이더  (0) 2022.06.28
12. 기하 셰이더  (0) 2022.06.24
10. 혼합  (0) 2022.06.22
9. 텍스처 적용  (0) 2022.06.21
8. 조명  (0) 2022.06.20

 

픽셀 셰이더가 생성한 픽셀 단편들은 출력 병합기 단계의 입력으로 들어가는데, 이때 깊이 판정이나 스텐실 판정에 의해 기각되지 않은 픽셀 단편들은 후면 버퍼에 기록된다.

혼합은 출력 병합기 단계에서 일어나며, 후면 버퍼에 이미 기록된 픽셀을 완전히 덮어쓰는 것이 아니라 일정한 공식에 따라 새 픽셀과 기존 픽셀을 섞은 결과를 기록하는 것이다.

 

10.1 혼합 공식

1. 현재 후면 버퍼에 있는 대상 픽셀 rgb색상 * 혼합계수1

2. 원본 픽셀에 대해 출력한 rgb색상 * 혼합계수2

혼합 연산자로 1, 2 를 계산

 

둘을 혼합 연산하면 새로운 색상을 얻을 수 있다.

색상의 알파값에도 혼합 공식을 따로 적용할 수 있다.

10.4 혼합 상태

PSO에 D3D12_BLEND_DESC를 서술하여 설정한다.

ㄴ 알파 포괄도 변환 활성화 여부

ㄴ 렌더 대상마다 혼합을 다른 방식으로 할 것인지 여부

ㄴ D3D12_RENDER_TARGET_DESC 배열 렌더 대상마다 혼합을 다른 방식으로 할 경우 원소마다 다른 혼합 설정이들어간다. 렌더 대상들에 동일한 혼합 방식을 적용할 경우 0번 인덱스의 혼합 설정이 사용된다.

 

D3D12_RENDER_TARGET_DESC 구조체

ㄴ 논리 연산 할것인지 혼합 연산 할것인지 지정

ㄴ rgb 혼합 계수 두개

ㄴ rgb 혼합 연산자

ㄴ 알파 혼합 계수 두개

ㄴ 알파 혼합 연산자

ㄴ 논리 연산자 

ㄴ 렌더 대상 쓰기 마스크 (혼합의 결과가 기록될 후면 버퍼 색상 채널을 결정한다. R G B A)

 

10.5 혼합 연산 예제

가산, 감산, 승산(곱셈)

투명도

ㄴ 반투명한 물체를 그리는데 사용할 수 있다.

ㄴ 이 혼합 방식으로 반투명을 구현한다면 혼합을 사용하지 않는 물체를 먼저 그리고, 혼합을 사용하는 물체들은 카메라에서 먼 물체부터 차례대로 정렬한 뒤 그려야 한다.

ㄴ 가산, 감산, 승산 혼합의 경우 혼합을 사용하지 않는 물체는 먼저 그려야 하지만 혼합을 사용하는 물체들끼리 정렬할 필요는 없다. 교환 법칙을 만족하기 때문에

 

ㄴ 혼합 물체를 그릴때는 깊이 판정을 꺼야 한다. 혼합 물체에 의해 가려진 물체의 픽셀 단편이 제거되어 색이 날아가 버리기 때문이다.

 

10.6 알파 채널

분산광 텍스처 맵에 담긴 알파 채널을 사용하여 색상의 알파값을 원본 색상읠 알파 값을 지정한다.

 

10.7 픽셀 잘라내기

픽셀 셰이더에서 clip 함수를 사용하여 투명에 가까운 픽셀을 잘라낼 수 있다. (해당 픽셀에 대한 계산이 더 이루어지지 않는다.)

ALPHA_TEST가 정의되어 있는 경우에만 clip을 사용하도록 해야 한다.
알파 판정에는 비용이 따르므로 필요한 경우에만 사용해야 한다.

10.8 안개

절두체의 먼 평면 뒤에 있던 물체가 카메라가 이동함에 따라 절두체 안으로 들어오면서 갑자기 화면에 튀어나오는 파핑 현상을 숨길 수 있다.

ㄴ 패스 상수 버퍼에 안개 색상, 안개 시작 거리, 안개 범위 값을 넘긴다.

ㄴ 정점과 시점의 거리에 따라 안개의 비율을 계산하여 안개 색상과 계산한 픽셀 색상을 보간하여 최종 색상을 결정한다.

 

연습문제

2. 이번 장의 혼합 예제('BlendDemo')를 물을 제일 먼저 렌더링하도록 수정하고, 그 실행 결과를 설명하라.

혼합 물체인 물을 가장 먼저 렌더링하면 물 아래에 있는 땅이 보이지 않는다.

5. 혼합 예제를, 적색 채널과 녹색 채널로의 색상 기록을 비활성화하는 혼합 상태를 생성해서 혼합에 적용하도록 수정하라.

혼합의 결과가 Blue와 Alpha 채널에만 기록되어서 물이 더 푸르스름하게 보인다.
물을 그릴 때 사용되는 파이프라인 객체 transparent pso를 만들때 혼합 상태를 정의하는데 이 때 렌더 대상 쓰기 마스크를 설정할 수 있다.

연습문제 파일

https://github.com/lemonyun/Directx12_study/tree/main/10/Chapter%2010%20Blending/BlendDemo

 

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

12. 기하 셰이더  (0) 2022.06.24
11. 스텐실 적용  (0) 2022.06.22
9. 텍스처 적용  (0) 2022.06.21
8. 조명  (0) 2022.06.20
7. Direct3D의 그리기 연산 제2부  (0) 2022.06.17

 

9.2 텍스처 좌표

법선 벡터와 마찬가지로 삼각형의 정점마다 텍스처 좌표를 지정해주면 보간에 의해 삼각형의 모든 점마다 그에 대응되는 텍스처 좌표가 결정된다.

9.3 텍스처 자료 원본

DDS (DirectDraw Surface format)가 지원하는 기능

1. GPU가 직접 해제할 수 있는 압축 형식

2. 밉맵

3. 텍스처 배열

4. 입방체 맵

5. 입체 텍스처

 

9.4 텍스처 생성 및 활성화

텍스처 자원은 CreateDDSTextureFromFile12 함수로 생성

 

Texture 구조체는 Light과 Material과 달리 텍스처 자원의 포인터와 업로드 힙(자원 생성 시 기본 힙으로 복사하기 위한)의 텍스처 자원 포인터를 가지고 있다.

 

CreateDefaultBuffer 함수를 사용해서 정점 버퍼를 기본 힙에 복사하고 업로드 힙의 임시자원과 기본 힙에 복사된 정점 버퍼 자원을 가리키는 포인터를 MeshGeometry 구조체가 가지는 것과 비슷하게 

CreateDDSTextureFromFile12 함수를 사용해서 텍스처 자원을 기본 힙에 복사하고 Texture 구조체는 업로드 힙의 자원과 텍스처 자원의 포인터를 출력 매개변수로 얻는다.

 

자원을 생성했으면 SRV 서술자 힙과 SRV 서술자 힙을 만든다.

 

텍스처를 파이프라인에 묶기

재질을 상수 버퍼에서 가져오기 때문에 한 번의 그리기에 동일한 재질만 사용할 수 있는 제한된 방식을 사용하지 않고 재질 자료를 텍스처 맵에서 가져오는 방법

 

상수버퍼의 DiffuseAlbedo(분산 반사율)은 그대로 사용할 수 있다. 텍스처에서 얻은 분산 반사율과 곱하여 사용

(텍스처에 색을 입힐 수 있는 방법) (상수 버퍼의 분산 반사율이 (1, 1, 1, 1)이면 그대로)

 

Material 구조체에 사용할 분산광 반사율 텍스처 맵의 색인(SRV 힙에서의)을 저장한다.

 

9.5 필터

밉맵 필터링 설정

점 필터링 (point)

ㄴ 화면의 투영된 기하구조의 해상도에 가장 가까운 밉맵 수준을 사용하는 방식

선형 필터링 (linear)

ㄴ 화면의 투영된 기하구조의 해상도에 가장 가까운 이웃한 두 밉맵 수준을 각각 선형 보간해서 텍스처 색상 하나씩 뽑은 다음에 두 밉맵 수준을 보간하여 최종 색상 결정

비등방 필터링 (anisotropic)

ㄴ 법선 벡터와 카메라의 시선 벡터 사이의 각도가 클 때 발생하는 왜곡 현상이 완화된다.

 

9.6 텍스처 좌표 지정 모드

텍스처 좌표의 정의역 [0, 1] 바깥의 좌표가 주어졌을 때의 처리 방식

1. 순환

2. 테두리 색상

3. 한정

4. 반사

9.7 표본추출기 객체

텍스처 필터링과 좌표 지정 모드를 지정하는 객체

 

표본 추출기를 생성하는 방법

1. 표본 추출기 힙에 표본 추출기 서술자를 설정하고 레지스터 슬롯에 묶어 셰이더로 보내는 방법

2. 정적 표본추출기를 사용 

ㄴ 표본 추출기 배열을 만들어 사용, 표본 추출기 힙을 설정하지 않아도 됨

ㄴ 테두리 색상 설정에 제한이 있음

9.8 셰이더에서 텍스처 표본 추출

하나의 SRV가 Texture2D 객체에 묶인다.

텍스처로부터 한 픽셀의 텍스처 좌표 (u, v)가 주어졌을 때 Sample메서드를 이용해서 그 픽셀에 해당하는 표본을 텍스처로부터 실제로 추출한다.

 

9.10 텍스처 변환

정점 셰이더에서 입력 텍스처 좌표를 변환하는데 쓰이는 상수 버퍼 변수

비례 행렬을 적용하면 텍스처를 타일 방식으로 입힐 수 있다.

텍스처의 간단한 애니메이션을 구현하는데 좋다.

 

gTexTransform 

gMatTransform // 재질에 대해 텍스처 변환을 적용하는 것이 합당한 경우

 

연습문제

2.

level 0 ~ level 4로 5개의 밉맵을 가진 dds파일을 만들었다.

멀리서 바라봤을 때는 가장 작은 레벨의 밉맵으로 면이 채워진다

점 밉맵 필터링을 사용한 경우 정육면체의 오른쪽 면을 기준으로

시점에서 가까운 쪽의 면의 일부분은 가장 가까운 수준의 밉맵이 3레벨 밉맵이고

먼 쪽 면의 일부분은 가까운 밉맵이 4레벨 밉맵이어서 경계가 생긴다.

선형 밉맵 필터링을 사용한 경우 하나의 밉맵을 선택하는 것이 아니라 가까운 두 밉맵의 텍스처 색상을 보간하기 때문에 

두 밉맵이 섞여 보인다.

 

3. 다중 텍스처 적용

두 텍스처의 성분별 곱셈으로 새로운 텍스처를 만들어낸다.
Default.hlsl를 수정하여 텍스처를 하나 더 받도록 한다.
Default.hlsl의 픽셀 셰이더 부분에서 두 텍스처를 성분별로 곱한다.
두 개의 텍스처 파일을 불러와 자원을 만든다.
srv 힙의 크기를 2로 늘리고 각각의 텍스처에 대한 서술자를 만든다.
루트 매개변수로 서술자 테이블을 사용했고 t0, t1레지스터에 담아서 보낸다.

4.

텍스처 좌표는 xy 평면에 존재하므로 z축을 기준으로 회전하는 행렬을 적용한다.

각도 theta를 totalTime()으로 지정했기 때문에 2π초마다 한바퀴를 돈다.

Z축 기준 회전을 할때 x= 0.5, y =0.5 (화염구의 중심)을 기준으로 회전시키고 싶기 때문에 x, y 각각 -0.5 씩 이동변환 시킨후 회전하고, 다시 +0.5씩 이동변환 시켜 제자리로 돌아오게 한다.

연습문제 파일

https://github.com/lemonyun/Directx12_study/tree/main/9/Chapter%209%20Texturing

 

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

11. 스텐실 적용  (0) 2022.06.22
10. 혼합  (0) 2022.06.22
8. 조명  (0) 2022.06.20
7. Direct3D의 그리기 연산 제2부  (0) 2022.06.17
6. Direct3D의 그리기 연산  (0) 2022.06.16

 

8.1 빛과 재질의 상호작용

조명을 사용할 때에는 정점 색상들을 직접 지정하지 않음. 표면의 재질들과 표면에 비출 빛들을 지정하고 조명 방정식을 적용해서 정점 색상이 결정되게 한다.

 

국소 모형 조명

ㄴ 대부분의 실시간 응용 프로그램이 사용하는 조형 모형으로 각 물체는 광원에서 직접 방출된 빛만 다룬다. (다른 물체에 의해 반사되어 들어오는 빛은 무시한다.)

전역 모형 조명

ㄴ 다른 물체에 의해 반사되어 들어오는 빛도 계산한다. 비용이 커서 실시간 게임에 사용하기에는 비용이 크다.

 

8.2 법선 벡터

정점 자료에 Normal 성분 값이 정점 법선 벡터 값인데 조명 계산에 쓰인다.

삼각형의 세 꼭짓점에서만 표면 법선을 계산해도 래스터화 과정에서 법선 성분 값이 보간되어 필요한 모든 표면 법선을 얻을 수 있다.

 

8.2.2 법선 벡터의 변환

법선 벡터를 비균등 비례변환이나 전단변환하게 되면 변환된 접선 벡터와 법선벡터는 서로 수직이 아니게 된다.

접선 벡터에 A 행렬로 변환 했다면 법선 벡터에는 A 역행렬의 전치행렬을 사용하여 변환하면 서로 수직이 된다.

 

8.3 조명의 주요 벡터

시점(E) : 눈의 위치

 

빛 벡터(L) : 표면의 점에서 광원을 가리키는 방향

반사 벡터(R) : 입사광이 표면 법선 n을 기준으로 반사된 방향

 

8.4 람베르트 코사인 법칙

복사 조도: 단위 면적당 복사 선속(빛 에너지)

비스듬히 비출수록 어두워짐

감소되는 빛의 비율는 L과 n의 내적으로 구할 수 있다. (L과 n이 수직이면 0)

8.5 분산 조명 (Diffuse)

분산 반사 (난반사): 모든 방향으로 고르게 반사되는 빛

ㄴ 색상과 분산 반사율의 내적으로 구해진다.

 

주변 조명

ㄴ 주변광(근사값)과 분산 반사율의 내적

 

8.7 반영 조명 (Specular)

프레넬 효과(Fresnel effect) : 두 매질 사이의 경계면에 빛이 도달하면 빛의 일부는 반사되고 나머지는 굴절되는 현상

 

ㄴ 매질에 따라 반사되는 빛의 양이 다르다.

ㄴ 법선벡터 n과 L 사이의 각도가 클수록 반사되는 빛의 양이 커진다.

 

표면 거칠기

ㄴ 완벽한 거울이 아닌 이상 반사된 빛은 거칠기의 정도에 따라 원뿔 모양으로 퍼진다.(반영 돌출부)

ㄴ 거칠기에 따라 정규분포 함수의 모양이 달라진다. 거칠기가 커지면 빛 에너지가 더 넓고 약하게 퍼지고

거칠기가 작으면 좁고 강하게 퍼진다.

8.8 조명 모형 정리

ㄴ 프레넬 값(매질의 속성), 분산 반사율, 거칠기 계수가 재질의 속성을 결정하는데 사용된다.

8.9 재질의 구현

정점에 색상을 지정하는 대신에 정점에 재질 값을 설정하고 텍스처 매핑을 사용한다.

 

물체 각각의 세계 행렬과 마찬가지로 각각의 재질을 담은 상수 버퍼를 업로드 힙에 올려서 관리한다.

여러 renderitem의 material 포인터가 같은 material 객체를 참조하면 여러 renderitem이 같은 재질을 사용할 수도 있다.

Material 객체에는 자기 자신의 상수 버퍼 index를 저장하고 있다.

 

8.10 평행광 (지향광)

빛의 색상과 빛의 벡터로 정의됨

8.11 점광

감쇠 계수를 구하기 위해 falloffstart와 falloffend라는 거리에 따라 빛이 감소하는 지점을 정의해아 한다.

감쇠 계수의 구간은 [0, 1]이며 광원에서 falloffstart 떨어진 점이 받는 계수는 1이고 falloffend가 받는 계수는 0이다.

이를 광원 값(색상)에 곱한다.

 

8.12 점적광

점적광 계수 : d와 -L의 각도 차이가 클수록 감소함, 지수 s를 조율하여 빛의 세기가 0으로 떨어지는 각도를 제어할 수 있음

빛의 색상에 감쇠계수와 점적광 계수를 모두 곱해야 해서 가장 연산비용이 비싸다.

 

8.13 조명 구현

셰이더 파일에 struct Light를 정의하고 패스 상수 버퍼에 사용하는 조명의 정보를 Light 배열로 넘긴다.

주변광 빛도 포함한다.

 

C++의 구조체 멤버와 hlsl의 구조체 채우기 방식을 동일하게 하여야 한다.

지향광, 점광, 점적광의 조명 계산 함수는 LightingUtil.hlsl이라는 셰이더 파일에 정의되어 있다.

 

픽셀 셰이더에서 한 표면의 점에 대해 모든 조명에 대한 계산을 하여 색상을 누적한 값이 최종 픽셀 색상이 된다.

 

연습문제

1.

B,G 값을 진동시키기 위해 전체 시간(초 단위)을 sin 함수에 넣음

4.

구 위치에 점광 10개를 만듬
Default.hlsl 파일에서 점광이 10개인 것을 기록

6.

툰 셰이딩 결과
거리에 따른 빛의 세기를 계산하는데 쓰이는 CalcAttenuation 함수를 변형하여 툰 셰이딩을 수행

연습문제 파일

https://github.com/lemonyun/Directx12_study/tree/main/8/Chapter%208%20Lighting

 

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

10. 혼합  (0) 2022.06.22
9. 텍스처 적용  (0) 2022.06.21
7. Direct3D의 그리기 연산 제2부  (0) 2022.06.17
6. Direct3D의 그리기 연산  (0) 2022.06.16
5. 렌더링 파이프라인  (0) 2022.06.14

+ Recent posts