[directx11]알파 블렌딩

Date:

카테고리:

알파 블렌딩

알파 블렌딩

z버퍼는 카메라의 시점을 기준으로 깊이를 나타내는 정보를 저장하는 버퍼이다.

렌더링파이프라인을 어느정도 이해하고 있다면, 프레임워크에서는 먼저 그려진

객체의 위에 나중에 그려진 객체가 그려지기 때문에

https://i.pcmag.com/imagery/encyclopedia-terms/alpha-blending-_alphach.fit_lim.size_1050x.gif

이렇게 같은 시점에서 이미지가 그려질 때, 투명도가 없으면 바로 가려진 상태로 보여지게 된다.

그렇기 때문에 투명도인 알파 값을 사용하여 뒤에 있는 물체의 RGB 색상과 혼합하게 된다.

이 과정은 OM 단계에서 이루어진다. 거두절미하고 수식을 보여주겠다.


반투명 공식

아래 식들의 back 은 new 와 같은 뜻이고, front는 old 와 동의어로 생각하면 된다.

\[R = (R_back * (1 - α) + R_front * α)\] \[G = (G_back * (1 - α) + G_front * α)\] \[B = (B_back * (1 - α) + B_front * α)\]

아래는 매우 쉬운 방법으로, 2D 게임 등에서 매우 효과적이고 간단하게 투명도를 표현할 수 있다.
\(R = (R_back / 2) + (R_front / 2)\)

\[G = (G_back / 2) + (G_front / 2)\] \[B = (B_back / 2) + (B_front / 2)\]

만약 50퍼센트의 블렌딩이 들어간다고 하면 위와 같은 수식으로 표현될 수 있는 것이다.

DirectX11에서는 아래와 같은 함수들이 사용된다.

//알파 블렌딩
D3D11_BLEND_DESC blendDesc = {};
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);

deviceContext->OMSetBlendState(blendState, nullptr, 0xffffffff);

// 가산 블렌딩
D3D11_BLEND_DESC blendDesc = {};
blendDesc.AlphaToCoverageEnable = false;
blendDesc.IndependentBlendEnable = false;
blendDesc.RenderTarget[0].BlendEnable = true;
blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_ONE;
blendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
blendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;

ID3D11BlendState* blendState;
device->CreateBlendState(&blendDesc, &blendState);

deviceContext->OMSetBlendState(blendState, nullptr, 0xffffffff);

//곱셈 블렌딩
D3D11_BLEND_DESC blendDesc = {};
blendDesc.AlphaToCoverageEnable = false;
blendDesc.IndependentBlendEnable = false;
blendDesc.RenderTarget[0].BlendEnable = true;
blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_DEST_COLOR;
blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO;
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);

deviceContext->OMSetBlendState(blendState, nullptr, 0xffffffff);

//뺄셈 블렌딩
D3D11_BLEND_DESC blendDesc = {};
blendDesc.AlphaToCoverageEnable = false;
blendDesc.IndependentBlendEnable = false;
blendDesc.RenderTarget[0].BlendEnable = true;
blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_ONE;
blendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_REV_SUBTRACT;
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);

deviceContext->OMSetBlendState(blendState, nullptr, 0xffffffff);

위의 코드에서 blendDesc 구조체의 필드 값을 조정하면 사용자 정의 모드의 블렌딩을 구현할 수 있다.

또한, 아래의 수식으로 알파 값이 1이라면 최적화하여 알파 블렌딩을 수행하지 않도록 만들어, 오버헤드를 줄일 수 있다.

// 알파 블렌딩 설정
D3D11_BLEND_DESC blendDesc;
ZeroMemory(&blendDesc, sizeof(blendDesc));
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;

// 알파 값이 1인 경우에 대한 최적화 처리
blendDesc.RenderTarget[0].BlendEnable = FALSE;

// 블렌딩 상태를 생성
ID3D11BlendState* blendState;
device->CreateBlendState(&blendDesc, &blendState);

// 렌더링 시 블렌딩 상태를 설정
context->OMSetBlendState(blendState, nullptr, 0xFFFFFFFF);

광원효과 공식

\[R = R_back R + R_front R\] \[G = G_back G + G_front G\] \[B = B_back B + B_front B\]

대신 이 공식은 RGB의 한계가 255이기 때문에, 255를 초과하는 상황에서는 적합하지 않을 수 있다.

따라서 255가 넘게 되면 255로 값을 고정해주는 등의 처리를 하게 된다.
한계 처리는 아래와 같이 진행된다.

  • RGB 값의 한계 처리

    RGB 값의 한계가 255인 경우, 클램핑(clamping) 처리를 추가해야 한다.

예를 들어, 밝은 광원의 색상이 (255, 255, 255)이라면 이 값을 (1.0, 1.0, 1.0)로나누어

RGB 값을 1 이하로 조정할 수 있다.

클램핑은 RGB 값을 특정 범위로 제한하는 방법이다.

만약 RGB 값이 255를 초과하면, 이를 255로 제한하여 최대값으로 유지한다.

예를 들어, RGB 값이 (300, 200, 150)이라면 이를 (255, 200, 150)으로 클램핑할 수 있다.

// 아래는 클램핑 예시 코드이다
def clamp(value, min_val, max_val):
    return max(min(value, max_val), min_val)

r = 300
g = 200
b = 150

r = clamp(r, 0, 255)
g = clamp(g, 0, 255)
b = clamp(b, 0, 255)

광원효과는 DirectX11에서 아래의 코드로 구현하게 된다.

ID3D11ShaderResourceView* lightTextureView; // 광원에 사용되는 텍스처 뷰 객체

deviceContext->PSSetShaderResources(slot, 1, &lightTextureView);

위의 코드에서 slot은 텍스처 슬롯 번호를 나타내고, lightTextureView는 광원에 사용되는 텍스처 뷰 객체를 가리킨다.
이 함수를 호출하여 픽셀 쉐이더에 광원에 필요한 텍스처 리소스를 설정할 수 있다.

ID3D11Buffer* lightBuffer; // 광원에 사용되는 상수 버퍼 객체

deviceContext->PSSetConstantBuffers(slot, 1, &lightBuffer);

DirectX 카테고리 내 다른 글 보러가기

댓글 남기기