Add support for cascades to DDGI
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user