개발 일자 : 2022년 3월 7일 ~ 10일
지난 번에 창문으로 햇빛이 들어오는 효과를 위해서 Emissive를 개발했고 아트에게 Emissive Map도 요청해서 받았으나, 적용해보니 생각보다 느낌이 살지 않아 Bloom을 개발하게 되었습니다.
결과는 만족스럽게 나왔고 어떤식으로 개발했는지 살펴보겠습니다.
제가 참고한 Bloom 예시는 Halo3의 자료입니다.
이 자료에서 Bloom의 단계는 크게 9단계로 나뉩니다.
왼쪽에 Halo3 예시와 함께 제 엔진에서 적용된 화면을 같이 보겠습니다.
먼저, 원본 렌더타겟에 4x4 다운샘플링을 합니다.
그 후 Bloom Curve를 적용시켜 일정 밝기 이상을 추출해줍니다.
코드는 책과 Halo3 자료를 참고했습니다.
float4 PS(VertexOut pin) : SV_Target
{
float3 color = gTexture.Sample(samLinear, pin.Tex).xyz;
float intensity = dot(color, float3(0.3f, 0.3f, 0.3f));
float bloomIntensity = GetBloomCurve(intensity);
float3 bloomColor = color * bloomIntensity / intensity;
return float4(bloomColor.xyz, 1.0);
}
float GetBloomCurve(float x)
{
float result = x;
x *= 2.0f;
#ifdef BLOOMCURVE_METHOD_1
result = x * 0.05 + max(0, x - gThreshold) * 0.5; // default gThreshold = 1.26
#endif
#ifdef BLOOMCURVE_METHOD_2
result = x * x / 3.2;
#endif
#ifdef BLOOMCURVE_METHOD_3
result = max(0, x - gThreshold); // default gThreshold = 1.0
result *= result;
#endif
return result * 0.5f;
}
예시에서는 샘플러 스테이트의 필터를 POINT로 설정해 네모네모한 모습이고, 저는 LINEAR로 설정해 픽셀이 주변 픽셀들의 평균으로 계산되어 더 부드럽다는 차이가 있습니다.
Bloom에 관여하는 클래스는 2개입니다.
먼저, Scaler 클래스는 렌더 타겟을 Down,Up Sampling하는데 사용됩니다.
- 스케일 함수, output으로 나갈 렌더 타겟 사이즈에 맞춰 뷰포트 크기를 자동으로 조정합니다.
void Scaler::Scale(RenderTarget* output, RenderTarget* input)
{
// 뷰포트 크기 설정
m_Viewport->Width = (float)output->GetWidth();
m_Viewport->Height = (float)output->GetHeight();
ID3D11DeviceContext* _dc = m_DX11Core->GetDC();
_dc->ClearRenderTargetView(output->GetRTV(), reinterpret_cast<const float*>(&EColors::Black));
ID3D11RenderTargetView* rt[] = { output->GetRTV() };
_dc->OMSetRenderTargets(1, &rt[0], 0);
// 뷰포트 세팅
_dc->RSSetViewports(1, m_Viewport.get());
Effects::DebugTexFX->SetWorldViewProj(EMath::Matrix::Identity);
Effects::DebugTexFX->SetTexture(input->GetSRV());
ID3DX11EffectTechnique* tech = Effects::DebugTexFX->ViewArgbTech;
RenderTargetDrawer::DrawRenderTarget(_dc, tech);
ID3D11ShaderResourceView* nullSRV[1] = { 0 };
_dc->PSSetShaderResources(0, 1, nullSRV);
}
또, 기존의 BlurManager 클래스에 Bloom 함수를 추가하였습니다. 다음은 위에서 보여드린 과정을 코드로 나타낸 것입니다.
void BlurManager::Bloom(Scaler* scaler, RenderTarget* output, RenderTarget* input)
{
// 먼저, 원본을 저장 (여기다 계속 픽셀을 누적시킬 것임)
CopyOriginalRenderTarget(m_BloomOriginal.get(), input);
CopyOriginalRenderTarget(m_BloomAccumulate_3.get(), input);
// 4x4 DownSample + Bloom Curve 적용으로 밝은 부분 추출
scaler->Scale(m_BloomDownScale4x4_0.get(), input);
BloomCurve(m_BloomCurve_1.get(), m_BloomDownScale4x4_0.get());
CopyOriginalRenderTarget(m_BloomAccumulate_2.get(), m_BloomCurve_1.get());
// 6x6 DownSample
scaler->Scale(m_BloomDownScale6x6_2.get(), m_BloomCurve_1.get());
CopyOriginalRenderTarget(m_BloomAccumulate_0.get(), m_BloomDownScale6x6_2.get());
// 6x6 DownSample
scaler->Scale(m_BloomDownScale6x6_3.get(), m_BloomDownScale6x6_2.get());
// Blur Again
Blur(m_BloomDownScale6x6_3.get());
/// Up Sampling
// 6x6 UpSample + Accumulate(축적)
scaler->Scale(m_BloomUpScale6x6_4.get(), m_BloomDownScale6x6_3.get());
Accumulate(m_BloomAccumulate_1.get(), m_BloomAccumulate_0.get(), m_BloomUpScale6x6_4.get());
// Blur
Blur(m_BloomAccumulate_1.get());
// 6x6 UpSample + Accumulate(축적) + Blur
scaler->Scale(m_BloomUpScale6x6_5.get(), m_BloomAccumulate_1.get());
Accumulate(m_BloomAccumulate_3.get(), m_BloomAccumulate_2.get(), m_BloomUpScale6x6_5.get());
Blur(m_BloomAccumulate_3.get());
// 4x4 UpSample + Accumulate(축적)
scaler->Scale(m_BloomUpScale4x4_6.get(), m_BloomAccumulate_3.get());
Accumulate_Per(output, m_BloomOriginal.get(), m_BloomUpScale4x4_6.get());
}
참고
- HDR The Bungie Way 자료
'DirectX 자체엔진 개발' 카테고리의 다른 글
[DirectX 11] HDR, Tone Mapping (0) | 2022.04.15 |
---|---|
[DirectX 11] 외곽선 (0) | 2022.04.14 |
[DirectX 11] 파티클 시스템 (0) | 2022.03.18 |
[DirectX 11] Emissive (0) | 2022.03.14 |
[DirectX 11] 자체 포맷 개발 (0) | 2022.02.25 |