Add support for cascades to DDGI

This commit is contained in:
Wojciech Figat
2022-06-09 08:55:45 +02:00
parent 73d762cf0c
commit 6a74ebd62e
14 changed files with 395 additions and 234 deletions

View File

@@ -22,19 +22,18 @@
// DDGI data for a constant buffer
struct DDGIData
{
float3 ProbesOrigin;
float ProbesSpacing;
float4 RaysRotation;
float4 ProbesOriginAndSpacing[4];
int4 ProbesScrollOffsets[4];
int4 ProbeScrollDirections[4];
uint3 ProbesCounts;
uint CascadesCount;
float IrradianceGamma;
int3 ProbesScrollOffsets;
float ProbeHistoryWeight;
float RayMaxDistance;
float Padding0;
float4 RaysRotation;
float3 ViewDir;
uint RaysCount;
int3 ProbeScrollDirections;
float RayMaxDistance;
uint3 ProbeScrollClear; // TODO: pack into bits
uint Padding0;
};
uint GetDDGIProbeIndex(DDGIData data, uint3 probeCoords)
@@ -62,88 +61,106 @@ uint3 GetDDGIProbeCoords(DDGIData data, uint probeIndex)
return probeCoords;
}
uint2 GetDDGIProbeTexelCoords(DDGIData data, uint probeIndex)
uint2 GetDDGIProbeTexelCoords(DDGIData data, uint cascadeIndex, uint probeIndex)
{
uint probesPerPlane = data.ProbesCounts.x * data.ProbesCounts.z;
uint planeIndex = probeIndex / probesPerPlane;
uint gridSpaceX = probeIndex % data.ProbesCounts.x;
uint gridSpaceY = probeIndex / data.ProbesCounts.x;
uint x = gridSpaceX + (planeIndex * data.ProbesCounts.x);
uint y = gridSpaceY % data.ProbesCounts.z;
uint y = gridSpaceY % data.ProbesCounts.z + cascadeIndex * data.ProbesCounts.z;
return uint2(x, y);
}
uint GetDDGIScrollingProbeIndex(DDGIData data, uint3 probeCoords)
uint GetDDGIScrollingProbeIndex(DDGIData data, uint cascadeIndex, uint3 probeCoords)
{
// Probes are scrolled on edges to stabilize GI when camera moves
return GetDDGIProbeIndex(data, (probeCoords + data.ProbesScrollOffsets + data.ProbesCounts) % data.ProbesCounts);
return GetDDGIProbeIndex(data, (probeCoords + data.ProbesScrollOffsets[cascadeIndex].xyz + data.ProbesCounts) % data.ProbesCounts);
}
float3 GetDDGIProbeWorldPosition(DDGIData data, uint3 probeCoords)
float3 GetDDGIProbeWorldPosition(DDGIData data, uint cascadeIndex, uint3 probeCoords)
{
float3 probePosition = probeCoords * data.ProbesSpacing;
float3 probeGridOffset = (data.ProbesSpacing * (data.ProbesCounts - 1)) * 0.5f;
return data.ProbesOrigin + probePosition - probeGridOffset + (data.ProbesScrollOffsets * data.ProbesSpacing);
float3 probesOrigin = data.ProbesOriginAndSpacing[cascadeIndex].xyz;
float probesSpacing = data.ProbesOriginAndSpacing[cascadeIndex].w;
float3 probePosition = probeCoords * probesSpacing;
float3 probeGridOffset = (probesSpacing * (data.ProbesCounts - 1)) * 0.5f;
return probesOrigin + probePosition - probeGridOffset + (data.ProbesScrollOffsets[cascadeIndex].xyz * probesSpacing);
}
// Loads probe probe state
float LoadDDGIProbeState(DDGIData data, Texture2D<float4> probesState, uint probeIndex)
float LoadDDGIProbeState(DDGIData data, Texture2D<float4> probesState, uint cascadeIndex, uint probeIndex)
{
int2 probeDataCoords = GetDDGIProbeTexelCoords(data, probeIndex);
int2 probeDataCoords = GetDDGIProbeTexelCoords(data, cascadeIndex, probeIndex);
float4 probeState = probesState.Load(int3(probeDataCoords, 0));
return probeState.w;
}
// Loads probe world-space position (XYZ) and probe state (W)
float4 LoadDDGIProbePositionAndState(DDGIData data, Texture2D<float4> probesState, uint probeIndex, uint3 probeCoords)
float4 LoadDDGIProbePositionAndState(DDGIData data, Texture2D<float4> probesState, uint cascadeIndex, uint probeIndex, uint3 probeCoords)
{
int2 probeDataCoords = GetDDGIProbeTexelCoords(data, probeIndex);
int2 probeDataCoords = GetDDGIProbeTexelCoords(data, cascadeIndex, probeIndex);
float4 probeState = probesState.Load(int3(probeDataCoords, 0));
probeState.xyz += GetDDGIProbeWorldPosition(data, probeCoords);
probeState.xyz += GetDDGIProbeWorldPosition(data, cascadeIndex, probeCoords);
return probeState;
}
// Calculates texture UVs for sampling probes atlas texture (irradiance or distance)
float2 GetDDGIProbeUV(DDGIData data, uint probeIndex, float2 octahedralCoords, uint resolution)
float2 GetDDGIProbeUV(DDGIData data, uint cascadeIndex, uint probeIndex, float2 octahedralCoords, uint resolution)
{
uint2 coords = GetDDGIProbeTexelCoords(data, probeIndex);
uint2 coords = GetDDGIProbeTexelCoords(data, cascadeIndex, probeIndex);
float probeTexelSize = resolution + 2.0f;
float textureWidth = probeTexelSize * (data.ProbesCounts.x * data.ProbesCounts.y);
float textureHeight = probeTexelSize * data.ProbesCounts.z;
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 /= float2(textureWidth, textureHeight);
uv /= textureSize;
return uv;
}
// Samples DDGI probes volume at the given world-space position and returns the irradiance.
float3 SampleDDGIIrradiance(DDGIData data, Texture2D<float4> probesState, Texture2D<float4> probesDistance, Texture2D<float4> probesIrradiance, float3 worldPosition, float3 worldNormal, float bias)
// rand - randomized per-pixel value in range 0-1, used to smooth dithering for cascades blending
float3 SampleDDGIIrradiance(DDGIData data, Texture2D<float4> probesState, Texture2D<float4> probesDistance, Texture2D<float4> probesIrradiance, float3 worldPosition, float3 worldNormal, float bias, float dither = 0.0f)
{
float4 irradiance = float4(0, 0, 0, 0);
float3 probesOrigin = data.ProbesScrollOffsets * data.ProbesSpacing + data.ProbesOrigin;
float3 probesExtent = (data.ProbesCounts - 1) * (data.ProbesSpacing * 0.5f);
// Select the highest cascade that contains the sample location
uint cascadeIndex = 0;
for (; cascadeIndex < data.CascadesCount; cascadeIndex++)
{
float probesSpacing = data.ProbesOriginAndSpacing[cascadeIndex].w;
float3 probesOrigin = data.ProbesScrollOffsets[cascadeIndex].xyz * probesSpacing + data.ProbesOriginAndSpacing[cascadeIndex].xyz;
float3 probesExtent = (data.ProbesCounts - 1) * (probesSpacing * 0.5f);
float fadeDistance = probesSpacing * 0.5f;
float cascadeWeight = saturate(Min3(probesExtent - abs(worldPosition - probesOrigin)) / fadeDistance);
if (cascadeWeight > dither) // Use dither to make transition smoother
break;
}
if (cascadeIndex == data.CascadesCount)
return float3(0, 0, 0);
float probesSpacing = data.ProbesOriginAndSpacing[cascadeIndex].w;
float3 probesOrigin = data.ProbesScrollOffsets[cascadeIndex].xyz * probesSpacing + data.ProbesOriginAndSpacing[cascadeIndex].xyz;
float3 probesExtent = (data.ProbesCounts - 1) * (probesSpacing * 0.5f);
// Bias the world-space position to reduce artifacts
float3 surfaceBias = (worldNormal * bias) + (data.ViewDir * (bias * -4.0f));
float3 biasedWorldPosition = worldPosition + surfaceBias;
// Get the grid coordinates of the probe nearest the biased world position
uint3 baseProbeCoords = clamp(uint3((worldPosition - probesOrigin + probesExtent) / data.ProbesSpacing), 0, data.ProbesCounts - 1);
float3 baseProbeWorldPosition = GetDDGIProbeWorldPosition(data, baseProbeCoords);
float3 biasAlpha = saturate((biasedWorldPosition - baseProbeWorldPosition) / data.ProbesSpacing);
uint3 baseProbeCoords = clamp(uint3((worldPosition - probesOrigin + probesExtent) / probesSpacing), 0, data.ProbesCounts - 1);
float3 baseProbeWorldPosition = GetDDGIProbeWorldPosition(data, cascadeIndex, baseProbeCoords);
float3 biasAlpha = saturate((biasedWorldPosition - baseProbeWorldPosition) / probesSpacing);
// Loop over the closest probes to accumulate their contributions
float4 irradiance = float4(0, 0, 0, 0);
for (uint i = 0; i < 8; i++)
{
uint3 probeCoordsOffset = uint3(i, i >> 1, i >> 2) & 1;
uint3 probeCoords = clamp(baseProbeCoords + probeCoordsOffset, 0, data.ProbesCounts - 1);
uint probeIndex = GetDDGIScrollingProbeIndex(data, probeCoords);
uint probeIndex = GetDDGIScrollingProbeIndex(data, cascadeIndex, probeCoords);
// Load probe position and state
float4 probeState = probesState.Load(int3(GetDDGIProbeTexelCoords(data, probeIndex), 0));
float4 probeState = probesState.Load(int3(GetDDGIProbeTexelCoords(data, cascadeIndex, probeIndex), 0));
if (probeState.w == DDGI_PROBE_STATE_INACTIVE)
continue;
float3 probeBasePosition = baseProbeWorldPosition + ((probeCoords - baseProbeCoords) * data.ProbesSpacing);
float3 probeBasePosition = baseProbeWorldPosition + ((probeCoords - baseProbeCoords) * probesSpacing);
float3 probePosition = probeBasePosition + probeState.xyz;
// Calculate the distance and direction from the (biased and non-biased) shading point and the probe
@@ -156,7 +173,7 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D<float4> probesState, Textur
// Sample distance texture
float2 octahedralCoords = GetOctahedralCoords(-biasedPosToProbe);
float2 uv = GetDDGIProbeUV(data, probeIndex, octahedralCoords, DDGI_PROBE_RESOLUTION_DISTANCE);
float2 uv = GetDDGIProbeUV(data, cascadeIndex, probeIndex, octahedralCoords, DDGI_PROBE_RESOLUTION_DISTANCE);
float2 probeDistance = probesDistance.SampleLevel(SamplerLinearClamp, uv, 0).rg * 2.0f;
float probeDistanceMean = probeDistance.x;
float probeDistanceMean2 = probeDistance.y;
@@ -183,7 +200,7 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D<float4> probesState, Textur
// Sample irradiance texture
octahedralCoords = GetOctahedralCoords(worldNormal);
uv = GetDDGIProbeUV(data, probeIndex, octahedralCoords, DDGI_PROBE_RESOLUTION_IRRADIANCE);
uv = GetDDGIProbeUV(data, cascadeIndex, probeIndex, octahedralCoords, DDGI_PROBE_RESOLUTION_IRRADIANCE);
float3 probeIrradiance = probesIrradiance.SampleLevel(SamplerLinearClamp, uv, 0).rgb;
#if DDGI_SRGB_BLENDING
probeIrradiance = pow(probeIrradiance, data.IrradianceGamma * 0.5f);
@@ -196,6 +213,18 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D<float4> probesState, Textur
irradiance += float4(probeIrradiance * weight, weight);
}
#if 0
// Debug DDGI cascades with colors
if (cascadeIndex == 0)
irradiance = float4(1, 0, 0, 1);
else if (cascadeIndex == 1)
irradiance = float4(0, 1, 0, 1);
else if (cascadeIndex == 2)
irradiance = float4(0, 0, 1, 1);
else
irradiance = float4(1, 0, 1, 1);
#endif
if (irradiance.a > 0.0f)
{
// Normalize irradiance
@@ -204,10 +233,6 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D<float4> probesState, Textur
irradiance.rgb *= irradiance.rgb;
#endif
irradiance.rgb *= 2.0f * PI;
// Fade-out outside the probes volume
float fadeDistance = data.ProbesSpacing * 0.5f;
irradiance.rgb *= saturate(Min3(probesExtent - abs(worldPosition - probesOrigin)) / fadeDistance);
}
return irradiance.rgb;
}