Fix depth bounds in reversed Z

#2684
This commit is contained in:
2026-05-08 09:02:17 +02:00
parent ead71e6836
commit 7127ccda37
8 changed files with 73 additions and 26 deletions
+9 -4
View File
@@ -15,9 +15,13 @@
#endif #endif
#if FLAX_REVERSE_Z #if FLAX_REVERSE_Z
#define GPU_DEPTH_CLEAR_VALUE 0.0f #define GPU_DEPTH_MIN_VALUE 1.0f
#define GPU_DEPTH_MAX_VALUE 0.0f
#define GPU_DEPTH_BOUNDS_SWAP(min, max) max, min
#else #else
#define GPU_DEPTH_CLEAR_VALUE 1.0f #define GPU_DEPTH_MIN_VALUE 0.0f
#define GPU_DEPTH_MAX_VALUE 1.0f
#define GPU_DEPTH_BOUNDS_SWAP(min, max) min, max
#endif #endif
class GPUConstantBuffer; class GPUConstantBuffer;
@@ -214,7 +218,7 @@ public:
/// <param name="depthBuffer">The depth buffer to clear.</param> /// <param name="depthBuffer">The depth buffer to clear.</param>
/// <param name="depthValue">The clear depth value.</param> /// <param name="depthValue">The clear depth value.</param>
/// <param name="stencilValue">The clear stencil value.</param> /// <param name="stencilValue">The clear stencil value.</param>
API_FUNCTION() virtual void ClearDepth(GPUTextureView* depthBuffer, float depthValue = GPU_DEPTH_CLEAR_VALUE, uint8 stencilValue = 0) = 0; API_FUNCTION() virtual void ClearDepth(GPUTextureView* depthBuffer, float depthValue = GPU_DEPTH_MAX_VALUE, uint8 stencilValue = 0) = 0;
/// <summary> /// <summary>
/// Clears an unordered access buffer with a float value. /// Clears an unordered access buffer with a float value.
@@ -634,9 +638,10 @@ public:
/// <summary> /// <summary>
/// Sets the minimum and maximum depth values for depth bounds test. /// Sets the minimum and maximum depth values for depth bounds test.
/// </summary> /// </summary>
/// <remarks>Both values must be between 0.0 and 1.0, MinDepth must be less than or equal to MaxDepth.</remarks>
/// <param name="minDepth">The minimum value for depth bound test.</param> /// <param name="minDepth">The minimum value for depth bound test.</param>
/// <param name="maxDepth">The maximum value for depth bound test.</param> /// <param name="maxDepth">The maximum value for depth bound test.</param>
API_FUNCTION() virtual void SetDepthBounds(float minDepth, float maxDepth) = 0; API_FUNCTION() virtual void SetDepthBounds(float minDepth = 0.0f, float maxDepth = 1.0f) = 0;
public: public:
/// <summary> /// <summary>
+44 -2
View File
@@ -684,6 +684,20 @@ float RenderTools::TemporalHalton(int32 index, int32 base)
Float2 RenderTools::GetDepthBounds(const RenderView& view, const Float3& nearPoint, const Float3& farPoint) Float2 RenderTools::GetDepthBounds(const RenderView& view, const Float3& nearPoint, const Float3& farPoint)
{ {
#if FLAX_REVERSE_Z
Float3 viewNearPoint = Float3::Transform(nearPoint, view.View);
Float3 viewFarPoint = Float3::Transform(farPoint, view.View);
viewNearPoint.Z = Math::Max(viewNearPoint.Z, view.Near);
viewFarPoint.Z = Math::Min(viewFarPoint.Z, view.Far);
Float4 clipNearPoint = Matrix::TransformPosition(view.Projection, Float4(0, 0, viewNearPoint.Z, 1));
Float4 clipFarPoint = Matrix::TransformPosition(view.Projection, Float4(0, 0, viewFarPoint.Z, 1));
clipNearPoint /= clipNearPoint.W;
clipFarPoint /= clipFarPoint.W;
return Float2(clipNearPoint.Z, clipFarPoint.Z);
#else
// Point closest the view // Point closest the view
const Float4 nearPointClip = Matrix::TransformPosition(view.ViewProjection(), Float4(nearPoint, 1.0)); const Float4 nearPointClip = Matrix::TransformPosition(view.ViewProjection(), Float4(nearPoint, 1.0));
float nearDepth = nearPointClip.Z / nearPointClip.W; float nearDepth = nearPointClip.Z / nearPointClip.W;
@@ -700,8 +714,8 @@ Float2 RenderTools::GetDepthBounds(const RenderView& view, const Float3& nearPoi
nearDepth = Math::Clamp(nearDepth, 0.0f, 1.0f); nearDepth = Math::Clamp(nearDepth, 0.0f, 1.0f);
farDepth = Math::Clamp(farDepth, nearDepth, 1.0f); farDepth = Math::Clamp(farDepth, nearDepth, 1.0f);
// TODO: swap depths when using reversed depth buffer
return Float2(nearDepth, farDepth); return Float2(nearDepth, farDepth);
#endif
} }
Float2 RenderTools::GetDepthBounds(const RenderView& view, const BoundingSphere& bounds) Float2 RenderTools::GetDepthBounds(const RenderView& view, const BoundingSphere& bounds)
@@ -713,6 +727,24 @@ Float2 RenderTools::GetDepthBounds(const RenderView& view, const BoundingSphere&
Float2 RenderTools::GetDepthBounds(const RenderView& view, const Span<Float3>& points) Float2 RenderTools::GetDepthBounds(const RenderView& view, const Span<Float3>& points)
{ {
#if FLAX_REVERSE_Z
// Find min and max view depth range for list of points
float nearDepth = view.Far, farDepth = view.Near;
for (int32 i = 0; i < points.Length(); i++)
{
const Float3 viewPoint = Float3::Transform(points[i], view.View);
nearDepth = Math::Min(nearDepth, viewPoint.Z);
farDepth = Math::Max(farDepth, viewPoint.Z);
}
// Project end points to clip space
Float4 clipNearPoint = Matrix::TransformPosition(view.Projection, Float4(0, 0, nearDepth, 1));
Float4 clipFarPoint = Matrix::TransformPosition(view.Projection, Float4(0, 0, farDepth, 1));
clipNearPoint /= clipNearPoint.W;
clipFarPoint /= clipFarPoint.W;
return Float2(clipNearPoint.Z, clipFarPoint.Z);
#else
// Find min and max depth range for list of points // Find min and max depth range for list of points
float nearDepth = 1.0f, farDepth = 0.0f; float nearDepth = 1.0f, farDepth = 0.0f;
for (int32 i = 0; i < points.Length(); i++) for (int32 i = 0; i < points.Length(); i++)
@@ -729,8 +761,8 @@ Float2 RenderTools::GetDepthBounds(const RenderView& view, const Span<Float3>& p
nearDepth = Math::Clamp(nearDepth, 0.0f, 1.0f); nearDepth = Math::Clamp(nearDepth, 0.0f, 1.0f);
farDepth = Math::Clamp(farDepth, nearDepth, 1.0f); farDepth = Math::Clamp(farDepth, nearDepth, 1.0f);
// TODO: swap depths when using reversed depth buffer
return Float2(nearDepth, farDepth); return Float2(nearDepth, farDepth);
#endif
} }
Float2 RenderTools::GetDepthBounds(const RenderView& view, const BoundingBox& bounds) Float2 RenderTools::GetDepthBounds(const RenderView& view, const BoundingBox& bounds)
@@ -749,11 +781,21 @@ Float2 RenderTools::GetDepthBounds(const RenderView& view, const OrientedBoundin
float RenderTools::GetDepthBounds(const RenderView& view, const Float3& point, bool near) float RenderTools::GetDepthBounds(const RenderView& view, const Float3& point, bool near)
{ {
#if FLAX_REVERSE_Z
Float3 viewPoint = Float3::Transform(point, view.View);
viewPoint.Z = Math::Clamp(viewPoint.Z, view.Near, view.Far);
Float4 clipPoint = Matrix::TransformPosition(view.Projection, Float4(0, 0, viewPoint.Z, 1));
clipPoint /= clipPoint.W;
return clipPoint.Z;
#else
const Float4 pointClip = Matrix::TransformPosition(view.ViewProjection(), Float4(point, 1.0)); const Float4 pointClip = Matrix::TransformPosition(view.ViewProjection(), Float4(point, 1.0));
float depth = pointClip.Z / pointClip.W; float depth = pointClip.Z / pointClip.W;
if (depth >= 1.0f) if (depth >= 1.0f)
depth = near ? 0.0f : 1.0f; // Point is behind the view depth = near ? 0.0f : 1.0f; // Point is behind the view
return Math::Clamp(depth, 0.0f, 1.0f); return Math::Clamp(depth, 0.0f, 1.0f);
#endif
} }
float RenderTools::GetDepthBounds(const RenderView& view, float viewDistance, bool near) float RenderTools::GetDepthBounds(const RenderView& view, float viewDistance, bool near)
+6 -1
View File
@@ -173,7 +173,12 @@ public:
static Float2 GetDepthBounds(const RenderView& view, const OrientedBoundingBox& 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, const Float3& point, bool near);
static float GetDepthBounds(const RenderView& view, float viewDistance, 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 // Skip background/sky pixels from shading
#if FLAX_REVERSE_Z
static constexpr float DepthBoundMaxBackground = 0.0000001f;
#else
static constexpr float DepthBoundMaxBackground = 1.0f - 0.0000001f;
#endif
// Calculates error for a given render target format to reduce floating-point precision artifacts via QuantizeColor (from Noise.hlsl). // Calculates error for a given render target format to reduce floating-point precision artifacts via QuantizeColor (from Noise.hlsl).
static Float3 GetColorQuantizationError(PixelFormat format); static Float3 GetColorQuantizationError(PixelFormat format);
@@ -796,9 +796,8 @@ void GPUContextVulkan::OnDrawCall()
if (_psDirtyFlag && pipelineState && (_rtDepth || _rtCount)) if (_psDirtyFlag && pipelineState && (_rtDepth || _rtCount))
{ {
_psDirtyFlag = false; _psDirtyFlag = false;
const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer()->GetHandle();
const auto pipeline = pipelineState->GetState(_renderPass, _vertexLayout); const auto pipeline = pipelineState->GetState(_renderPass, _vertexLayout);
vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); vkCmdBindPipeline(cmdBuffer->GetHandle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
RENDER_STAT_PS_STATE_CHANGE(); RENDER_STAT_PS_STATE_CHANGE();
if (_depthBoundsEnable && (!_currentState || !_currentState->DepthBoundsEnable)) if (_depthBoundsEnable && (!_currentState || !_currentState->DepthBoundsEnable))
{ {
@@ -536,7 +536,7 @@ void AmbientOcclusionPass::Render(RenderContext& renderContext)
} }
// Apply // Apply
context->SetDepthBounds(0.0f, RenderTools::GetDepthBounds(renderContext.View, aoSettings.FadeOutDistance, false)); context->SetDepthBounds(GPU_DEPTH_BOUNDS_SWAP(GPU_DEPTH_MIN_VALUE, RenderTools::GetDepthBounds(renderContext.View, aoSettings.FadeOutDistance, false)));
context->BindSR(SSAO_TEXTURE_SLOT0, m_finalResults->ViewArray()); context->BindSR(SSAO_TEXTURE_SLOT0, m_finalResults->ViewArray());
context->SetViewportAndScissors((float)m_sizeX, (float)m_sizeY); context->SetViewportAndScissors((float)m_sizeX, (float)m_sizeY);
context->SetState(settings.SkipHalfPixels ? _psApplyHalf : _psApply); context->SetState(settings.SkipHalfPixels ? _psApplyHalf : _psApply);
+5 -5
View File
@@ -283,7 +283,7 @@ void LightPass::RenderLights(RenderContextBatch& renderContextBatch, GPUTextureV
if (_depthBounds) if (_depthBounds)
{ {
Float2 minMaxDepth = RenderTools::GetDepthBounds(view, BoundingSphere(light.Position, light.Radius)); Float2 minMaxDepth = RenderTools::GetDepthBounds(view, BoundingSphere(light.Position, light.Radius));
context->SetDepthBounds(minMaxDepth.X, minMaxDepth.Y); context->SetDepthBounds(GPU_DEPTH_BOUNDS_SWAP(minMaxDepth.X, minMaxDepth.Y));
} }
context->UpdateCB(cb0, &perLight); context->UpdateCB(cb0, &perLight);
context->BindCB(0, cb0); context->BindCB(0, cb0);
@@ -331,7 +331,7 @@ void LightPass::RenderLights(RenderContextBatch& renderContextBatch, GPUTextureV
if (_depthBounds) if (_depthBounds)
{ {
Float2 minMaxDepth = RenderTools::GetDepthBounds(view, BoundingSphere(light.Position, light.Radius)); Float2 minMaxDepth = RenderTools::GetDepthBounds(view, BoundingSphere(light.Position, light.Radius));
context->SetDepthBounds(minMaxDepth.X, minMaxDepth.Y); context->SetDepthBounds(GPU_DEPTH_BOUNDS_SWAP(minMaxDepth.X, minMaxDepth.Y));
} }
context->UpdateCB(cb0, &perLight); context->UpdateCB(cb0, &perLight);
context->BindCB(0, cb0); context->BindCB(0, cb0);
@@ -365,7 +365,7 @@ void LightPass::RenderLights(RenderContextBatch& renderContextBatch, GPUTextureV
// Calculate lighting // Calculate lighting
if (_depthBounds) if (_depthBounds)
context->SetDepthBounds(0.0f, RenderTools::DepthBoundMaxBackground); context->SetDepthBounds(GPU_DEPTH_BOUNDS_SWAP(GPU_DEPTH_MIN_VALUE, RenderTools::DepthBoundMaxBackground));
context->UpdateCB(cb0, &perLight); context->UpdateCB(cb0, &perLight);
context->BindCB(0, cb0); context->BindCB(0, cb0);
context->BindCB(1, cb1); context->BindCB(1, cb1);
@@ -398,7 +398,7 @@ void LightPass::RenderLights(RenderContextBatch& renderContextBatch, GPUTextureV
if (_depthBounds) if (_depthBounds)
{ {
Float2 minMaxDepth = RenderTools::GetDepthBounds(view, BoundingSphere(light.Position, light.Radius)); Float2 minMaxDepth = RenderTools::GetDepthBounds(view, BoundingSphere(light.Position, light.Radius));
context->SetDepthBounds(minMaxDepth.X, minMaxDepth.Y); context->SetDepthBounds(GPU_DEPTH_BOUNDS_SWAP(minMaxDepth.X, minMaxDepth.Y));
} }
context->UpdateCB(cb0, &perLight); context->UpdateCB(cb0, &perLight);
context->BindCB(0, cb0); context->BindCB(0, cb0);
@@ -411,7 +411,7 @@ void LightPass::RenderLights(RenderContextBatch& renderContextBatch, GPUTextureV
// Restore state // Restore state
if (_depthBounds) if (_depthBounds)
context->SetDepthBounds(0, 1); context->SetDepthBounds();
context->ResetRenderTarget(); context->ResetRenderTarget();
context->ResetSR(); context->ResetSR();
context->ResetCB(); context->ResetCB();
+4 -4
View File
@@ -347,7 +347,7 @@ void ReflectionsPass::Render(RenderContext& renderContext, GPUTextureView* light
// Setup depth bounds (if device supports it) // Setup depth bounds (if device supports it)
if (_depthBounds) if (_depthBounds)
context->SetDepthBounds(minMaxDepth.X, minMaxDepth.Y); context->SetDepthBounds(GPU_DEPTH_BOUNDS_SWAP(minMaxDepth.X, minMaxDepth.Y));
// Pack probe properties buffer // Pack probe properties buffer
probe.SetShaderData(data.PData); probe.SetShaderData(data.PData);
@@ -368,7 +368,7 @@ void ReflectionsPass::Render(RenderContext& renderContext, GPUTextureView* light
context->UnBindSR(4); context->UnBindSR(4);
context->ResetRenderTarget(); context->ResetRenderTarget();
if (_depthBounds) if (_depthBounds)
context->SetDepthBounds(0, 1); context->SetDepthBounds();
} }
// Screen Space Reflections pass // Screen Space Reflections pass
@@ -417,7 +417,7 @@ void ReflectionsPass::Render(RenderContext& renderContext, GPUTextureView* light
if (_depthBounds) if (_depthBounds)
{ {
context->SetRenderTarget(depthBufferRTV, lightBuffer); context->SetRenderTarget(depthBufferRTV, lightBuffer);
context->SetDepthBounds(0, RenderTools::DepthBoundMaxBackground); context->SetDepthBounds(GPU_DEPTH_BOUNDS_SWAP(GPU_DEPTH_MIN_VALUE, RenderTools::DepthBoundMaxBackground));
} }
else else
context->SetRenderTarget(lightBuffer); context->SetRenderTarget(lightBuffer);
@@ -430,7 +430,7 @@ void ReflectionsPass::Render(RenderContext& renderContext, GPUTextureView* light
context->SetState(_psCombinePass); context->SetState(_psCombinePass);
context->DrawFullscreenTriangle(); context->DrawFullscreenTriangle();
if (_depthBounds) if (_depthBounds)
context->SetDepthBounds(0, 1); context->SetDepthBounds();
} }
RenderTargetPool::Release(ssrBuffer); RenderTargetPool::Release(ssrBuffer);
+3 -7
View File
@@ -1147,11 +1147,7 @@ void ShadowsPass::SetupLight(ShadowsCustomBuffer& shadows, RenderContext& render
void ShadowsPass::ClearShadowMapTile(GPUContext* context, GPUConstantBuffer* quadShaderCB, QuadShaderData& quadShaderData) const void ShadowsPass::ClearShadowMapTile(GPUContext* context, GPUConstantBuffer* quadShaderCB, QuadShaderData& quadShaderData) const
{ {
// Color.r is used by PS_DepthClear in Quad shader to clear depth // Color.r is used by PS_DepthClear in Quad shader to clear depth
#if FLAX_REVERSE_Z quadShaderData.Color = GPU_DEPTH_MAX_VALUE;
quadShaderData.Color = Float4::Zero;
#else
quadShaderData.Color = Float4::One;
#endif
context->UpdateCB(quadShaderCB, &quadShaderData); context->UpdateCB(quadShaderCB, &quadShaderData);
context->BindCB(0, quadShaderCB); context->BindCB(0, quadShaderCB);
@@ -1753,8 +1749,8 @@ void ShadowsPass::RenderShadowMask(RenderContextBatch& renderContextBatch, Rende
if (light.IsPointLight || light.IsSpotLight) if (light.IsPointLight || light.IsSpotLight)
minMaxDepth = RenderTools::GetDepthBounds(view, BoundingSphere(light.Position, ((RenderLocalLightData&)light).Radius)); minMaxDepth = RenderTools::GetDepthBounds(view, BoundingSphere(light.Position, ((RenderLocalLightData&)light).Radius));
else //if (light.IsDirectionalLight) else //if (light.IsDirectionalLight)
minMaxDepth = Float2(0.0f, RenderTools::DepthBoundMaxBackground); minMaxDepth = Float2(GPU_DEPTH_MIN_VALUE, RenderTools::DepthBoundMaxBackground);
context->SetDepthBounds(minMaxDepth.X, minMaxDepth.Y); context->SetDepthBounds(GPU_DEPTH_BOUNDS_SWAP(minMaxDepth.X, minMaxDepth.Y));
} }
if (light.IsPointLight) if (light.IsPointLight)
{ {