1. RenderingPipeLine - Blend
Blending 과정은 렌더링 파이프라인에서 Output Merger 단계에서 진행된다.
우리 엔진의 RenderingPipeLine 클래스에서 어떤 Blend 방식을 사용할지 세팅할 수 있다.
GameEngineRenderingPipeLine 인스턴스를 생성할 때 별도의 Setting을 하지 않으면 AlphaBlend 모드로 자동 세팅하게 만들었다.
<hide/>
GameEngineRenderingPipeLine::GameEngineRenderingPipeLine()
: InputLayOut(nullptr)
, VertexBuffer(nullptr)
, VertexShader(nullptr)
, IndexBuffer(nullptr)
, Rasterizer(nullptr)
, PixelShader(nullptr)
, DepthStencil(nullptr)
, Blend(nullptr)
, Topology(D3D11_PRIMITIVE_TOPOLOGY::D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST)
{
VertexBuffer = GameEngineVertexBuffer::Find("rect");
IndexBuffer = GameEngineIndexBuffer::Find("rect");
Rasterizer = GameEngineRasterizer::Find("EngineRasterizer");
//인스턴스가 생성될 때 Blend 모드를 AlphaBlend로 세팅
Blend = GameEngineBlend::Find("AlphaBlend");
}
//별도의 Blend 모드를 사용하고 싶을 때 사용하는 함수
void GameEngineRenderingPipeLine::SetOutputMergerBlend(const std::string& _Name)
{
Blend = GameEngineBlend::Find(_Name);
if (nullptr == Blend)
{
MsgBoxAssert("존재하지 않는 블랜더를 세팅하려고 했습니다.");
return;
}
}
Blend가 세팅됐으면 Rendering 할 때 Blend의 세팅 함수를 호출한다.
<hide/>
//매 프레임마다 호출되는 함수
void GameEngineRenderingPipeLine::Rendering()
{
InputAssembler1VertexBufferSetting();
VertexShaderSetting();
InputAssembler2IndexBufferSetting();
RasterizerSetting();
PixelShaderSetting();
OutputMergerBlendSetting();
OutputMergerDepthStencilSetting();
Draw();
}
void GameEngineRenderingPipeLine::OutputMergerBlendSetting()
{
Blend->Setting();
}
2. GameEngineBlend
실제 DirectX의 Blending관련 데이터를 보관하고 사용자 인터페이스 함수를 제공하는 클래스.
별도의 Blend 모드를 추가하고 싶으면 이 클래스의 인스턴스를 추가해야 한다.
<hide/>
#pragma once
#include "GameEngineRes.h"
// 설명 : 최종 픽셀쉐이더의 출력결과를 랜더타겟에 출력할때(Outputmerger) 색깔 통합에 관련
class GameEngineBlend : public GameEngineRes<GameEngineBlend>
{
public:
static GameEngineBlend* Create(const std::string& _Name, const D3D11_BLEND_DESC& _Desc);
public:
// constrcuter destructer
GameEngineBlend();
~GameEngineBlend();
// delete Function
GameEngineBlend(const GameEngineBlend& _Other) = delete;
GameEngineBlend(GameEngineBlend&& _Other) noexcept = delete;
GameEngineBlend& operator=(const GameEngineBlend& _Other) = delete;
GameEngineBlend& operator=(GameEngineBlend&& _Other) noexcept = delete;
void Setting();
protected:
private:
//Blend 관련 인터페이스 객체
ID3D11BlendState* State;
// Blend 방식에 대한 정보
D3D11_BLEND_DESC Desc;
float4 Factor;
unsigned int Mask;
void Create(const D3D11_BLEND_DESC& _Info);
};
Device를 통해 Blend 인터페이스 객체를 생성하고 Context로 Blend를 세팅해준다.
<hide/>
void GameEngineBlend::Create(const D3D11_BLEND_DESC& _Desc)
{
Desc = _Desc;
if (S_OK != GameEngineDevice::GetDevice()->CreateBlendState(&Desc, &State))
{
MsgBoxAssert("Blend State 생성에 실패했습니다.");
}
}
void GameEngineBlend::Setting()
{
if (nullptr == State)
{
MsgBoxAssert("블랜드가 nullptr 입니다. 세팅할수 없습니다.");
return;
}
GameEngineDevice::GetContext()->OMSetBlendState(State, Factor.Arr1D, Mask);
}
3. Blend_Desc
기본적인 AlphaBlend의 Desc는 GameEngineCore_Resource의 EngineSubSetting함수에서 생성된다.
<hide/>
void EngineSubSetting()
{
{
D3D11_BLEND_DESC Desc = { 0 };
Desc.AlphaToCoverageEnable = FALSE;
Desc.IndependentBlendEnable = FALSE;
Desc.RenderTarget[0].BlendEnable = true;
Desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
//Blend관련 연산
Desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
Desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
Desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
//Blend 관련 연산 - 알파쪽만 따로 처리하는 옵션
Desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
Desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
Desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
//값을 넣어준 Desc로 AlphaBlend를 세팅해준다.
GameEngineBlend::Create("AlphaBlend", Desc);
}
}
중요한 것은 Blend 관련 연산이다.
3-1. Blend 연산 (AlphaBlend)
알파 값이 0.4인 빨간색 사각형 (Src - 1번)을 파란색 배경의 백버퍼(Dest - 2번)에 렌더링 한다고 생각해보자.
우리는 3번의 결과를 원한다. 그럼 1번 Src와 2번 Dest가 어떤 연산을 거쳐서 3번의 결과가 나오는지 알아보자.
Src와 Dest의 색깔 값을 RGBA 방식으로 표현하면 다음과 같다.
Src : float4(1.0f,0.0,0.0,0.4f)
Dest : float4(0.0f,0.0f,1.0f,1.0f)
Blend 연산은 다음과 같다.
Src(RGBA) * 소스 팩터 + Dest(RGBA) * 원본 팩터 = 화면에 나오는 색깔
Src(RGBA)와 Dest(RGBA)는 Src와 Dest의 RGBA값이 들어간다. 이때 중요한 것은 A값은 1.0f로 고정된다.
즉 Src는 Src(1.0f, 0.0f, 0.0f, 1.0f), Dest는 Dest(0.0f, 0.0f, 1.0f, 1.0f)가 된다.
그 이유는 우리가 다음과 같은 코드를 쳤기 때문이다.
//RGBA의 A값을 1.0f로 바꿔준다.
Desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
Desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
Alpha값을 1로 바꾸는 이유에 대해서는 뒤에서 설명하겠다.
소스 팩터와 원본 팩터는 float4 형식으로 Alpha값이 저장된다.
ex) Src의 알파 값이 0.4인 경우 소스팩터 : float4(0.4f, 0.4f, 0.4f, 0.4f), Dest의 알파값이 0.9인 경우 원본 팩터 float(0.9f, 0.9f, 0.9f, 0.9f)
소스 팩터와 원본 팩터의 값을 세팅해줬다.
//Src의 Alpha값을 소스팩터에 세팅
Desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
//1-Src의 Alpha값을 원본팩터에 세팅
Desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
연산과정은 다음과 같다.
(1.0f, 0.0f, 0.0f, 1.0f) * (0.4f, 0.4f, 0.4f, 0.4f) + (0.0f, 0.0f, 1.0f, 1.0f) * (0.6f, 0.6f, 0.6f, 0.6f) = (0.4f , 0.0f, 0.6f, 1.0f)
Src와 Dest의 Alpha값이 1인 이유는 소스 팩터, 원본 팩터에서 알파 값을 가지고 있으니 알파값 보존을 위해서다.
'DirectX > 공부 내용' 카테고리의 다른 글
DirectX::FrameAnimation (0) | 2022.07.15 |
---|---|
DirectX:: Atlas Texture 처리 (0) | 2022.07.14 |
DirectX::GameEngineTexture & GameEngineSampler (0) | 2022.07.12 |
C++ Functional (0) | 2022.07.12 |
렌더링 파이프 라인 : ConstantBuffer (0) | 2022.07.11 |