Add half-res DDGI reflections resolving to optimzie rendering on low-end devices

This commit is contained in:
2026-07-20 12:06:13 +02:00
parent ac357fd2b9
commit 078a45c4b7
4 changed files with 54 additions and 16 deletions
@@ -37,6 +37,7 @@ void GlobalIlluminationSettings::BlendWith(GlobalIlluminationSettings& other, fl
BLEND_COL(FallbackIrradiance);
BLEND_ENUM(IndirectResolution);
BLEND_ENUM(Reflections);
BLEND_ENUM(ReflectionsResolution);
}
void BloomSettings::BlendWith(BloomSettings& other, float weight)
+12 -1
View File
@@ -365,10 +365,15 @@ API_ENUM(Attributes="Flags") enum class GlobalIlluminationSettingsOverride : int
/// </summary>
Reflections = 1 << 8,
/// <summary>
/// Overrides <see cref="GlobalIlluminationSettings.ReflectionsResolution"/> property.
/// </summary>
ReflectionsResolution = 1 << 9,
/// <summary>
/// All properties.
/// </summary>
All = Mode | Intensity | TemporalResponse | Distance | FallbackIrradiance | BounceIntensity | IndirectShadowsStrength | IndirectResolution | Reflections,
All = Mode | Intensity | TemporalResponse | Distance | FallbackIrradiance | BounceIntensity | IndirectShadowsStrength | IndirectResolution | Reflections | ReflectionsResolution,
};
/// <summary>
@@ -440,6 +445,12 @@ API_STRUCT() struct FLAXENGINE_API GlobalIlluminationSettings : ISerializable
API_FIELD(Attributes = "EditorOrder(100), PostProcessSetting((int)GlobalIlluminationSettingsOverride.Reflections)")
ReflectionsMode Reflections = ReflectionsMode::EnvironmentProbes;
/// <summary>
/// The indirect specular reflections render resolution. Full gives better quality, but half improves performance.
/// </summary>
API_FIELD(Attributes = "EditorOrder(110), PostProcessSetting((int)GlobalIlluminationSettingsOverride.ReflectionsResolution)")
ResolutionMode ReflectionsResolution = ResolutionMode::Full;
public:
/// <summary>
/// Blends the settings using given weight.
@@ -75,6 +75,9 @@ GPU_CB_STRUCT(Data0 {
float TestValue;
Float3 QuantizationError;
int32 FrameIndexMod8;
Float2 Padding0;
float ResolveDitherScaleIndirect;
float ResolveDitherScaleSpecular;
});
GPU_CB_STRUCT(Data1 {
@@ -182,6 +185,19 @@ public:
}
};
void InitData0Shared(const RenderContext& renderContext, const DDGICustomBuffer& ddgiData, Data0& data)
{
data.DDGI = ddgiData.Result.Constants;
data.TestValue = Graphics::TestValue;
data.TemporalTime = renderContext.List->Setup.UseTemporalAAJitter ? RenderTools::ComputeTemporalTime() : 0.0f;
data.FrameIndexMod8 = (int32)(Engine::FrameCount % 8);
auto& settings = renderContext.List->Settings.GlobalIllumination;
constexpr float DitherScaleHalfRes = 0.1f; // Hardcoded to reduce noise at half-res due to TAA not being able to filter this out (in future BilateralUpscale could try smooth it)
data.ResolveDitherScaleIndirect = settings.IndirectResolution == ResolutionMode::Full ? 1.0f : DitherScaleHalfRes;
data.ResolveDitherScaleSpecular = settings.ReflectionsResolution == ResolutionMode::Full ? 1.0f : DitherScaleHalfRes;
GBufferPass::SetInputs(renderContext.View, data.GBuffer);
}
void CalculateVolumeRandomRotation(Matrix3x3& matrix)
{
// Reference: James Arvo's algorithm Graphics Gems 3 (pages 117-120)
@@ -650,7 +666,6 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont
Quaternion::RotationMatrix(raysRotationMatrix, raysRotation);
raysRotation.Conjugate();
data.DDGI = ddgiData.Result.Constants;
data.GlobalSDF = bindingDataSDF.Constants;
data.GlobalSurfaceAtlas = bindingDataSurfaceAtlas.Constants;
data.ProbesCount = data.DDGI.ProbesCounts[0] * data.DDGI.ProbesCounts[1] * data.DDGI.ProbesCounts[2];
@@ -660,12 +675,10 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont
auto& cascade = ddgiData.Cascades[cascadeIndex];
data.ProbeScrollClears[cascadeIndex] = Int4(cascade.ProbeScrollClears, 0);
}
data.TemporalTime = renderContext.List->Setup.UseTemporalAAJitter ? RenderTools::ComputeTemporalTime() : 0.0f;
data.ViewDir = renderContext.View.Direction;
data.SkyboxIntensity = renderContext.List->Sky ? renderContext.List->Sky->GetIndirectLightingIntensity() : 1.0f;
data.TestValue = Graphics::TestValue;
data.QuantizationError = RenderTools::GetColorQuantizationError(ddgiData.ProbesIrradiance->Format());
data.FrameIndexMod8 = (int32)(Engine::FrameCount % 8);
InitData0Shared(renderContext, ddgiData, data);
GBufferPass::SetInputs(renderContext.View, data.GBuffer);
context->UpdateCB(_cb0, &data);
context->BindCB(0, _cb0);
@@ -899,10 +912,7 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
if (!render)
{
Data0 data;
data.DDGI = ddgiData->Result.Constants;
data.TemporalTime = renderContext.List->Setup.UseTemporalAAJitter ? RenderTools::ComputeTemporalTime() : 0.0f;
data.FrameIndexMod8 = (int32)(Engine::FrameCount % 8);
GBufferPass::SetInputs(renderContext.View, data.GBuffer);
InitData0Shared(renderContext, *ddgiData, data);
context->UpdateCB(_cb0, &data);
context->BindCB(0, _cb0);
}
@@ -1051,10 +1061,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderReflections(RenderContext& rend
if (!render)
{
Data0 data;
data.DDGI = ddgiData->Result.Constants;
data.TemporalTime = renderContext.List->Setup.UseTemporalAAJitter ? RenderTools::ComputeTemporalTime() : 0.0f;
data.FrameIndexMod8 = (int32)(Engine::FrameCount % 8);
GBufferPass::SetInputs(renderContext.View, data.GBuffer);
InitData0Shared(renderContext, *ddgiData, data);
context->UpdateCB(_cb0, &data);
context->BindCB(0, _cb0);
}
@@ -1066,6 +1073,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderReflections(RenderContext& rend
context->BindSR(5, ddgiData->Result.ProbesDistance);
context->BindSR(6, ddgiData->Result.ProbesRadiance);
auto& settings = renderContext.List->Settings.GlobalIllumination;
if (settings.ReflectionsResolution == ResolutionMode::Full || !MultiScaler::Instance()->IsReady())
{
// Full-res
auto rtAction = GPUDrawPassAction::Store;
@@ -1075,6 +1083,21 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderReflections(RenderContext& rend
context->SetState(_psSpecularLighting);
context->DrawFullscreenTriangle();
}
else
{
// Upscale
auto width = RenderTools::GetResolution((int32)renderContext.View.ScreenSize.X, settings.ReflectionsResolution);
auto height = RenderTools::GetResolution((int32)renderContext.View.ScreenSize.Y, settings.ReflectionsResolution);
auto temp = RenderTargetPool::Get(GPUTextureDescription::New2D(width, height, reflectionsBuffer->GetFormat(), GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget));
context->SetViewportAndScissors((float)width, (float)height);
context->SetRenderTarget(temp->View());
context->SetState(_psSpecularLighting);
context->DrawFullscreenTriangle();
context->ResetRenderTarget();
MultiScaler::Instance()->BilateralUpscale(context, Viewport(Float2(renderContext.View.ScreenSize)), temp, reflectionsBuffer, renderContext.Buffers->DepthBuffer, renderContext.Buffers->GBuffer1);
RenderTargetPool::Release(temp);
context->SetViewportAndScissors(renderContext.View.ScreenSize.X, renderContext.View.ScreenSize.Y);
}
context->ResetSR();
context->ResetRenderTarget();
}
+6 -3
View File
@@ -52,6 +52,9 @@ float3 ViewDir;
float TestValue;
float3 QuantizationError;
uint FrameIndexMod8;
float2 Padding0;
float ResolveDitherScaleIndirect;
float ResolveDitherScaleSpecular;
META_CB_END
META_CB_BEGIN(1, Data1)
@@ -969,7 +972,7 @@ Texture2D<snorm float4> ProbesData : register(t4);
Texture2D<float4> ProbesDistance : register(t5);
// Shared code for both irradiance and specular sampling
#define DDGI_GET_DITHER RandN2(input.TexCoord + TemporalTime).x
#define DDGI_GET_DITHER (RandN2(input.TexCoord + TemporalTime).x)
#define DDGI_GET_SAMPLE_POS gBuffer.WorldPos + gBuffer.Normal * (dither * 0.1f + 0.1f)
#endif
@@ -991,7 +994,7 @@ float4 PS_IndirectLighting(Quad_VS2PS input) : SV_Target0
return float4(0, 0, 0, 0);
// Sample irradiance
float dither = DDGI_GET_DITHER;
float dither = DDGI_GET_DITHER * ResolveDitherScaleIndirect;
float3 samplePos = DDGI_GET_SAMPLE_POS;
float3 irradiance = SampleDDGIIrradiance(DDGI, ProbesData, ProbesDistance, ProbesIrradiance, samplePos, gBuffer.Normal, DDGI_DEFAULT_BIAS, dither);
@@ -1018,7 +1021,7 @@ float4 PS_SpecularLighting(Quad_VS2PS input) : SV_Target0
return float4(0, 0, 0, 0);
// Sample specular reflection
float dither = DDGI_GET_DITHER;
float dither = DDGI_GET_DITHER * ResolveDitherScaleSpecular;
float3 samplePos = DDGI_GET_SAMPLE_POS;
float3 specular = SampleDDGISpecular(DDGI, ProbesData, ProbesDistance, ProbesRadiance, samplePos, gBuffer.Normal, gBuffer.Roughness, DDGI_DEFAULT_BIAS, dither);