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

Implements screen-space ambient occlusion post-process effect. More...

#include <ScreenSpaceAmbientOcclusion.hpp>

Classes

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

Public Types

enum  FEATURE_FLAGS : Uint32 { FEATURE_FLAG_NONE = 0u , FEATURE_FLAG_HALF_PRECISION_DEPTH = 1u << 0u , FEATURE_FLAG_HALF_RESOLUTION = 1u << 1u }
 Feature flags that control the behavior of the effect. More...
enum  ALGORITHM_TYPE : Uint32 { ALGORITHM_TYPE_GTAO = 0 , ALGORITHM_TYPE_HBAO = 1 , ALGORITHM_TYPE_VBAO = 2 }
 Ambient occlusion algorithm. More...

Public Member Functions

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

Static Public Member Functions

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

Detailed Description

Implements screen-space ambient occlusion post-process effect.

Screen Space Ambient Occlusion

Kitchen scene rendered by Hydrogent with Screen Space Ambient Occlusion effect applied.

Table of contents

  • IntroductionIntroduction
  • Integration guidelinesIntegration guidelines
  • Input resourcesInput resources
  • Host APIHost API
  • Implementation detailsImplementation details

Introduction

We needed to add screen space ambient occlusion to our project with the following requirements:

  • Compatibility with WebGL
  • Use cosine-weighted AO and have the ability to use a large radius

    We used Intel's implementation of Screen Space Occlusion as the basis for our implementation [Intel-XeGTAO]

Integration guidelines

Input resources

The following table enumerates all external inputs required by SSAO.

Name Format Notes
Depth buffer APPLICATION SPECIFIED (1x FLOAT) The depth buffer for the current frame provided by the application. The data should be provided as a single floating point value, the precision of which is under the application's control.
Normal buffer APPLICATION SPECIFIED (3x FLOAT) The normal buffer for the current frame provided by the application in the [-1.0, +1.0] range. Normals should be in world space.

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

Name Notes
Effect radius World-space ambient occlusion radius.
Effect falloff range Gently reduces sample impact as it gets out of the 'Effect radius' bounds.
Radius multiplier Use different value as compared to the ground truth radius to counter inherent screen space biases.
Depth MIP sampling offset Controls the main trade-off between performance (memory bandwidth) and quality (temporal stability is the first affected, thin objects next).
Temporal stability factor The value is responsible for interpolating between the current and previous frame.
Spatial reconstruction radius Controls the kernel size in the spatial reconstruction step. Increasing the value increases the deviation from the ground truth but reduces the noise.
Bitmask thickness View-space occluder thickness used by the VBAO algorithm. Larger values let more light pass behind thin surfaces.

The effect can be configured using the ScreenSpaceAmbientOcclusion::FEATURE_FLAGS enumeration. The following table lists the flags and their descriptions.

Name Notes
FEATURE_FLAG_HALF_PRECISION_DEPTH Use half-precision fixed-point format for depth values in the SSAO computation
FEATURE_FLAG_HALF_RESOLUTION Compute SSAO at half resolution of the target render texture

The ambient occlusion algorithm is selected with HLSL::ScreenSpaceAmbientOcclusionAttribs::Algorithm using the SSAO_ALGORITHM_GTAO, SSAO_ALGORITHM_HBAO, or SSAO_ALGORITHM_VBAO constants. These values match the host-side ScreenSpaceAmbientOcclusion::ALGORITHM_TYPE enumeration.

Host API

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

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

Now, create the necessary objects:

m_PostFXContext = std::make_unique<PostFXContext>(m_pDevice);
m_SSAO = std::make_unique<ScreenSpaceAmbientOcclusion>(m_pDevice);

Next, call the methods to prepare resources for the PostFXContext and ScreenSpaceAmbientOcclusion 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_ScreenSpaceAmbientOcclusion->PrepareResources(m_pDevice, m_pImmediateContext, m_PostFXContext.get(), ActiveFeatures);
}
FEATURE_FLAGS
Feature flags that control the behavior of the effect.
Definition ScreenSpaceAmbientOcclusion.hpp:219

Now we invoke the method PostFXContext::Execute. At this stage, some intermediate resources necessary for all post-processing objects dependent on PostFXContext are calculated. This method can take a constant buffer directly containing an array from the current and previous cameras (for this method, you can refer to this section of the code [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 (the depth buffers should not contain transparent objects), and a buffer with motion vectors in NDC space, into the corresponding ITextureView* pCurrDepthBufferSRV, ITextureView* pPrevDepthBufferSRV, ITextureView* pMotionVectorsSRV pointers.

{
PostFXContext::RenderAttributes PostFXAttibs;
PostFXAttibs.pDevice = m_pDevice;
PostFXAttibs.pDeviceContext = m_pImmediateContext;
PostFXAttibs.pCameraAttribsCB = m_FrameAttribsCB; // m_Resources[RESOURCE_IDENTIFIER_CAMERA_CONSTANT_BUFFER].AsBuffer();
PostFXAttibs.pCurrDepthBufferSRV = m_CurrDepthBuffer; // m_Resources[RESOURCE_IDENTIFIER_DEPTH0 + CurrFrameIdx].GetTextureSRV();
PostFXAttibs.pPrevDepthBufferSRV = m_PrevDepthBuffer; // m_Resources[RESOURCE_IDENTIFIER_DEPTH0 + PrevFrameIdx].GetTextureSRV();
PostFXAttibs.pMotionVectorsSRV = m_MotionBuffer; // m_GBuffer->GetBuffer(GBUFFER_RT_MOTION_VECTORS)->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE);
m_PostFXContext->Execute(PostFXAttibs);
}

Now we need to directly invoke calculation of SSAO. To do this, we call the ScreenSpaceAmbientOcclusion::Execute method. Before this, we need to fill the passed structures ScreenSpaceAmbientOcclusionAttribs and ScreenSpaceAmbientOcclusion::RenderAttributes with the necessary data. Refer to the Input resources sectionInput resources section for parameter description.

{
HLSL::ScreenSpaceAmbientOcclusionAttribs SSAOSettings{};
SSAORenderAttribs.pDevice = m_pDevice;
SSAORenderAttribs.pDeviceContext = m_pImmediateContext;
SSAORenderAttribs.pPostFXContext = m_PostFXContext.get();
SSAORenderAttribs.pDepthBufferSRV = m_CurrDepthBuffer; // m_Resources[RESOURCE_IDENTIFIER_DEPTH0 + CurrFrameIdx].GetTextureSRV();
SSAORenderAttribs.pNormalBufferSRV = m_NormalBuffer; // m_GBuffer->GetBuffer(GBUFFER_RT_NORMAL)->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE);
SSAORenderAttribs.pSSAOAttribs = &SSAOSettings;
m_SSAO->Execute(SSAORenderAttribs);
}
Render attributes.
Definition ScreenSpaceAmbientOcclusion.hpp:245
ITextureView * pNormalBufferSRV
Shader resource view of the source normal buffer.
Definition ScreenSpaceAmbientOcclusion.hpp:262
IDeviceContext * pDeviceContext
Device context that will record the rendering commands.
Definition ScreenSpaceAmbientOcclusion.hpp:253
IRenderDevice * pDevice
Render device that may be used to create new objects needed for this frame, if any.
Definition ScreenSpaceAmbientOcclusion.hpp:247
PostFXContext * pPostFXContext
PostFX context.
Definition ScreenSpaceAmbientOcclusion.hpp:256
const HLSL::ScreenSpaceAmbientOcclusionAttribs * pSSAOAttribs
SSAO settings.
Definition ScreenSpaceAmbientOcclusion.hpp:265
ITextureView * pDepthBufferSRV
Shader resource view of the source depth.
Definition ScreenSpaceAmbientOcclusion.hpp:259

Now, you can directly obtain a ITextureView on the texture containing the SSAO result using the method ScreenSpaceAmbientOcclusion::GetAmbientOcclusionSRV. After this, you can apply SSAO in your rendering pipeline using the formula below.

\large \text{DiffuseRadiance} = \sum_{i=1}^{n} (1 - F) \times \text{Diffuse} \times \text{SSAO} \times \text{IrradianceMap}_i

The parameter $F$ is the Fresnel Schlick coefficient. You can use the approximation FresnelSchlickWithRoughness from this article [Sébastien Lagarde, Adopting a physically based shading model]

Implementation details

As we mentioned earlier, we use the [Intel-XeGTAO] implementation as a starting point for our algorithm. For a deeper understanding of the context and nuances involved, it is recommended to review the documentation available in the implementation's repository.

Modifications made from the original algorithm version include:

  • Unfortunately, we cannot use compute shaders for calculating prefiltered mip levels of the depth map, so we use pixel shaders for convolving the depth map, thus we loose a bit in performance.
  • Currently, the computation of bent-normals is not implemented. However, we are considering incorporating this feature in future updates.
  • We have added the capability to compute Ambient Occlusion at half resolution. This is achieved by downsampling the depth buffer using a checkerboard pattern, effectively reducing it by half. For additional details on this process, the [DAW-Upsampling] technique provides comprehensive insights.
  • The denoiser from XeGTAO is ineffective with larger AO radii, necessitating the development of a more sophisticated denoiser based on the ReBLUR algorithm. To grasp a better understanding of the underlying principles and our approach, reviewing related presentations is advised [RTAO, 2019], [ReBLUR, 2020], [ReBLUR, 2021].

References

Member Enumeration Documentation

◆ ALGORITHM_TYPE

Ambient occlusion algorithm.

Enumerator
ALGORITHM_TYPE_GTAO 

Ground-Truth Ambient Occlusion (cosine-weighted horizon integration).

ALGORITHM_TYPE_HBAO 

Horizon-Based Ambient Occlusion (uniformly weighted horizon integration).

ALGORITHM_TYPE_VBAO 

Visibility Bitmask Ambient Occlusion (partial occlusion via a per-sector bit field).

◆ FEATURE_FLAGS

Feature flags that control the behavior of the effect.

Enumerator
FEATURE_FLAG_NONE 

No feature flags are set.

FEATURE_FLAG_HALF_PRECISION_DEPTH 

Use half-precision depth buffer.

FEATURE_FLAG_HALF_RESOLUTION 

Compute the effect in half resolution.