Programming/D3D12

[책공부] Blend 예제 + chap 10 연습 문제

Dorasima 2024. 1. 16. 17:20

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

1. Blending은 GPU에서 작업해준다. 

- 픽셀 쉐이더는 화면(Viewport)에 점이 무슨 색으로 찍힐지 결정하는 친구이다. Blend를 만약에 하기로 했다면, 이미 픽셀로 찍혀있는 (Rasterized) 색과 App이 정해준 계산으로 새로운 색을 찍는 것이라고 한다.

- App에서 PSO의 D3D12_RENDER_TARGET_BLEND_DESC 으로 로직을 정해준다.

(Pixel Shader 바디에서 로직을 정해주는 것이 아니다. Pixel Shader의 로직이 끝나고 나온 값과 원래 찍힌 화면 픽셀이 계산에 쓰이는 것이다.)

- D3D12는 Alpha와 RGB blend를 따로 진행 할 수 있다.

- 그리고 픽셀 형식이 DXGI_FORMAT_R8G8B8A8_UNORM 라면 비트 연산으로도 블랜딩을 할 수 있다고 한다.

- 예제에서는 RGB blend만 해줬다. 새로 그려지는 픽셀(Src)의 알파값을 중심으로 원래 픽셀(Dest)과 섞어서 새로운 색을 그리는 것이다.

- Vertex Shader에서 Homogeneous 좌표로 즉, 렌더링 좌표로 변환이 되어서 넘어오기 때문에, 그냥 화면에 찍힌 색깔이 무엇인지 + 정해진 Blend 공식은 무엇인지만 신경쓰면 되는것이다.

 

2. App에서 해줘야 하는 것

- 이미 찍혀있는, 픽셀과 계산을 하는 것이기 때문에 alpha가 (0, 1) 값을 가지고 blend를 하기로 한 친구라면 opaque(불투명)한 친구가 다 화면에 찍힌 다음에 다음에 Draw 요청을 걸어야 한다.

- 그리고 만약에 transparent 한 친구가 여럿이 있고, 그것들이 카메라 사이에 여러개 존재한다면, 정확한 색이 화면에 찍히게 하기 위해서는 Homogeneous  Coords에서 Z - order를 맞춰야 한다.

(바로 위 내용은 예제에서는 아직 다루지 않았다.)

- 그리고 blend를 해주기로 한 PSO로 랜더링을 한다면, 추가적인 작업을 한다는 뜻이기 때문에 작업이 느려지게 된다. 그래서 opaque 한 친구들을 렌더링 할때는 blend 옵션을 꺼주어야 한다.

 

3. 언급할 만한 예제에서 사용한 방법(구조)

- 철망이나 울타리 같은 것을 표현할 때, Alpha 값을 가진 텍스쳐로 표현하였다. 복잡한 Vertex 구조가 필요 없이, Pixel Shader에서 Alpha 값을 기준으로 clip을 걸어서 중간중간 구멍이난 망 형태를 쉽게 표현할 수 있었다.

- Vertex Shader 에서 World 변환을 통해서 얻은 값을 Pixel Shader로 넘겨서, 해당 surface(표면)가 카메라와 얼마나 거리가 먼지 구할 수 있게 된다.  

(목표는 카메라와 픽셀을 찍을 surface가 멀다면 안개의 농도를 짙게하는 것이다.)

- 안개의 색으로 전부 가려버릴 거리를  정하고, 카메라와 표면 간의 거리로 안개의 농도를 구하는 식을 새운다.

(필요하다면 안개가 시작되는 거리를 정해서 식에 넣어도된다.)

- 그리고 쉐이더 함수인 lerp의 알파 값으로 사용해서, 계산된 픽셀 색과 안개 색을 적절히 섞어서 안개를 표현하게 된다.

4. 연습 문제

(클릭하면 커집니다.)

 

연습 문제 1

 

 

연습 문제 2

- Blending을 하기로 하고 + Alpha가 (0, 1)인 픽셀을 먼저 화면에 찍으면

- 다음처럼 Back Buffer에 초기로 넣었던 배경 값과 blending이 일어난다.

- m_MainPassCB.FogColor를 바꿔서 시도해보면, 알 수 있다.

 

 

연습 문제 3

- 원래 픽셀 값이 뭔지 모른다. 그러니 그냥 lerp에 들어가는 alpha값 (안개 농도) 을 계산하겠다.

fogStart = 10 / fogRange = 200

(a) (160 - fogStart) / fogRange = (160 - 10) / 200  = 0.75

(b) (110 - fogStart) / fogRange = (110 - 10) / 200 = 0.5

(c) (60 - fogStart) / fogRange = (60 - 10) / 200 = 0.25

(d) (30 - fogStart) / fogRange = (30 - 10) / 200 = 0.1

 

 

연습 문제 4

- fxc으로 컴파일 해서 asm을 확인한다.

- 일단 여기 방법이 있고 Syntax - Win32 apps | Microsoft Learn

 

Syntax - Win32 apps

Here is the syntax for calling FXC.exe, the effect-compiler tool. For an example, see Offline Compiling.

learn.microsoft.com

- 매크로를 넣어서 컴파일 하는 방법은

fxc "<경로>\Shaders\07_Blend.hlsl" /Od /D "ALPHA_TEST"="1"/Zi /T ps_5_1 /E "PS" /Fo "<경로>\Shaders\07_Blending_PS_Alpha.cso" /Fc "<경로>\Shaders\07_Blending_PS_Alpha.asm"

- fxc는 이미 환경변수로 추가가 되어있는 상태이다. 혹은 fxc.exe가 있는 곳으로 작업 폴더를 옮겼던가.

 

(나는 쉐이더 어셈블리를 볼 줄 모른다... 그니까 차이점만 대충 보자.)

 

ALPHA_TEST 를 하지 않는 쉐이더 어셈블리                                                    ALPHA_TEST 를 하는 쉐이더 어셈블리

(일단 위에 구조체 선언과 시그니처 선언을 넘기고 PS(main) 시작지점 부터 캡쳐 했다.)

 

- 오른쪽 asm을 보면 96줄 부터 r1.x 에다가 -1 값을 넣더니 

(l 은 무슨 뜻일까... lvalue라는 뜻인가?)

- r1.w과 r1.x을 더한 값을 r1.x 에 넣는다.

-  lt 명령어를 이용해서 r1.x 가 0.0 보다 작은지 결과를 r1.x에 저장한다.

- 그리고 r1.x와 -1를 and 비트 건 것을 다시 r1.x에 저장한다. (32비트)

- r1.x의 결과를 가지고 discard_nz를 건다.

- 아래 링크 내용을 요약하면, r1.x의 값에 따라 현재 픽셀을 버릴지 말지 결정하는 것이다.

 

(discard 에 관한 설명은 여기 있다. discard(sm4 - asm) - Win32 apps | Microsoft Learn )

 

discard(sm4 - asm) - Win32 apps

프로그램 끝에 도달하면 삭제할 픽셀 셰이더의 결과에 조건부로 플래그를 지정합니다.

learn.microsoft.com

 

 

연습 문제 5

- 이렇게 되면 더 밝아지긴 하는데, 그래도 어떤 느낌인지는 알 수 있다.

 

더보기

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