Diligent Engine
 
Loading...
Searching...
No Matches
Diligent::Bloom Class Reference

Implements bloom post-process effect. More...

#include <Bloom.hpp>

Classes

struct  CreateInfo
 Create info. More...
 
struct  RenderAttributes
 Render attributes. More...
 

Public Member Functions

 Bloom (IRenderDevice *pDevice, const CreateInfo &CI)
 Creates a new instance of the Bloom class.
 
void PrepareResources (IRenderDevice *pDevice, IDeviceContext *pDeviceContext, PostFXContext *pPostFXContext, FEATURE_FLAGS FeatureFlags)
 Prepares the bloom effect for rendering.
 
void Execute (const RenderAttributes &RenderAttribs)
 Executes the bloom effect.
 
ITextureViewGetBloomTextureSRV () const
 Returns the shader resource view of the bloom texture.
 

Static Public Member Functions

static bool UpdateUI (HLSL::BloomAttribs &Attribs, FEATURE_FLAGS &FeatureFlags)
 Adds the ImGui controls to the UI.
 

Detailed Description

Implements bloom post-process effect.

Bloom

Table of contents

  • Introduction
  • Integration guidelines
    • Input resources
    • Host API
  • Implementation details
  • References

Introduction

Displays have a low dynamic range (LDR), limiting brightness levels to a scale from black to full brightness (RGB 0 to 1), unlike the unlimited brightness in real life. High dynamic range (HDR) technology allows for brightness levels above 1, though actual displays still limit these to their maximum LDR brightness. HDR scenes are adapted for LDR displays through tonemapping and auto-exposure, which adjust the image's brightness and contrast.

Bloom is an effect that simulates intense brightness by causing bright pixels to bleed into adjacent ones.

Integration guidelines

Input resources

The following table enumerates all external inputs required by Bloom effect.

Name Format Notes
Color buffer APPLICATION SPECIFIED (3x FLOAT) The HDR render target of the current frame containing the scene radiance

The effect uses a number of parameters that control the quality and performance and are organized into the HLSL::ScreenSpaceAmbientOcclusionAttribs structure. The following table lists the parameters and their descriptions.

Name Notes
Intensity Intensity of the bloom effect.
Threshold The minimum brightness required for a pixel to contribute to the bloom effect.
SoftTreshold The softness of the threshold. A higher value will result in a softer threshold.
Radius The size of the bloom effect. A larger radius will result in a larger area of the image being affected by the bloom effect.

Host API

To integrate Bloom into your project, include the following header files:

#include "PostFXContext.hpp"
#include "Bloom.hpp"
namespace HLSL
{
#include "Shaders/Common/public/BasicStructures.fxh"
#include "Shaders/PostProcess/Bloom/public/BloomStructures.fxh"
} // namespace HLSL

Now, create the necessary objects:

m_PostFXContext = std::make_unique<PostFXContext>(m_pDevice);
m_Bloom = std::make_unique<Bloom>(m_pDevice);

Next, call the methods to prepare resources for the PostFXContext and Bloom objects. This needs to be done every frame before starting the rendering process.

PostFXContext::FrameDesc FrameDesc;
FrameDesc.Index = m_CurrentFrameNumber; // Current frame number.
FrameDesc.Width = SCDesc.Width; // Current screen width.
FrameDesc.Height = SCDesc.Height; // Current screen height.
m_PostFXContext->PrepareResources(m_pDevice, FrameDesc, PostFXContext::FEATURE_FLAG_NONE);
m_Bloom->PrepareResources(m_pDevice, m_pImmediateContext, m_PostFXContext.get(), Bloom::FEATURE_FLAG_NONE);

Next, call the PostFXContext::Execute method prepare intermediate resources required by all post-processing objects dependent on PostFXContext. This method can take a constant buffer containing the current and previous-frame cameras (refer to these code examples: [0] and [1]). Alternatively, you can pass the corresponding pointers const HLSL::CameraAttribs* pCurrCamera and const HLSL::CameraAttribs* pPrevCamera for the current and previous cameras, respectively. You also need to pass the depth of the current and previous frames, and a buffer with motion vectors in NDC space, via the corresponding ITextureView* pCurrDepthBufferSRV, ITextureView* pPrevDepthBufferSRV, and ITextureView* pMotionVectorsSRV pointers.

PostFXContext::RenderAttributes PostFXAttibs;
PostFXAttibs.pDevice = m_pDevice;
PostFXAttibs.pDeviceContext = m_pImmediateContext;
PostFXAttibs.pCameraAttribsCB = m_FrameAttribsCB;
m_PostFXContext->Execute(PostFXAttibs);

To calculate bloom effect, call the Bloom::Execute method. Before this, fill the BloomAttribs and Bloom::RenderAttributes structures with the necessary data. Refer to the Input resources section for parameter description.

HLSL::BloomAttribs BloomSettings{};
Bloom::RenderAttributes BloomRenderAttribs{};
BloomRenderAttribs.pDevice = m_pDevice;
BloomRenderAttribs.pDeviceContext = m_pImmediateContext;
BloomRenderAttribs.pPostFXContext = m_PostFXContext.get();
BloomRenderAttribs.pColorBufferSRV = m_ColorBuffer;
BloomRenderAttribs.pBloomAttribs = &BloomSettings;
m_Bloom->Execute(SSAORenderAttribs);
Render attributes.
Definition Bloom.hpp:201
IRenderDevice * pDevice
Render device that may be used to create new objects needed for this frame, if any.
Definition Bloom.hpp:203

To obtain an ITextureView of the texture containing the bloom result, use the Bloom::GetBloomTextureSRV method.

Implementation details

Our implementation is based on the technique described in [Léna Piquet, 2021]. We modified it by adding a threshold and using the Additive Bloom as described in [Alexander Christensen, 2022]. Additionally, we changed the weighting to eliminate fireflies from this article (section Bonus material 2: Karis average) as follows:

float Weights[5];
Weights[0] = 0.125f;
Weights[1] = 0.125f;
Weights[2] = 0.125f;
Weights[3] = 0.125f;
Weights[4] = 0.5f;
float3 Groups[5];
Groups[0] = (A + B + D + E) / 4.0f;
Groups[1] = (B + C + E + F) / 4.0f;
Groups[2] = (D + E + G + H) / 4.0f;
Groups[3] = (E + F + H + I) / 4.0f;
Groups[4] = (J + K + L + M) / 4.0f;
float4 ColorSum = float4(0.0, 0.0, 0.0, 0.0);
for (int GroupId = 0; GroupId < 5; ++GroupId) {
float Weight = Weights[GroupId] * KarisAverage(Groups[GroupId]);
ColorSum += float4(Groups[GroupId], 1.0) * Weight;
}
float3 ResultColor = ColorSum.xyz / ColorSum.w;

References