개발하는 리프터 꽃게맨입니다.

[물방울책 공부록] Chapter 9 Blending 본문

컴퓨터 그래픽스/DirectX 11

[물방울책 공부록] Chapter 9 Blending

파워꽃게맨 2024. 10. 5. 20:58

 

프레임을 렌더링할 때, 먼저 지형을 그리고 그 다음으로 나무 상자를 그리며, 지형과 나무 상자의 픽셀들이 백 버퍼에 저장된다.

 

그 후 Blending을 사용하여 물 표면을 back buffer에 그리면, 물의 픽셀들이 지형과 나무 상자에 적절하게 혼합되어 지형과 나무 상자가 물을 통해 보이게 된다.

 

이 장에서는 현재 래스터화 중인 픽셀을 이전 back buffer에 이미 래스터화된 픽셀과 혼합할 수 있게 해주는 기술을 살펴본다.

 

이 기술은 물과 유리 같은 반투명 객체를 렌더링할 수 있게 해준다.

 

논의를 위해 우리는 back buffer를 렌더 타겟을 언급한다.

그러나 나중에 off screen 렌더 타겟에도 렌더링할 수 있다는 것을 보여줄 것이다.

 

블렌딩은 이러한 렌더 타겟에도 동일하게 적용되며, 목적지 픽셀은 이미 래스터화된 펙셀 값들이다.

 

블렌딩 방정식

Csrc 는 현재 우리가 래스터화 중인 ij번째 픽셀의 픽셀 쉐이더로부터 출력된 색상이라고 하고

Cdst는 현재 back buffer에 있는 ij번째 픽셀의 색상이라고 하겠다.

 

블렌딩이 없다면 Csrc는 Cdst를 덮어쓰고 ij back buffer 픽셀의 새로운 색상이 될 것이다.

 

그러나 블렌딩이 있으면 Csrc와 Cdst가 함께 혼합되어 Cdst를 덮어쓸 새로운 색상 C가 생성된다.

 

Driect3D는 소스 및 목적지 픽셀 색상을 혼합하기 위해 다음 블렌딩 방정식을 사용한다.

 

여기서 Fsrc와 Fdst는 혼합인자라고 불리는 것이다.

혼합 인자를 통해서 다양한 효과를 낼 수 있다.

 

(x) 연산자는 앞서 말한 색상 벡터의 곱셈을 의미하며

[+] 연산자는 사용자 정의 연산자이다.

[+] 연산자는 이진 연산라고 불리는 임의로 정의할 수 있는 연산자이다. 단순하게 R, G, B 값을 더하는 것도 일반적으로 사용하는 연산 중 하나이다.

 

위 블렌딩 방적식은 RGB 구성 요소에만 적용되고, 알파 구성 요소는 별도의 유사한 방정식으로 처리된다.

방정식은 본질적으로 동일하지만, 혼합 인자와 이진 연산이 다를 수 있다.

 

RGB와 알파를 분리하는 이유는 그들을 독립적으로 처리할 수 있기 위함이다.

그러므로 RGB와 알파는 서로 다르게 처리할 수 있는데, 이런 구조를 띄는 이유는 알파 구성 요소를 블렌딩하는 경우는 RGB 구성 요소를 블렌딩하는 경우보다 훨씬 드물기 때문이다.

 

이는 주로 백 버퍼의 알파 값에 대해 신경쓰지 않기 때문이고, 백 버퍼의 알파 값은 목적지 알파 값을 요구하는 알고리즘이 있을 때만 중요하다.

 

Blend Operations

블렌딩 방정식에서 사용되는 이진 [+] 연산자는 다음 중 하나일 수 있다.

typedef enum D3D11_BLEND_OP
{
    D3D11_BLEND_OP_ADD = 1,        // Add operation
    D3D11_BLEND_OP_SUBTRACT = 2,   // Subtract operation
    D3D11_BLEND_OP_REV_SUBTRACT = 3, // Reverse subtract operation
    D3D11_BLEND_OP_MIN = 4,        // Minimum operation
    D3D11_BLEND_OP_MAX = 5,        // Maximum operation
} D3D11_BLEND_OP;

 

min/max 연산에서는 블렌드 인자가 무시된다.

이 동일한 연산자들은 알파 블렌딩 방정식에도 적용된다.

 

또한, RGB와 알파에 대해 서로 다른 연산자를 지정할 수 있다.

 

예를 들어, 두 RGB 향을 더하면서, 두 알파 항은 뺄 수 있다.

 

Blend Factors

소스와 목적지 블렌드 팩터의 조합과 다양한 블렌드 연산자를 설정함으로써 수십 가지의 다른 블렌딩 효과를 얻을 수 있다. 후에 몇 가지 조합을 설명할 예정이지만, 직접 실험해보며 어떤 효과가 있는지 파악하는 것이 좋다.

 

자세한 것은 msdn을 참고하면 좋다.

D3D11_BLEND_ZERO
값: 1
혼합 계수는 (0, 0, 0, 0)입니다. 사전 혼합 작업이 없습니다.
D3D11_BLEND_ONE
값: 2
혼합 계수는 (1, 1, 1, 1)입니다. 사전 혼합 작업이 없습니다.
D3D11_BLEND_SRC_COLOR
값: 3
혼합 요소는 픽셀 셰이더의 색 데이터(RGB)인 (Rs, Gs, Bs, As)입니다. 사전 혼합 작업이 없습니다.
D3D11_BLEND_INV_SRC_COLOR
값: 4
혼합 계수는 픽셀 셰이더의 색 데이터(RGB)인 (1 - Rs, 1 - Gs, 1 - Bs, 1 - As)입니다. 사전 혼합 작업은 데이터를 반전하여 1 - RGB를 생성합니다.
D3D11_BLEND_SRC_ALPHA
값: 5
혼합 요소는 픽셀 셰이더의 알파 데이터(A)인 (As, As)입니다. 사전 혼합 작업이 없습니다.
D3D11_BLEND_INV_SRC_ALPHA
값: 6
혼합 요소는 픽셀 셰이더의 알파 데이터(A)인 ( 1 - As, 1 - As, 1 - As, 1 - As)입니다. 미리 혼합 작업은 데이터를 반전하여 1 - A를 생성합니다.
D3D11_BLEND_DEST_ALPHA
값: 7
혼합 요소는 렌더링 대상의 알파 데이터인 (Ad A dA d d)입니다. 사전 혼합 작업이 없습니다.
D3D11_BLEND_INV_DEST_ALPHA
값: 8
혼합 요소는 렌더링 대상의 알파 데이터인 (1 - Ad 1 - Ad 1 - Ad 1 - Ad)입니다. 미리 혼합 작업은 데이터를 반전하여 1 - A를 생성합니다.
D3D11_BLEND_DEST_COLOR
값: 9
혼합 요소는 렌더링 대상의 색 데이터인 (Rd, Gd, Bd, Ad)입니다. 사전 혼합 작업이 없습니다.
D3D11_BLEND_INV_DEST_COLOR
값: 10
혼합 요소는 렌더링 대상의 색 데이터인 (1 - Rd, 1 - Gd, 1 - Bd, 1 - Ad)입니다. 사전 혼합 작업은 데이터를 반전하여 1 - RGB를 생성합니다.
D3D11_BLEND_SRC_ALPHA_SAT
값: 11
혼합 계수는 (f, f, f, 1); where f = min(As, 1
- ad). 사전 혼합 연산은 데이터를 1 이하로 고정합니다.
D3D11_BLEND_BLEND_FACTOR
값: 14
혼합 계수는 ID3D11DeviceContext::OMSetBlendState로 설정된 혼합 요소입니다. 사전 혼합 작업이 없습니다.
D3D11_BLEND_INV_BLEND_FACTOR
값: 15
혼합 계수는 ID3D11DeviceContext::OMSetBlendState로 설정된 혼합 요소입니다. 혼합 전 연산은 혼합 인자를 반전하여 1 - blend_factor 생성합니다.
D3D11_BLEND_SRC1_COLOR
값: 16
혼합 요소는 픽셀 셰이더의 색상 데이터 출력인 데이터 원본입니다. 혼합 전 작업은 없습니다. 이 혼합 요소는 이중 소스 색 혼합을 지원합니다.
D3D11_BLEND_INV_SRC1_COLOR
값: 17
혼합 요소는 픽셀 셰이더의 색상 데이터 출력인 데이터 원본입니다. 사전 혼합 작업은 데이터를 반전하여 1 - RGB를 생성합니다. 이 혼합 요소는 이중 소스 색 혼합을 지원합니다.
D3D11_BLEND_SRC1_ALPHA
값: 18
혼합 요소는 픽셀 셰이더의 알파 데이터 출력인 데이터 원본입니다. 혼합 전 작업은 없습니다. 이 혼합 요소는 이중 소스 색 혼합을 지원합니다.
D3D11_BLEND_INV_SRC1_ALPHA
값: 19
혼합 요소는 픽셀 셰이더의 알파 데이터 출력인 데이터 원본입니다. 미리 혼합 작업은 데이터를 반전하여 1 - A를 생성합니다. 이 혼합 요소는 이중 소스 색 혼합을 지원합니다.

 

주의할 점은 알파 블렌딩 방정식에서는 _COLOR 로 끝나는 블렌드 팩터는 허용되지 않는 다는 것이다.

 

Blend State

이제 블렌드 상태를 관리하는 BlendState 인터페이스에 대해서 알아보자.

여타 다른 자원과 유사하게 생성할 수 있다.

 

HRESULT ID3D11Device::CreateBlendState(
  const D3D11_BLEND_DESC *pBlendStateDesc,
  ID3D11BlendState **ppBlendState
);

 

가장 핵심은 D3D11_BLEND_DESC 이다.

 

typedef struct D3D11_BLEND_DESC {
  BOOL AlphaToCoverageEnable; // 기본값: False
  BOOL IndependentBlendEnable; // 기본값: False
  D3D11_RENDER_TARGET_BLEND_DESC RenderTarget[8];
} D3D11_BLEND_DESC;

 

  • AlphaToCoverageEnable
    멀티 샘플링과 관련된 옵션이다. 멀티 샘플링을 지원하지 않는 프로그램의 경우 해당 옵션을 사용하지 못한다.

  • IndependentBlendEnable
    Dx11 은 최대 8개의 렌더 타겟을 동시에 지원하는데, 이 플래그를 true로 설정하면 각 렌더 타겟에 대해 다른 블렌딩을 수행할 수 있으며, false로 설정하면 모든 렌더 타겟은 동일한 블렌딩 방식을 사용한다.

  • RenderTarget
    각 렌더 타겟에 대한 블렌딩 방식에 대해 설명하는 D3D11_RENDER_TARGET_BLEND_DESC 배열이다.

 

D3D11_RENDER_TARGET_BLEND_DESC 는 다음과 같다.

typedef struct D3D11_RENDER_TARGET_BLEND_DESC {
  BOOL BlendEnable; // 기본값: False
  D3D11_BLEND SrcBlend; // 기본값: D3D11_BLEND_ONE
  D3D11_BLEND DestBlend; // 기본값: D3D11_BLEND_ZERO
  D3D11_BLEND_OP BlendOp; // 기본값: D3D11_BLEND_OP_ADD
  D3D11_BLEND SrcBlendAlpha; // 기본값: D3D11_BLEND_ONE
  D3D11_BLEND DestBlendAlpha; // 기본값: D3D11_BLEND_ZERO
  D3D11_BLEND_OP BlendOpAlpha; // 기본값: D3D11_BLEND_OP_ADD
  UINT8 RenderTargetWriteMask; // 기본값: D3D11_COLOR_WRITE_ENABLE_ALL
} D3D11_RENDER_TARGET_BLEND_DESC;

 

 

 

  • BlendEnable: 블렌딩을 활성화하려면 true로 설정해야함
  • SrcBlend: RGB 블렌딩에 대한 소스 블렌드 팩터를 지정.
  • DestBlend: RGB 블렌딩에 대한 목적지 블렌드 팩터를 지정.
  • BlendOp: RGB 블렌딩 연산자를 지정.
  • SrcBlendAlpha: 알파 블렌딩에 대한 소스 블렌드 팩터를 지정.
  • DestBlendAlpha: 알파 블렌딩에 대한 목적지 블렌드 팩터를 지정.
  • BlendOpAlpha: 알파 블렌딩 연산자를 지정.
  • RenderTargetWriteMask: 백 버퍼에서 쓰기가 허용되는 색상 채널을 제어.

RenderTargetWriteMask는 플래그들의 조합이다.

typedef enum D3D11_COLOR_WRITE_ENABLE {
  D3D11_COLOR_WRITE_ENABLE_RED = 1,
  D3D11_COLOR_WRITE_ENABLE_GREEN = 2,
  D3D11_COLOR_WRITE_ENABLE_BLUE = 4,
  D3D11_COLOR_WRITE_ENABLE_ALPHA = 8,
  D3D11_COLOR_WRITE_ENABLE_ALL = (D3D11_COLOR_WRITE_ENABLE_RED | D3D11_COLOR_WRITE_ENABLE_GREEN | D3D11_COLOR_WRITE_ENABLE_BLUE | D3D11_COLOR_WRITE_ENABLE_ALPHA)
} D3D11_COLOR_WRITE_ENABLE;

 

 

예제

많이 사용되는 블렌딩 방식에 대해서 설명한다.이 예제들은 rgb 블렌딩만 다루며, 알파 블렌딩 또한 유사한 방식으로 처리된다.

 

1. No Color Write

만약 현재 래스터라이징 중인 소스 픽셀과 원래의 목적지 픽셀을 혼합하거나 덮어쓰지 않고, 기존 목적지 픽셀을 그대로 유지하고 싶을 때 사용할 수 있는 기법이다.

예를 들어, 깊이/스텐실 버퍼에는 쓰되, 백 버퍼에는 쓰지 않으려는 경우 유용하다.

이를 위해 소스 픽셀 블렌드 팩터를 D3D11_BLEND_ZERO로 설정하고, 목적지 블렌드 팩터를 D3D11_BLEND_ONE 으로 설정하며, 블렌드 연산을 D3D11_BLEND_OP_ADD로 설정한다.

 

그러면 블렌딩 방정식은 다음과 같다.

즉, 이 설정은 새로운 소스 픽셀이 전혀 그려지지 않는다. 그러므로 원래 목적지 픽셀 값을 그대로 유지한다.

 

이 방식 대신, D3D11_RENDER_TARGET_BLEND_DESC::RenderTargetWriteMask 멤버를 0으로 설정하여 모든 색상 채널에 쓰기를 비활성화할 수도 있다.

 

2. 더하기/빼기

소스 픽셀과 목적지 픽셀을 더하려고 한다고 가정해본다.

이를 위해 소스 블렌드 팩터를 D3D11_BLEND_ONE, 목적지 블렌드 팩터를 D3D11_BLEND_ONE으로 설정하고

블렌드 연산은 D3D11_BLEND_OP_ADD로 설정한다.

 

이 설정은 위 그림처럼 두 픽셀을 적절하게 더하는 역할을 한다.

 

만약 빼기 연산자를 사용한다면, 9.3 그림처럼 된다.

 

이와 같이 블렌드 연산을 통해 다양한 시각 효과를 만들어낼 수 있으며, 더하기, 뺴기, 곱하기와 같은 연산을 사용해 밝기나 색상의 변화를 적용할 수 있다.

 

3. 곱하기

소스 픽셀을 해당 목적지 픽셀과 곱하고 싶다고 가정한다.

이를 위해 소스 블렌드 팩터를 D3D11_BLEND_ZERO, 목적지 블렌드 팩터를 D3D11_BLEND_SRC_COLOR 로 설정하고, 블렌드 연산을 D3D11_BLEND_OP_ADD로 설정한다.

 

이 설정을 통해 블렌딩 방정식은 다음과 같이 단순화된다.

즉, 소스 픽셀과 목적지 픽셀이 곱해져서 새로운 픽셀이 생성되며, 그 값이 목적지 픽셀에 쓰여진다. 곱하기 연산은 이미지의 색상을 혼합하여 다양한 효과를 낼 수 있으며, 어두운 영역을 더 어둡게 하고 밝은 영역을 유지하는 등의 결과를 얻을 수 있다.

 

4. 투명도

소스 알파 성분인 as를 소스 픽셀의 불투명도를 제어하는 비율로 생각할 수 있다.

예를 들어, 알파 값이 0이면 0% 불투명, 0.4이면 40% 불투명, 1.0이면 100% 불투명하다.

 

불투명도와 투명도의 관계는 단순히 T =  1 - A 로 표현되며, 여기서 A는 불투명도, T는 투명도를 의미한다.

예를 들어 어떤 물체가 0.4 불투명하면, 그것은 1 - 0.4 = 0.6 투명한 상태이다. 

 

이제 소스 픽셀의 불투명도를 기준으로 소수와 목적지 픽셀을 블렌딩하고 싶다고 가정해본다.

 

이 경우, 소스 블렌드 팩터는 D3D11_BLEND_SRC_ALPHA로, 목적지 블렌드 팩터는 D3D11_BLEND_INV_SRC_ALPHA로, 그리고 블렌드 연산자는 D3D11_BLEND_OP_ADD로 설정한다.

 

이 설정을 통해 블렌딩 방정식은 다음과 같이 단순화된다.

예를 들어 소스 픽셀의 알파 값이 0.25 라면

 

소스의 색상 25% 와 데스트의 색상 75%를 더해서 새로운 색상을 만들어 낸다.

 

해당 방식을 이용해서 위 그림과 같이 투명한 객체를 그릴 수 있다.

이 블렌딩 방법을 사용할 때 객체를 그리는 순서가 중요하다는 점에 주의해야 한다.

 

왜냐하면, 위 방정식에서 봤듯 도착지의 픽셀 정보가 혼합에 중요하기 때문이다.

 

우리는 다음 규칙을 따른다.

 

먼저 블렌딩을 사용하지 않는 객체들을 먼저 그린 후, 블렌딩을 사용하는 객체들을 카메라와의 거리 순서대로 정렬한뒤, 마지막으로 블렌딩을 사용하는 객체들을 뒤에서 앞으로 그린다.

 

투명 객체 뒤에 있는 모든 픽셀이 백 버퍼에 이미 기록되어 있어야, 투명한 소스 픽셀을 뒤에 있는 장면의 픽셀들과 블렌딩할 수 잇다.

 

앞서 말한.. No Color Write 방식같은 블렌딩 방법은 소스 픽셀을 백 버퍼에 쓰는 것을 방지하므로 그리기 순서는 중요하지 않다.

 

그러나 블렌딩을 사용하는 객체들은 굳이 정렬할 필요가 없다. 그 이유는 연산이 교환법칙을 따르기 때문이다. 즉, 백 버퍼 픽셀 색상 B에서 시작해여 해당 픽셀에 n번의 더하기/빼기/곱하기 블렌딩을 수행할 경우, 그 순서는 중요하지 않다.

D3D11_BLEND_DESC blendDesc = {0};
blendDesc.AlphaToCoverageEnable = false;
blendDesc.IndependentBlendEnable = false;
blendDesc.RenderTarget[0].BlendEnable = true;
blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
blendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
blendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
ID3D11BlendState* blendState;
device->CreateBlendState(&blendDesc, &blendState);

 

투명도를 설정하는 blendState는 위와 같다.

 

Blending and the Depth Buffer

덧셈/뺄셈/곱셈 블렌딩을 사용할 때 깊이 테스트와 관련된 문제가 발생한다.

예시로 덧셈 블렌딩을 설명하겠지만, 뺄셈/곱셈 블렌딩에도 같은 개념이 적용된다.

 

덧셈 블렌딩을 사용하는 객체 집합 S를 렌더링할 때, 집합 S에 속한 객체들은 서로를 가리지 않으며, 그들의 색상은 단순히 누적되도록 설계된다.

 

따라서 집합 S의 객체들 간에는 깊이 테스트를 수행하지 않아야 한다. 그렇지 않으면 뒤에서 앞으로 그리는 순서가 아닌 상태에서 집합 S의 하나의 객체가 다른 객체를 가려 픽셀 프로그래먼트가 깊이 테스트로 인해 거부되며, 그 결과 해당 객체의 픽셀 색상이 블렌딩 합계에 포함되지 않게 된다.

 

집합 S의 객체를 렌더링할 때 깊이 버퍼에 쓰기를 비활성화함으로써 객체 간의 깊이 테스트를 비활성화할 수 있다.

 

깊이 쓰기가 비활성화되어 있으면, 덧셈 블렌딩으로 그려진 집합 S의 객체 깊이는 깊이 버퍼에 기록되지 않는다. 따라서 이 객체는 뒤에 그려진 집합 S의 다른 객체를 깊이 테스트로 가리지 않게 된다.

 

덧셈 블렌딩으로 그려진 객체 집합 S를 그릴 때에만 깊이 쓰기를 비활성화해야 한다.

 

깊이 읽기와 깊이 테스트는 여전히 활성화된다.

이는 블렌딩되지 않은 기하 도형이 뒤에 있는 블렌딩된 기하 도형을 여전히 가릴 수 있도록 하기 위함이다.

 

예를 들어, 벽 뒤에 덧셈 블렌딩된 객체들이 있을 경우, 견고한 벽이 그 객체들을 가리므로 블렌딩된 객체들은 보이지 않게 된다.

 

깊이 쓰기를 비활성화하고, 보다 일반적으로 깊이 테스트를 설정을 구성하는 방법은 다음 장에 다루도록 한다.

 

알파 채널

소스 픽셀의 알파 요소는 RGB 블렌딩에서 투명도를 제어하는 데 사용될 수 있음을 보여주었다.

블렌딩 방정식에서 사용되는 소스 색상은 픽셀 쉐이더에서 나온다.

 

이전 장에서 본 것처럼, 우리는 Diffuse 머테리얼의 알파 값을 픽셀 쉐이더의 알파 출력으로 반환한다.

따라서, Diffuse 머테리얼의 W값은 투명도를 제어하는 데 사용된다.

이런 것을 알파 채널이라고 한다.

 

일반적으로 포토샵과 같은 인기 있는 이미지 편집 소프트웨어에서 알파 채널을 추가하고, 알파 채널을 지원하는 형식으로 이미지를 저장할 수 있다.

 

그러나 여기서는 이전 장에서 다룬 DXTex 유틸리티 프로그램을 사용하여 알파 채널을 삽입하는 대체 방법을 보여준다.

 

// 소프트웨어 툴을 다루는 내용이므로 스킵..

 

픽셀 클리핑

때때로 우리는 소스 픽셀이 더 이상 처리되지 않도록 완전히 거부하고 싶을 떄가 있다.

이것은 HLSL의 내장 함수 clip(x)을 사용하여 처리할 수 있다.

 

이 함수는 픽셀 쉐이더에서만 호출할 수 있으며, x < 0 일 경우 현재 픽셀을 더 이상 처리하지 않고 버린다.

 

이 함수는 예를 들어 위와같은 철망 텍스처를 렌더링할 때 유용하다.

즉, 픽셀이 완전히 불투명하거나 완전히 투명한 경우의 렌더링에 유용하다.

 

픽셀 쉐이더에서 우리는 텍스처의 알파 값을 가져온다.

알파 값이 0에 가까운 작은 값이라면 픽셀이 완전히 투명하다는 것을 의미하므로, 우리는 해당 픽셀을 더 이상 처리하지 않도록 클리핑한다.

 

 

만약, 이 옵션을 on/off 형식으로 바꿔주고 싶다면

if(gAlphaClip) 라는 조건문을 두어 해당 매개변수가 true일 때만 클립을 수행하도록 할 수 있을 것이다.

이는 일부 기하에서는 클리핑을 하지 않도록 하는 기능을 한다.

 

블렌딩을 사용하여 같은 결과를 얻을 수 있지만, 이 방법이 더 효율적이다.

(예를 들어 알파 값이 0.1 미만이면 알파를 0으로 설정한다던가..)

 

첫째로, 블렌딩 계산을 수행할 필요가 없으며, 그리기 순서또한 중요하지 않다.

더 나아가, 픽셀 쉐이더에서 픽셀을 일찍 버림으로써 나머지 픽셀 쉐이더 명령을 건너뛸 수 있다.

(버려진 픽셀에 대한 계산을 수행할 필요가 없다.)

 

필터링으로 인해 알파 채널이 약간 흐려질 수 있으므로, 픽셀을 클립할 때 약간의 여유를 남겨 두는 것이 좋다.

예를 들어, 알파 값이 0에 가까운 픽셀을 클립하지만, 반드시 정확히 0일 필요는 없다.

 

또 한 가지 언급할 점은, 철망 텍스처가 있는 박스를 통해 후면도 볼 수 있어야 하므로, back face culling 도 비활성화할 수 있어야 한다.

 

안개 Fog

게임에서 특정한 날씨 조건을 시뮬레이션하려면 안개 효과를 구현할 필요가 있다.

 

안개의 명백한 목적 외에도, 안개는 몇 가지 부가적인 이점을 제공한다. 예를 들어, 안개는 멀리 있는 렌더링 아티팩트를 감추고 팝핑 현상을 방지할 수 있다.

 

팝핑은 원래 멀리 있는 평면 뒤에 있던 오브젝트가 카메라의 움직임으로 인해 갑자기 프러스텀 앞에 나타나면서 보이는 현상을 의미한다. 그래서 장면에 갑자기 튀어나오는 것처럼 보인다.

 

멀리 안개 층을 두면 이런 팝핑을 감출 수 있다.

 

장면이 맑은 날에 발생한다고 해도 멀리에는 약간의 안개를 포함시키는 것이 좋다. 왜냐하면 맑은 날에도 산과 같은 먼 무레는 깊이의 함수로 인해 더 흐릿하게 보이고 대비가 감소하기 떄문이다. 우리는 안개를 사용해 이러한 대기 원근감을 시뮬레이션할 수 있다.

 

우리의 안개 구현 전략은 다음과 같다.

우리는 안개의 색상, 카메라에서 시작하는 안개의 거리(fog start), 그리고 안개의 범위(fog range)를 지정한다.

즉, 안개가 시작되는 거리부터 안개가 모든 물체를 완전히 숨길 때까지의 범위이다. 그런 다음 삼각형의 한 점에서의 색상은 일반적인 색상과 안개 색상의 가중 평균으로 계산된다.

 

 

매개변수 s는  0~1 사이의 값이며, 카메라 위치와 표면 점 사이의 거리 함수이다.

표면 점과 눈 사이의 거리가 멀어질수록 그 점은 점점 더 안개에 의해 가려지게 된다.

 

매개변수 s는 다음과 같이 정의된다.

fogStart와 fogRange 파라미터

여기서 dist(p, E)는 표면 점 p와 카메라 위치 E 사이의 거리이다.

 

saturate함수는 인수를 [0, 1] 범위로 고정하는 역할을 수행한다.

 

 

 

위 함수를 시각적으로 표현하면 위 그림과 같다.

 

 

만약, 픽셀과 카메라와의 거리가 fogStart보다 짧은 경우 색상은 그대로 그려지게 된다.

픽셀과 카메라의 거리가 fogStart 이상이 되는 시점부터 픽셀은 안개에 의해 가려지기 시작한다.

 

fogEnd는 fogStart + fogRange 로 정의하는데,

픽셀과 카메라의 거리가 fogEnd보다 커질경우 안개는 표면 점을 완전히 가리고, 그 결과로 보이는 것은 안개 색상뿐이다.

 

fogStart < dist(p, E) < fogEnd 일 때, dist(p, E)가 fogStart에서 fogEnd로 증가함에 따라 s는 0에서 1로 선형적으로 증가한다.

 

이는 거리가 멀어질수록 안개 색상이 더 많은 가중치 s를 얻고, 원래 색상은 점점 덜 반영된다는 것을 의미한다.

이는 당연히 이해가 되는 부분이다. 거리가 멀어질수록 안개가 표면 점을 점점 더 많이 가리기 때문이다.

 

이제 쉐이더 코드에 안개와 관련된 부분을 추가하겠다.

 

 

새로운 컨스턴트 버퍼를 만들었다.

안개와 관련된 사항은 거의 바뀌지 않기 때문이다.

 

 

안개는 위와 같이 계산한다.

 

다음 포스팅에는 동영상이 올라갈 예정입니닷