Add roughness-based filtering when sampling DDGI probes
This commit is contained in:
Binary file not shown.
@@ -75,9 +75,8 @@ GPU_CB_STRUCT(Data0 {
|
||||
float TestValue;
|
||||
Float3 QuantizationError;
|
||||
int32 FrameIndexMod8;
|
||||
Float2 ResolveDitherScale;
|
||||
Float2 Padding0;
|
||||
float ResolveDitherScaleIndirect;
|
||||
float ResolveDitherScaleSpecular;
|
||||
});
|
||||
|
||||
GPU_CB_STRUCT(Data1 {
|
||||
@@ -185,16 +184,18 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
void InitData0Shared(const RenderContext& renderContext, const DDGICustomBuffer& ddgiData, Data0& data)
|
||||
void InitData0(const RenderContext& renderContext, const DDGICustomBuffer& ddgiData, Data0& data)
|
||||
{
|
||||
Platform::MemoryClear((byte*)&data + sizeof(data.DDGI), sizeof(Data0) - sizeof(data.DDGI));
|
||||
data.DDGI = ddgiData.Result.Constants;
|
||||
data.ProbesCount = data.DDGI.ProbesCounts[0] * data.DDGI.ProbesCounts[1] * data.DDGI.ProbesCounts[2];
|
||||
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;
|
||||
data.ResolveDitherScale.X = settings.IndirectResolution == ResolutionMode::Full ? 1.0f : DitherScaleHalfRes;
|
||||
data.ResolveDitherScale.Y = settings.ReflectionsResolution == ResolutionMode::Full ? 1.0f : DitherScaleHalfRes;
|
||||
GBufferPass::SetInputs(renderContext.View, data.GBuffer);
|
||||
}
|
||||
|
||||
@@ -301,6 +302,8 @@ bool DynamicDiffuseGlobalIlluminationPass::setupResources()
|
||||
|
||||
// Initialize resources
|
||||
const auto shader = _shader->GPU;
|
||||
CHECK_INVALID_SHADER_PASS_CB_SIZE(shader, 0, Data0);
|
||||
CHECK_INVALID_SHADER_PASS_CB_SIZE(shader, 1, Data1);
|
||||
_cb0 = shader->GetCB(0);
|
||||
_cb1 = shader->GetCB(1);
|
||||
if (!_cb0 || !_cb1)
|
||||
@@ -658,6 +661,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont
|
||||
ddgiData.Result.ProbesRadiance = ddgiData.ProbesRadiance ? ddgiData.ProbesRadiance->View() : nullptr;
|
||||
|
||||
Data0 data;
|
||||
InitData0(renderContext, ddgiData, data);
|
||||
|
||||
// Compute random rotation matrix for probe rays orientation (randomized every frame)
|
||||
Matrix3x3 raysRotationMatrix;
|
||||
@@ -668,7 +672,6 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont
|
||||
|
||||
data.GlobalSDF = bindingDataSDF.Constants;
|
||||
data.GlobalSurfaceAtlas = bindingDataSurfaceAtlas.Constants;
|
||||
data.ProbesCount = data.DDGI.ProbesCounts[0] * data.DDGI.ProbesCounts[1] * data.DDGI.ProbesCounts[2];
|
||||
data.ResetBlend = clear ? 1.0f : 0.0f;
|
||||
for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++)
|
||||
{
|
||||
@@ -678,8 +681,6 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont
|
||||
data.ViewDir = renderContext.View.Direction;
|
||||
data.SkyboxIntensity = renderContext.List->Sky ? renderContext.List->Sky->GetIndirectLightingIntensity() : 1.0f;
|
||||
data.QuantizationError = RenderTools::GetColorQuantizationError(ddgiData.ProbesIrradiance->Format());
|
||||
InitData0Shared(renderContext, ddgiData, data);
|
||||
GBufferPass::SetInputs(renderContext.View, data.GBuffer);
|
||||
context->UpdateCB(_cb0, &data);
|
||||
context->BindCB(0, _cb0);
|
||||
}
|
||||
@@ -911,8 +912,9 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
|
||||
#endif
|
||||
if (!render)
|
||||
{
|
||||
// Bind constants if not set to draw DDGI
|
||||
Data0 data;
|
||||
InitData0Shared(renderContext, *ddgiData, data);
|
||||
InitData0(renderContext, *ddgiData, data);
|
||||
context->UpdateCB(_cb0, &data);
|
||||
context->BindCB(0, _cb0);
|
||||
}
|
||||
@@ -1060,8 +1062,9 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderReflections(RenderContext& rend
|
||||
PROFILE_GPU_CPU_NAMED("Specular Lighting");
|
||||
if (!render)
|
||||
{
|
||||
// Bind constants if not set to draw DDGI
|
||||
Data0 data;
|
||||
InitData0Shared(renderContext, *ddgiData, data);
|
||||
InitData0(renderContext, *ddgiData, data);
|
||||
context->UpdateCB(_cb0, &data);
|
||||
context->BindCB(0, _cb0);
|
||||
}
|
||||
|
||||
+32
-14
@@ -160,7 +160,7 @@ float2 GetDDGIProbeUV(DDGIData data, uint cascadeIndex, uint probeIndex, float2
|
||||
float probeTexelSize = resolution + 2.0f;
|
||||
float2 textureSize = float2(data.ProbesCounts.x * data.ProbesCounts.y, data.ProbesCounts.z * data.CascadesCount) * probeTexelSize;
|
||||
float2 uv = float2(coords.x * probeTexelSize, coords.y * probeTexelSize) + (probeTexelSize * 0.5f);
|
||||
uv += octahedralCoords.xy * (resolution * 0.5f);
|
||||
uv += octahedralCoords * (resolution * 0.5f);
|
||||
uv /= textureSize;
|
||||
return uv;
|
||||
}
|
||||
@@ -275,11 +275,11 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D<snorm float4> probes
|
||||
{
|
||||
bool invalidCascade = cascade.CascadeIndex >= data.CascadesCount;
|
||||
cascade.CascadeIndex = min(cascade.CascadeIndex, data.CascadesCount - 1);
|
||||
float2 octahedralCoords = GetOctahedralCoords(worldNormal);
|
||||
#if DDGI_FALLBACK_OUTER_DEDICATED_PROBE
|
||||
if (invalidCascade)
|
||||
{
|
||||
// Sample a special probe as a fallback for ambient GI outside the last cascade
|
||||
float2 octahedralCoords = GetOctahedralCoords(worldNormal);
|
||||
float2 uv = GetDDGIProbeUV(data, cascade.CascadeIndex, 0, octahedralCoords, DDGI_PROBE_RESOLUTION_IRRADIANCE);
|
||||
float3 probeIrradiance = probesIrradiance.SampleLevel(SamplerLinearClamp, uv, 0).rgb;
|
||||
#if DDGI_SRGB_BLENDING == 1
|
||||
@@ -301,7 +301,6 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D<snorm float4> probes
|
||||
DDGIProbeSample probe = SampleDDGIProbe(data, probesData, probesDistance, worldPosition, worldNormal, cascade, base, i, fallbacks);
|
||||
|
||||
// Sample irradiance texture
|
||||
float2 octahedralCoords = GetOctahedralCoords(worldNormal);
|
||||
float2 uv = GetDDGIProbeUV(data, cascade.CascadeIndex, probe.ProbeIndex, octahedralCoords, DDGI_PROBE_RESOLUTION_IRRADIANCE);
|
||||
float3 probeIrradiance = probesIrradiance.SampleLevel(SamplerLinearClamp, uv, 0).rgb;
|
||||
#if DDGI_SRGB_BLENDING == 1
|
||||
@@ -357,7 +356,33 @@ float3 DDGIHash3D(float3 p)
|
||||
return frac(frac(p.xxy * p.yzz) * 2.0 - 1.0);
|
||||
}
|
||||
|
||||
float3 SampleDDGISpecularCascade(DDGIData data, Texture2D<snorm float4> probesData, Texture2D<float4> probesDistance, Texture2D<float4> probesRadiance, float3 worldPosition, float3 worldNormal, float3 reflection, DDGICascadeSampling cascade)
|
||||
float3 FilterProbeRadiance(DDGIData data, Texture2D<float4> probesRadiance, float roughness, uint cascadeIndex, uint probeIndex, float3 direction)
|
||||
{
|
||||
uint2 coords = GetDDGIProbeTexelCoords(data, cascadeIndex, probeIndex);
|
||||
const float probeTexelSize = DDGI_PROBE_RESOLUTION_RADIANCE + 2.0f;
|
||||
float2 uv = float2(coords.x * probeTexelSize, coords.y * probeTexelSize) + (probeTexelSize * 0.5f);
|
||||
uv += GetOctahedralCoords(direction) * (DDGI_PROBE_RESOLUTION_RADIANCE * 0.5f);
|
||||
float2 textureSize = float2(data.ProbesCounts.x * data.ProbesCounts.y, data.ProbesCounts.z * data.CascadesCount) * probeTexelSize;
|
||||
uv /= textureSize;
|
||||
float3 radiance = probesRadiance.SampleLevel(SamplerLinearClamp, uv, 0).rgb;
|
||||
|
||||
// Box-blur filter for roughness (not physically correct, but cheap)
|
||||
float2 uvBase = float2(coords.x * probeTexelSize, coords.y * probeTexelSize) + 1; // The first texel of the probe (skip 1px padding)
|
||||
float2 uvMin = uvBase / textureSize;
|
||||
float2 uvMax = (uvBase + float2(DDGI_PROBE_RESOLUTION_RADIANCE, DDGI_PROBE_RESOLUTION_RADIANCE)) / textureSize;
|
||||
float2 filterSize = (float2)1.0f / textureSize;
|
||||
float3 radiance00 = probesRadiance.SampleLevel(SamplerLinearClamp, clamp(uv - filterSize, uvMin, uvMax), 0).rgb;
|
||||
float3 radiance10 = probesRadiance.SampleLevel(SamplerLinearClamp, clamp(uv + float2(filterSize.x, -filterSize.y), uvMin, uvMax), 0).rgb;
|
||||
float3 radiance01 = probesRadiance.SampleLevel(SamplerLinearClamp, clamp(uv + float2(-filterSize.x, filterSize.y), uvMin, uvMax), 0).rgb;
|
||||
float3 radiance11 = probesRadiance.SampleLevel(SamplerLinearClamp, clamp(uv + filterSize, uvMin, uvMax), 0).rgb;
|
||||
float3 radianceRough = (radiance00 + radiance10 + radiance01 + radiance11) * 0.25f;
|
||||
float alpha = (roughness - 0.4f) / 0.6f; // Remap [0.4; 1] to [0; 1]
|
||||
radiance = lerp(radiance, radianceRough, saturate(alpha));
|
||||
|
||||
return radiance;
|
||||
}
|
||||
|
||||
float3 SampleDDGISpecularCascade(DDGIData data, Texture2D<snorm float4> probesData, Texture2D<float4> probesDistance, Texture2D<float4> probesRadiance, float3 worldPosition, float3 worldNormal, float roughness, float3 reflection, DDGICascadeSampling cascade)
|
||||
{
|
||||
bool invalidCascade = cascade.CascadeIndex >= data.CascadesCount;
|
||||
cascade.CascadeIndex = min(cascade.CascadeIndex, data.CascadesCount - 1);
|
||||
@@ -365,10 +390,7 @@ float3 SampleDDGISpecularCascade(DDGIData data, Texture2D<snorm float4> probesDa
|
||||
if (invalidCascade)
|
||||
{
|
||||
// Sample a special probe as a fallback for ambient sky reflection outside the last cascade
|
||||
float2 octahedralCoords = GetOctahedralCoords(reflection);
|
||||
float2 uv = GetDDGIProbeUV(data, cascade.CascadeIndex, 0, octahedralCoords, DDGI_PROBE_RESOLUTION_RADIANCE);
|
||||
float3 probeRadiance = probesRadiance.SampleLevel(SamplerLinearClamp, uv, 0).rgb;
|
||||
return probeRadiance;
|
||||
return FilterProbeRadiance(data, probesRadiance, roughness, cascade.CascadeIndex, 0, reflection);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -390,11 +412,7 @@ float3 SampleDDGISpecularCascade(DDGIData data, Texture2D<snorm float4> probesDa
|
||||
sampleVector += worldNoise;
|
||||
|
||||
// Sample radiance texture
|
||||
float2 octahedralCoords = GetOctahedralCoords(sampleVector);
|
||||
float2 uv = GetDDGIProbeUV(data, cascade.CascadeIndex, probe.ProbeIndex, octahedralCoords, DDGI_PROBE_RESOLUTION_RADIANCE);
|
||||
float3 probeRadiance = probesRadiance.SampleLevel(SamplerLinearClamp, uv, 0).rgb;
|
||||
// TODO: filter radiance based on roughness
|
||||
// TODO: or maybe build mip-chain for updated probes radiance texture and sample it based on roughness like env probes?
|
||||
float3 probeRadiance = FilterProbeRadiance(data, probesRadiance, roughness, cascade.CascadeIndex, probe.ProbeIndex, sampleVector);
|
||||
|
||||
// Accumulate weighted radiance
|
||||
totalRadiance += float4(probeRadiance * probe.Weights.x, probe.Weights.x);
|
||||
@@ -516,7 +534,7 @@ float3 SampleDDGISpecular(DDGIData data, Texture2D<snorm float4> probesData, Tex
|
||||
|
||||
// Sample cascade
|
||||
float3 reflection = reflect(normalize(worldPosition - data.ViewPos), worldNormal);
|
||||
float3 specular = SampleDDGISpecularCascade(data, probesData, probesDistance, probesRadiance, worldPosition, worldNormal, reflection, cascade);
|
||||
float3 specular = SampleDDGISpecularCascade(data, probesData, probesDistance, probesRadiance, worldPosition, worldNormal, roughness, reflection, cascade);
|
||||
|
||||
return specular;
|
||||
}
|
||||
|
||||
@@ -52,9 +52,8 @@ float3 ViewDir;
|
||||
float TestValue;
|
||||
float3 QuantizationError;
|
||||
uint FrameIndexMod8;
|
||||
float2 ResolveDitherScale;
|
||||
float2 Padding0;
|
||||
float ResolveDitherScaleIndirect;
|
||||
float ResolveDitherScaleSpecular;
|
||||
META_CB_END
|
||||
|
||||
META_CB_BEGIN(1, Data1)
|
||||
@@ -994,7 +993,7 @@ float4 PS_IndirectLighting(Quad_VS2PS input) : SV_Target0
|
||||
return float4(0, 0, 0, 0);
|
||||
|
||||
// Sample irradiance
|
||||
float dither = DDGI_GET_DITHER * ResolveDitherScaleIndirect;
|
||||
float dither = DDGI_GET_DITHER * ResolveDitherScale.x;
|
||||
float3 samplePos = DDGI_GET_SAMPLE_POS;
|
||||
float3 irradiance = SampleDDGIIrradiance(DDGI, ProbesData, ProbesDistance, ProbesIrradiance, samplePos, gBuffer.Normal, DDGI_DEFAULT_BIAS, dither);
|
||||
|
||||
@@ -1021,7 +1020,7 @@ float4 PS_SpecularLighting(Quad_VS2PS input) : SV_Target0
|
||||
return float4(0, 0, 0, 0);
|
||||
|
||||
// Sample specular reflection
|
||||
float dither = DDGI_GET_DITHER * ResolveDitherScaleSpecular;
|
||||
float dither = DDGI_GET_DITHER * ResolveDitherScale.y;
|
||||
float3 samplePos = DDGI_GET_SAMPLE_POS;
|
||||
float3 specular = SampleDDGISpecular(DDGI, ProbesData, ProbesDistance, ProbesRadiance, samplePos, gBuffer.Normal, gBuffer.Roughness, DDGI_DEFAULT_BIAS, dither);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user