프로젝트 : DirectX를 활용한 그래픽스 (21 - 포스트 프로세스 (2))
지난 번에 일단 블러까지는 적용이 잘 되는 것을 확인하였다.
이제, 본격적으로 블룸을 적용해보자.
먼저, 여러개의 렌더타겟을 사용해야하기 때문에 외부에서도 렌더타겟을 만들어서 사용할 수 있도록 함수를 추가해주었다.
RenderTarget EngineBase::CreateRenderTarget(UINT _Width, UINT _Height)
{
RenderTarget NewRenderTarget;
Microsoft::WRL::ComPtr<ID3D11Texture2D> Texture;
D3D11_TEXTURE2D_DESC txtDesc;
ZeroMemory(&txtDesc, sizeof(txtDesc));
txtDesc.Width = _Width;
txtDesc.Height = _Height;
txtDesc.MipLevels = txtDesc.ArraySize = 1;
txtDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
txtDesc.SampleDesc.Count = 1;
txtDesc.Usage = D3D11_USAGE_DEFAULT;
txtDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET | D3D11_BIND_UNORDERED_ACCESS;
txtDesc.MiscFlags = 0;
txtDesc.CPUAccessFlags = 0;
D3D11_RENDER_TARGET_VIEW_DESC viewDesc;
viewDesc.Format = txtDesc.Format;
viewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
viewDesc.Texture2D.MipSlice = 0;
HRESULT Result;
Result = Device->CreateTexture2D(&txtDesc, NULL, Texture.GetAddressOf());
if (Result != S_OK)
{
std::cout << "CreateTexture2D() failed" << std::endl;
}
Result = Device->CreateRenderTargetView(Texture.Get(), &viewDesc, NewRenderTarget.RTV.GetAddressOf());
if (Result != S_OK)
{
std::cout << "CreateRenderTargetView() failed" << std::endl;
}
Result = Device->CreateShaderResourceView(Texture.Get(), nullptr, NewRenderTarget.SRV.GetAddressOf());
if (Result != S_OK)
{
std::cout << "CreateShaderResourceView() failed" << std::endl;
}
return NewRenderTarget;
}
해당 함수를 사용하면, 렌더타겟의 SRV와 RTV를 반환해주기 때문에 이를 활용해서 렌더링할 수 있게 된다.
DetectTarget = EngineBase::GetInstance().CreateRenderTarget(WindowSize.first, WindowSize.second);
BlurTarget = EngineBase::GetInstance().CreateRenderTarget(BlurData.Width, BlurData.Height);
먼저 렌더타겟을 생성해주었다.
float ClearColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
//추출
SetTexture(DoubleBufferSRV);
EngineBase::GetInstance().GetContext()->ClearRenderTargetView(DetectTarget.RTV.Get(), ClearColor);
EngineBase::GetInstance().GetContext()->OMSetRenderTargets(1, DetectTarget.RTV.GetAddressOf(), DepthStencilView.Get());
PostProcessRenderer->SetPSShader(L"BrightDetectPixelShader.hlsl");
PostProcessRenderer->Render(_DeltaTime);
그리고 PostProcess클래스의 Render 부분에서 이렇게 DetectTarget 렌더타겟에 밝은 부분을 추출하도록 하였다.
#include "LightHeader.hlsli"
struct PixelShaderInput
{
float4 pos : SV_POSITION;
float2 TexCoord : TEXCOORD;
};
Texture2D DiffuseTexture : register(t0);
SamplerState Sampler : register(s0);
float4 main(PixelShaderInput _Input) : SV_TARGET
{
float4 Color = DiffuseTexture.Sample(Sampler, _Input.TexCoord);
float Luminance = 0.2126f * Color.r + 0.7152f * Color.g + 0.0722f * Color.b;
if (Luminance > 0.8f)
{
return Color;
}
else
{
return (float4) 0.0f;
}
}
추출하는 쉐이더 코드는 위와 같다. 픽셀의 휘도를 계산하여 일정 수치 이상이라면 해당 색상을 렌더타겟에 렌더링하도록 하였다.
//블러
SetTexture(DetectTarget.SRV);
EngineBase::GetInstance().GetContext()->RSSetViewports(1, &DownViewPort);
EngineBase::GetInstance().GetContext()->ClearRenderTargetView(BlurTarget.RTV.Get(), ClearColor);
EngineBase::GetInstance().GetContext()->OMSetRenderTargets(1, BlurTarget.RTV.GetAddressOf(), DepthStencilView.Get());
PostProcessRenderer->SetPSShader(L"BlurPixelShader.hlsl");
PostProcessRenderer->Render(_DeltaTime);
EngineBase::GetInstance().GetContext()->RSSetViewports(1, &UpViewPort);
다음은 블러이다.
추출된 렌더타겟을 BlurPixelShader에서 사용할 텍스쳐로 연결해주었다.
그리고, 해당 텍스쳐에 블러처리를 해주었다.
#include "LightHeader.hlsli"
struct PixelShaderInput
{
float4 pos : SV_POSITION;
float2 TexCoord : TEXCOORD;
};
cbuffer EBloomData : register(b0)
{
int Width;
int Height;
int Padding1;
int Padding2;
};
Texture2D DiffuseTexture : register(t0);
SamplerState Sampler : register(s0);
static float Gau[5][5] =
{
{ 1, 4, 6, 4, 1 },
{ 4, 16, 24, 16, 4 },
{ 6, 24, 36, 24, 6 },
{ 4, 16, 24, 16, 4 },
{ 1, 4, 6, 4, 1 }
};
float4 main(PixelShaderInput _Input) : SV_TARGET
{
float4 Color = (float4)0.0f;
float WidthRatio = 1.0f / (float) Width;
float HeightRatio = 1.0f / (float) Height;
float2 StartTexCoord = float2(_Input.TexCoord.x - WidthRatio * 2, _Input.TexCoord.y - HeightRatio * 2);
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
Color += DiffuseTexture.Sample(Sampler, StartTexCoord.xy) * Gau[j][i];
StartTexCoord.y += HeightRatio;
}
StartTexCoord.x += WidthRatio;
StartTexCoord.y = _Input.TexCoord.y - HeightRatio * 2;
}
Color /= 256.0f;
return Color;
}
블러는 앞의 게시물에서 본 것과 동일하게 가우시안 필터를 사용하여 구현하였다.
이 때, 포스트프로세스 클래스의 코드를 보면 뷰포트를 세팅해주고 있는데 이유는 블러를 할 때 다운샘플링을 했기 때문이다. 블러의 범위를 넓게 적용하기 위해 해상도를 낮춘채로 블러를 적용하였고, 화면에 제대로 렌더링되도록 하기 위해 뷰포트와 크기를 맞춰준 것이다.
////병합
SetTexture(BlurTarget.SRV, 0);
EngineBase::GetInstance().GetContext()->OMSetRenderTargets(1, DoubleBufferRTV.GetAddressOf(), DepthStencilView.Get());
EngineBase::GetInstance().GetContext()->OMSetBlendState(BlendState.Get(), NULL, 0xFFFFFFFF);
PostProcessRenderer->SetPSShader(L"BloomPixelShader.hlsl");
PostProcessRenderer->Render(_DeltaTime);
블러까지 적용한 뒤, 기존 이미지에 합성해주었다.
블러된 이미지가 더블버퍼의 원본 이미지와 제대로 합성될 수 있도록 BlendState도 만들어주었다.
void BloomPostProcess::CreateBlendState()
{
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;
Desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
Desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
Desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
Desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_MAX;
Desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
Desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
EngineBase::GetInstance().GetDevice()->CreateBlendState(&Desc, BlendState.GetAddressOf());
}
결과는 위와 같다.
디테일은 많이 떨어지지만, 일단 블룸 자체는 잘 적용되는 듯 하다.
제대로 하려면, 다운샘플링하는 과정과 업샘플링하는 과정을 여러번 반복해야하지만, 아직은 이정도로 만족하자.
이제, 어느정도 쉐이더를 적용해보았으니 이제는 엔진 자체를 개선할 때가 된 것 같다.
엔진 내에 하드코딩된 부분도 너무 많고, 너무 안좋게 설계된 부분이 많다고 생각되기 때문에 천천히 엔진을 고쳐나가보자!