Refactor DDGI fallback radiance to use alpha for blending between fixed color and color at snapped location of the last cascade
This means artists don't need to adjust the value anymore as it can cover vista geometry with GI at last cascade borders.
This commit is contained in:
@@ -191,7 +191,7 @@ bool GlobalIlluminationFeature::Bind(MaterialShader::BindParameters& params, Spa
|
||||
{
|
||||
// Unbind SRVs to prevent issues
|
||||
data.DDGI.CascadesCount = 0;
|
||||
data.DDGI.FallbackIrradiance = Float3::Zero;
|
||||
data.DDGI.FallbackIrradiance = Float4::Zero;
|
||||
params.GPUContext->UnBindSR(srv + 0);
|
||||
params.GPUContext->UnBindSR(srv + 1);
|
||||
params.GPUContext->UnBindSR(srv + 2);
|
||||
|
||||
@@ -378,7 +378,7 @@ API_STRUCT() struct FLAXENGINE_API GlobalIlluminationSettings : ISerializable
|
||||
/// The irradiance lighting outside the GI range used as a fallback to prevent pure-black scene outside the Global Illumination range.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(40), PostProcessSetting((int)GlobalIlluminationSettingsOverride.FallbackIrradiance)")
|
||||
Color FallbackIrradiance = Color::Black;
|
||||
Color FallbackIrradiance = Color::Transparent;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
|
||||
@@ -328,7 +328,6 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont
|
||||
const float indirectLightingIntensity = settings.Intensity;
|
||||
const float probeHistoryWeight = Math::Clamp(settings.TemporalResponse, 0.0f, 0.98f);
|
||||
const float distance = settings.Distance;
|
||||
const Color fallbackIrradiance = settings.FallbackIrradiance;
|
||||
|
||||
// Automatically calculate amount of cascades to cover the GI distance at the current probes spacing
|
||||
const int32 idealProbesCount = 20; // Ideal amount of probes per-cascade to try to fit in order to cover whole distance
|
||||
@@ -518,7 +517,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont
|
||||
ddgiData.Result.Constants.ProbeHistoryWeight = probeHistoryWeight;
|
||||
ddgiData.Result.Constants.IrradianceGamma = 1.5f;
|
||||
ddgiData.Result.Constants.IndirectLightingIntensity = indirectLightingIntensity;
|
||||
ddgiData.Result.Constants.FallbackIrradiance = fallbackIrradiance.ToFloat3() * fallbackIrradiance.A;
|
||||
ddgiData.Result.Constants.FallbackIrradiance = settings.FallbackIrradiance.ToFloat4();
|
||||
ddgiData.Result.ProbesData = ddgiData.ProbesData->View();
|
||||
ddgiData.Result.ProbesDistance = ddgiData.ProbesDistance->View();
|
||||
ddgiData.Result.ProbesIrradiance = ddgiData.ProbesIrradiance->View();
|
||||
|
||||
@@ -25,8 +25,7 @@ public:
|
||||
float IndirectLightingIntensity;
|
||||
Float3 ViewPos;
|
||||
uint32 RaysCount;
|
||||
Float3 FallbackIrradiance;
|
||||
float Padding0;
|
||||
Float4 FallbackIrradiance;
|
||||
});
|
||||
|
||||
// Binding data for the GPU.
|
||||
|
||||
@@ -44,8 +44,7 @@ struct DDGIData
|
||||
float IndirectLightingIntensity;
|
||||
float3 ViewPos;
|
||||
uint RaysCount;
|
||||
float3 FallbackIrradiance;
|
||||
float Padding0;
|
||||
float4 FallbackIrradiance;
|
||||
};
|
||||
|
||||
uint GetDDGIProbeIndex(DDGIData data, uint3 probeCoords)
|
||||
@@ -164,6 +163,8 @@ float2 GetDDGIProbeUV(DDGIData data, uint cascadeIndex, uint probeIndex, float2
|
||||
|
||||
float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D<snorm float4> probesData, Texture2D<float4> probesDistance, Texture2D<float4> probesIrradiance, float3 worldPosition, float3 worldNormal, uint cascadeIndex, float3 probesOrigin, float3 probesExtent, float probesSpacing, float3 biasedWorldPosition)
|
||||
{
|
||||
bool invalidCascade = cascadeIndex >= data.CascadesCount;
|
||||
cascadeIndex = min(cascadeIndex, data.CascadesCount - 1);
|
||||
uint3 probeCoordsEnd = data.ProbesCounts - uint3(1, 1, 1);
|
||||
uint3 baseProbeCoords = clamp(uint3((worldPosition - probesOrigin + probesExtent) / probesSpacing), uint3(0, 0, 0), probeCoordsEnd);
|
||||
|
||||
@@ -190,11 +191,11 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D<snorm float4> probes
|
||||
uint3 fallbackCoords = DDGI_FALLBACK_COORDS_DECODE(probeData);
|
||||
float fallbackToProbeDist = length((float3)probeCoords - (float3)fallbackCoords);
|
||||
useVisibility = fallbackToProbeDist <= 1.0f; // Skip visibility test that blocks too far probes due to limiting max distance to 1.5 of probe spacing
|
||||
if (fallbackToProbeDist > 2.0f)
|
||||
minWight = 1.0f;
|
||||
if (fallbackToProbeDist > 2.0f) minWight = 1.0f;
|
||||
probeCoords = fallbackCoords;
|
||||
probeIndex = GetDDGIScrollingProbeIndex(data, cascadeIndex, fallbackCoords);
|
||||
probeData = LoadDDGIProbeData(data, probesData, cascadeIndex, probeIndex);
|
||||
//if (DecodeDDGIProbeState(probeData) == DDGI_PROBE_STATE_INACTIVE) continue;
|
||||
}
|
||||
|
||||
// Calculate probe position
|
||||
@@ -264,7 +265,8 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D<snorm float4> probes
|
||||
{
|
||||
// Normalize irradiance
|
||||
//irradiance.rgb /= irradiance.a;
|
||||
irradiance.rgb /= lerp(1, irradiance.a, saturate(irradiance.a * irradiance.a + 0.9f));
|
||||
//irradiance.rgb /= lerp(1, irradiance.a, saturate(irradiance.a * irradiance.a + 0.9f));
|
||||
irradiance.rgb /= invalidCascade ? irradiance.a : lerp(1, irradiance.a, saturate(irradiance.a * irradiance.a + 0.9f));
|
||||
#if DDGI_SRGB_BLENDING
|
||||
irradiance.rgb *= irradiance.rgb;
|
||||
#endif
|
||||
@@ -319,16 +321,13 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D<snorm float4> probesData, T
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
if (cascadeIndex == data.CascadesCount)
|
||||
return data.FallbackIrradiance;
|
||||
|
||||
// Sample cascade
|
||||
float3 result = SampleDDGIIrradianceCascade(data, probesData, probesDistance, probesIrradiance, worldPosition, worldNormal, cascadeIndex, probesOrigin, probesExtent, probesSpacing, biasedWorldPosition);
|
||||
|
||||
// Blend with the next cascade (or fallback irradiance outside the volume)
|
||||
cascadeIndex++;
|
||||
#if DDGI_CASCADE_BLEND_SMOOTH && !defined(DDGI_DEBUG_CASCADE)
|
||||
result *= cascadeWeight;
|
||||
cascadeIndex++;
|
||||
if (cascadeIndex < data.CascadesCount && cascadeWeight < 0.99f)
|
||||
{
|
||||
probesSpacing = data.ProbesOriginAndSpacing[cascadeIndex].w;
|
||||
@@ -336,18 +335,16 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D<snorm float4> probesData, T
|
||||
probesExtent = (data.ProbesCounts - 1) * (probesSpacing * 0.5f);
|
||||
biasedWorldPosition = worldPosition + GetDDGISurfaceBias(viewDir, probesSpacing, worldNormal, bias);
|
||||
float3 resultNext = SampleDDGIIrradianceCascade(data, probesData, probesDistance, probesIrradiance, worldPosition, worldNormal, cascadeIndex, probesOrigin, probesExtent, probesSpacing, biasedWorldPosition);
|
||||
result *= cascadeWeight;
|
||||
result += resultNext * (1 - cascadeWeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
result += data.FallbackIrradiance * (1 - cascadeWeight);
|
||||
}
|
||||
#else
|
||||
if (cascadeIndex == data.CascadesCount)
|
||||
{
|
||||
result += data.FallbackIrradiance * (1 - cascadeWeight);
|
||||
}
|
||||
#endif
|
||||
if (cascadeIndex >= data.CascadesCount)
|
||||
{
|
||||
// Blend between the last cascade and the fallback irradiance
|
||||
float fallbackWeight = (1 - cascadeWeight) * data.FallbackIrradiance.a;
|
||||
result = lerp(result, data.FallbackIrradiance.rgb, fallbackWeight);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -102,6 +102,11 @@ float3 Remap(float3 value, float3 fromMin, float3 fromMax, float3 toMin, float3
|
||||
return (value - fromMin) / (fromMax - fromMin) * (toMax - toMin) + toMin;
|
||||
}
|
||||
|
||||
bool IsProbeAtBorder(uint3 probeCoords)
|
||||
{
|
||||
return min(probeCoords.x, min(probeCoords.y, probeCoords.z)) == 0 || probeCoords.x == DDGI.ProbesCounts.x - 1 || probeCoords.y == DDGI.ProbesCounts.y - 1 || probeCoords.z == DDGI.ProbesCounts.z - 1;
|
||||
}
|
||||
|
||||
// Compute shader for updating probes state between active and inactive and performing probes relocation.
|
||||
META_CS(true, FEATURE_LEVEL_SM5)
|
||||
[numthreads(DDGI_PROBE_CLASSIFY_GROUP_SIZE, 1, 1)]
|
||||
@@ -183,16 +188,27 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
|
||||
float voxelLimit = GlobalSDF.CascadeVoxelSize[CascadeIndex] * 0.8f;
|
||||
float distanceLimit = probesSpacing * ProbesDistanceLimits[CascadeIndex];
|
||||
float relocateLimit = probesSpacing * ProbesRelocateLimits[CascadeIndex];
|
||||
#ifdef DDGI_PROBE_EMPTY_AREA_DENSITY
|
||||
uint3 probeCoordsStable = GetDDGIProbeCoords(DDGI, probeIndex);
|
||||
if (sdf > probesSpacing * DDGI.ProbesCounts.x * 0.3f &&
|
||||
probeCoordsStable.x % DDGI_PROBE_EMPTY_AREA_DENSITY == 0 && probeCoordsStable.y % DDGI_PROBE_EMPTY_AREA_DENSITY == 0 && probeCoordsStable.z % DDGI_PROBE_EMPTY_AREA_DENSITY == 0)
|
||||
if (sdf > probesSpacing * DDGI.ProbesCounts.x * 0.3f
|
||||
#if DDGI_PROBE_EMPTY_AREA_DENSITY > 1
|
||||
&& (
|
||||
// Low-density grid grid
|
||||
(probeCoordsStable.x % DDGI_PROBE_EMPTY_AREA_DENSITY == 0 && probeCoordsStable.y % DDGI_PROBE_EMPTY_AREA_DENSITY == 0 && probeCoordsStable.z % DDGI_PROBE_EMPTY_AREA_DENSITY == 0)
|
||||
// Edge probes at the last cascade (for good fallback irradiance outside the GI distance)
|
||||
//|| (CascadeIndex + 1 == DDGI.CascadesCount && IsProbeAtBorder(probeCoords))
|
||||
)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
// Addd some fallback probes in empty areas to provide valid GI for nearby dynamic objects or transparency
|
||||
probeOffset = float3(0, 0, 0);
|
||||
probeState = wasScrolled || probeStateOld == DDGI_PROBE_STATE_INACTIVE ? DDGI_PROBE_STATE_ACTIVATED : DDGI_PROBE_STATE_ACTIVE;
|
||||
probeAttention = DDGI_PROBE_ATTENTION_MIN;
|
||||
}
|
||||
else if (sdfDst > distanceLimit + length(probeOffset))
|
||||
else
|
||||
#endif
|
||||
if (sdfDst > distanceLimit + length(probeOffset))
|
||||
{
|
||||
// Probe is too far from geometry (or deep inside) so disable it
|
||||
probeOffset = float3(0, 0, 0);
|
||||
|
||||
Reference in New Issue
Block a user