Fixes for SSAO and SSR

This commit is contained in:
Wojtek Figat
2026-01-20 18:19:34 +01:00
parent 0c5cb59875
commit cc5e4c19e1
9 changed files with 43 additions and 38 deletions

View File

@@ -704,6 +704,12 @@ float RenderTools::GetDepthBounds(const RenderView& view, const Float3& point, b
return Math::Clamp(depth, 0.0f, 1.0f);
}
float RenderTools::GetDepthBounds(const RenderView& view, float viewDistance, bool near)
{
Float3 point = view.Position + view.Direction * (viewDistance * 2.0f);
return GetDepthBounds(view, point, near);
}
Float3 RenderTools::GetColorQuantizationError(PixelFormat format)
{
Float3 mantissaBits;

View File

@@ -155,6 +155,7 @@ public:
static Float2 GetDepthBounds(const RenderView& view, const BoundingBox& bounds);
static Float2 GetDepthBounds(const RenderView& view, const OrientedBoundingBox& bounds);
static float GetDepthBounds(const RenderView& view, const Float3& point, bool near);
static float GetDepthBounds(const RenderView& view, float viewDistance, bool near);
static constexpr float DepthBoundMaxBackground = 1.0f - 0.0000001f; // Skip background/sky pixels from shading
// Calculates error for a given render target format to reduce floating-point precision artifacts via QuantizeColor (from Noise.hlsl).

View File

@@ -270,12 +270,12 @@ void AmbientOcclusionPass::Render(RenderContext& renderContext)
// Cache data
auto device = GPUDevice::Instance;
auto context = device->GetMainContext();
float m_sizeX = (float)renderContext.Buffers->GetWidth();
float m_sizeY = (float)renderContext.Buffers->GetHeight();
float m_halfSizeX = (m_sizeX + 1) / 2;
float m_halfSizeY = (m_sizeY + 1) / 2;
int32 m_sizeX = renderContext.Buffers->GetWidth();
int32 m_sizeY = renderContext.Buffers->GetHeight();
int32 m_halfSizeX = (m_sizeX + 1) / 2;
int32 m_halfSizeY = (m_sizeY + 1) / 2;
GPUTexture* depthBuffer = aoSettings.DepthResolution == ResolutionMode::Full ? renderContext.Buffers->DepthBuffer : renderContext.Buffers->RequestHalfResDepth(context);
GPUTextureView* depthBufferApply = _depthBounds ? renderContext.Buffers->DepthBuffer ->ViewReadOnlyDepth() : nullptr;
GPUTextureView* depthBufferApply = _depthBounds ? renderContext.Buffers->DepthBuffer->ViewReadOnlyDepth() : nullptr;
// Request temporary buffers
GPUTexture* m_halfDepths[4];
@@ -287,20 +287,20 @@ void AmbientOcclusionPass::Render(RenderContext& renderContext)
for (int i = 0; i < 4; i++)
{
#if SSAO_DEPTH_MIPS_ENABLE_AT_QUALITY_PRESET < 99
tempDesc = GPUTextureDescription::New2D((int32)m_halfSizeX, (int32)m_halfSizeY, 0, SSAO_DEPTH_FORMAT, GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget | GPUTextureFlags::PerMipViews);
tempDesc = GPUTextureDescription::New2D(m_halfSizeX, m_halfSizeY, 0, SSAO_DEPTH_FORMAT, GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget | GPUTextureFlags::PerMipViews);
#else
tempDesc = GPUTextureDescription::New2D((int32)m_halfSizeX, (int32)m_halfSizeY, SSAO_DEPTH_FORMAT, GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget);
tempDesc = GPUTextureDescription::New2D(m_halfSizeX, m_halfSizeY, SSAO_DEPTH_FORMAT, GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget);
#endif
m_halfDepths[i] = RenderTargetPool::Get(tempDesc);
RENDER_TARGET_POOL_SET_NAME(m_halfDepths[i], "SSAO.HalfDepth");
}
tempDesc = GPUTextureDescription::New2D((int32)m_halfSizeX, (int32)m_halfSizeY, SSAO_AO_RESULT_FORMAT);
tempDesc = GPUTextureDescription::New2D(m_halfSizeX, m_halfSizeY, SSAO_AO_RESULT_FORMAT);
m_pingPongHalfResultA = RenderTargetPool::Get(tempDesc);
RENDER_TARGET_POOL_SET_NAME(m_pingPongHalfResultA, "SSAO.ResultsHalfA");
tempDesc = GPUTextureDescription::New2D((int32)m_halfSizeX, (int32)m_halfSizeY, SSAO_AO_RESULT_FORMAT);
tempDesc = GPUTextureDescription::New2D(m_halfSizeX, m_halfSizeY, SSAO_AO_RESULT_FORMAT);
m_pingPongHalfResultB = RenderTargetPool::Get(tempDesc);
RENDER_TARGET_POOL_SET_NAME(m_pingPongHalfResultA, "SSAO.ResultsHalfB");
tempDesc = GPUTextureDescription::New2D((int32)m_halfSizeX, (int32)m_halfSizeY, SSAO_AO_RESULT_FORMAT, GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget, 4);
tempDesc = GPUTextureDescription::New2D(m_halfSizeX, m_halfSizeY, SSAO_AO_RESULT_FORMAT, GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget, 4);
m_finalResults = RenderTargetPool::Get(tempDesc);
RENDER_TARGET_POOL_SET_NAME(m_finalResults, "SSAO.Results");
}
@@ -318,11 +318,12 @@ void AmbientOcclusionPass::Render(RenderContext& renderContext)
GBufferPass::SetInputs(view, _constantsBufferData.GBuffer);
Matrix::Transpose(view.View, _constantsBufferData.ViewMatrix);
_constantsBufferData.ViewportPixelSize = Float2(1.0f / m_sizeX, 1.0f / m_sizeY);
_constantsBufferData.HalfViewportPixelSize = Float2(1.0f / m_halfSizeX, 1.0f / m_halfSizeY);
_constantsBufferData.ViewportPixelSize = Float2(1.0f / (float)m_sizeX, 1.0f / (float)m_sizeY);
_constantsBufferData.HalfViewportPixelSize = Float2(1.0f / (float)m_halfSizeX, 1.0f / (float)m_halfSizeY);
_constantsBufferData.Viewport2xPixelSize = Float2(_constantsBufferData.ViewportPixelSize.X * 2.0f, _constantsBufferData.ViewportPixelSize.Y * 2.0f);
_constantsBufferData.Viewport2xPixelSize_x_025 = Float2(_constantsBufferData.Viewport2xPixelSize.X * 0.25f, _constantsBufferData.Viewport2xPixelSize.Y * 0.25f);
_constantsBufferData.InputDepthScale = aoSettings.DepthResolution == ResolutionMode::Full ? 2 : 1;
const float tanHalfFOVY = 1.0f / proj.Values[1][1];
@@ -375,7 +376,7 @@ void AmbientOcclusionPass::Render(RenderContext& renderContext)
// Bind scene depth buffer and set proper viewport
context->BindSR(SSAO_TEXTURE_SLOT0, depthBuffer);
context->SetViewportAndScissors(m_halfSizeX, m_halfSizeY);
context->SetViewportAndScissors((float)m_halfSizeX, (float)m_halfSizeY);
// Prepare depth in half resolution
{
@@ -437,7 +438,7 @@ void AmbientOcclusionPass::Render(RenderContext& renderContext)
// Generate SSAO (interleaved in checkerboard pattern)
{
context->SetViewportAndScissors(m_halfSizeX, m_halfSizeY);
context->SetViewportAndScissors((float)m_halfSizeX, (float)m_halfSizeY);
for (int32 pass = 0; pass < 4; pass++)
{
if (settings.SkipHalfPixels && ((pass == 1) || (pass == 2)))
@@ -526,10 +527,9 @@ void AmbientOcclusionPass::Render(RenderContext& renderContext)
}
// Apply
Float3 depthBoundPoint = renderContext.View.Position + renderContext.View.Direction * (aoSettings.FadeOutDistance * 2.0f);
context->SetDepthBounds(0.0f, RenderTools::GetDepthBounds(renderContext.View, depthBoundPoint, false));
context->SetDepthBounds(0.0f, RenderTools::GetDepthBounds(renderContext.View, aoSettings.FadeOutDistance, false));
context->BindSR(SSAO_TEXTURE_SLOT0, m_finalResults->ViewArray());
context->SetViewportAndScissors(m_sizeX, m_sizeY);
context->SetViewportAndScissors((float)m_sizeX, (float)m_sizeY);
context->SetState(settings.SkipHalfPixels ? _psApplyHalf : _psApply);
context->SetRenderTarget(depthBufferApply, renderContext.Buffers->GBuffer0->View());
context->DrawFullscreenTriangle();

View File

@@ -31,6 +31,9 @@ private:
int32 PassIndex;
float EffectMaxDistance;
Float3 Padding0;
uint32 InputDepthScale;
Float2 Viewport2xPixelSize;
Float2 Viewport2xPixelSize_x_025;

View File

@@ -362,15 +362,6 @@ void ReflectionsPass::Render(RenderContext& renderContext, GPUTextureView* light
{
ScreenSpaceReflectionsPass::Instance()->Render(renderContext, *reflectionsBuffer, lightBuffer);
context->SetViewportAndScissors(renderContext.Task->GetViewport());
/*
// DEBUG_CODE
context->RestoreViewport();
context->SetRenderTarget(output);
context->Draw(reflectionsRT);
return;
// DEBUG_CODE
*/
}
if (renderContext.View.Mode == ViewMode::Reflections)

View File

@@ -166,14 +166,14 @@ void ScreenSpaceReflectionsPass::Render(RenderContext& renderContext, GPUTexture
const int32 traceHeight = RenderTools::GetResolution(height, settings.RayTracePassResolution);
const int32 resolveWidth = RenderTools::GetResolution(width, settings.ResolvePassResolution);
const int32 resolveHeight = RenderTools::GetResolution(height, settings.ResolvePassResolution);
const int32 colorBufferWidth = width / 2;
const int32 colorBufferHeight = height / 2;
const int32 temporalWidth = width;
const int32 temporalHeight = height;
const int32 colorBufferWidth = RenderTools::GetResolution(width, ResolutionMode::Half);
const int32 colorBufferHeight = RenderTools::GetResolution(height, ResolutionMode::Half);
const int32 temporalWidth = resolveWidth;
const int32 temporalHeight = resolveHeight;
const auto colorBufferMips = MipLevelsCount(colorBufferWidth, colorBufferHeight);
// Prepare buffers
auto tempDesc = GPUTextureDescription::New2D(width / 2, height / 2, 0, PixelFormat::R11G11B10_Float, GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget | GPUTextureFlags::PerMipViews);
auto tempDesc = GPUTextureDescription::New2D(colorBufferWidth, colorBufferHeight, 0, PixelFormat::R11G11B10_Float, GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget | GPUTextureFlags::PerMipViews);
auto colorBuffer0 = RenderTargetPool::Get(tempDesc);
RENDER_TARGET_POOL_SET_NAME(colorBuffer0, "SSR.ColorBuffer0");
// TODO: maybe allocate colorBuffer1 smaller because mip0 is not used (the same as PostProcessingPass for Bloom), keep in sync to use the same buffer in frame
@@ -305,9 +305,8 @@ void ScreenSpaceReflectionsPass::Render(RenderContext& renderContext, GPUTexture
// and improves resolve pass performance (faster color texture lookups, less cache misses)
// Also for high surface roughness values it adds more blur to the reflection tail which looks more realistic.
const auto filterMode = MultiScaler::FilterMode::GaussianBlur9;
// Downscale with gaussian blur
auto filterMode = PLATFORM_ANDROID || PLATFORM_IOS || PLATFORM_SWITCH ? MultiScaler::FilterMode::GaussianBlur5 : MultiScaler::FilterMode::GaussianBlur9;
for (int32 mipLevel = 1; mipLevel < colorBufferMips; mipLevel++)
{
const int32 mipWidth = Math::Max(colorBufferWidth >> mipLevel, 1);