Files
UnrealEngine/Engine/Shaders/Private/Lumen/LumenRadianceCacheInterpolation.ush
2025-05-18 13:04:45 +08:00

576 lines
28 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "../MonteCarlo.ush"
#include "../ReflectionEnvironmentShared.ush"
#define RADIANCE_PROBE_MAX_CLIPMAPS 6
#define INVALID_PROBE_INDEX 0xFFFFFFFF
#if IS_MATERIAL_SHADER
#define RADIANCE_CACHE_PARAMETERS_IN_UB 1
#else
#define RADIANCE_CACHE_PARAMETERS_IN_UB 0
#endif
#ifndef RADIANCE_CACHE_DEPTH_TEST_SPHERE_PARALLAX
#define RADIANCE_CACHE_DEPTH_TEST_SPHERE_PARALLAX 0
#endif
#if RADIANCE_CACHE_PARAMETERS_IN_UB
#define RadianceProbeIndirectionTexture RadianceCacheInterpolation.RadianceProbeIndirectionTexture
#define RadianceCacheFinalRadianceAtlas RadianceCacheInterpolation.RadianceCacheFinalRadianceAtlas
#define RadianceCacheFinalSkyVisibilityAtlas RadianceCacheInterpolation.RadianceCacheFinalSkyVisibilityAtlas
#define RadianceCacheFinalIrradianceAtlas RadianceCacheInterpolation.RadianceCacheFinalIrradianceAtlas
#define RadianceCacheProbeOcclusionAtlas RadianceCacheInterpolation.RadianceCacheProbeOcclusionAtlas
#define RadianceProbeSettings RadianceCacheInterpolation.RadianceProbeSettings
#define ClipmapCornerTWSAndCellSize RadianceCacheInterpolation.ClipmapCornerTWSAndCellSize
#define ReprojectionRadiusScale RadianceCacheInterpolation.ReprojectionRadiusScale
#define InvClipmapFadeSize RadianceCacheInterpolation.InvClipmapFadeSize
#define ProbeAtlasResolutionInProbes RadianceCacheInterpolation.ProbeAtlasResolutionInProbes
#define InvProbeFinalRadianceAtlasResolution RadianceCacheInterpolation.InvProbeFinalRadianceAtlasResolution
#define InvProbeFinalIrradianceAtlasResolution RadianceCacheInterpolation.InvProbeFinalIrradianceAtlasResolution
#define InvProbeDepthAtlasResolution RadianceCacheInterpolation.InvProbeDepthAtlasResolution
#define NumRadianceProbeClipmaps RadianceCacheInterpolation.NumRadianceProbeClipmaps
#define RadianceProbeClipmapResolution RadianceCacheInterpolation.RadianceProbeClipmapResolution
#define RadianceProbeResolution RadianceCacheInterpolation.RadianceProbeResolution
#define FinalProbeResolution RadianceCacheInterpolation.FinalProbeResolution
#define CalculateIrradiance RadianceCacheInterpolation.CalculateIrradiance
#define IrradianceProbeResolution RadianceCacheInterpolation.IrradianceProbeResolution
#define OcclusionProbeResolution RadianceCacheInterpolation.OcclusionProbeResolution
#define FinalRadianceAtlasMaxMip RadianceCacheInterpolation.FinalRadianceAtlasMaxMip
#define RadianceCacheOneOverCachedLightingPreExposure RadianceCacheInterpolation.RadianceCacheOneOverCachedLightingPreExposure
#define OverrideCacheOcclusionLighting RadianceCacheInterpolation.OverrideCacheOcclusionLighting
#define ShowBlackRadianceCacheLighting RadianceCacheInterpolation.ShowBlackRadianceCacheLighting
#define ProbeWorldOffset RadianceCacheInterpolation.ProbeWorldOffset
#define RadianceCacheDepthAtlas RadianceCacheInterpolation.RadianceCacheDepthAtlas
#define ProbeAtlasResolutionModuloMask RadianceCacheInterpolation.ProbeAtlasResolutionModuloMask
#define ProbeAtlasResolutionDivideShift RadianceCacheInterpolation.ProbeAtlasResolutionDivideShift
#else
Texture3D<uint> RadianceProbeIndirectionTexture;
Texture2D<float3> RadianceCacheFinalRadianceAtlas;
Texture2D<float> RadianceCacheFinalSkyVisibilityAtlas;
Texture2D<float3> RadianceCacheFinalIrradianceAtlas;
Texture2D<float2> RadianceCacheProbeOcclusionAtlas;
Texture2D<float> RadianceCacheDepthAtlas;
StructuredBuffer<float4> ProbeWorldOffset;
float4 RadianceProbeSettings[RADIANCE_PROBE_MAX_CLIPMAPS];
float4 ClipmapCornerTWSAndCellSize[RADIANCE_PROBE_MAX_CLIPMAPS];
float ReprojectionRadiusScale;
float InvClipmapFadeSize;
uint2 ProbeAtlasResolutionInProbes;
float2 InvProbeFinalRadianceAtlasResolution;
float2 InvProbeFinalIrradianceAtlasResolution;
float2 InvProbeDepthAtlasResolution;
uint NumRadianceProbeClipmaps;
uint RadianceProbeClipmapResolution;
// Resolution of Octahedral layout during tracing
uint RadianceProbeResolution;
// Resolution of Octahedral layout during sampling
uint FinalProbeResolution;
uint CalculateIrradiance;
uint IrradianceProbeResolution;
// Resolution of Octahedral layout for sampling
uint OcclusionProbeResolution;
uint FinalRadianceAtlasMaxMip;
float RadianceCacheOneOverCachedLightingPreExposure;
uint OverrideCacheOcclusionLighting;
uint ShowBlackRadianceCacheLighting;
uint ProbeAtlasResolutionModuloMask;
uint ProbeAtlasResolutionDivideShift;
#endif
float GetRadianceProbeTMin(uint ClipmapIndex)
{
return RadianceProbeSettings[ClipmapIndex].x; // must match with LumenRadianceCacheInterpolation.h
}
// The minimum coordinate of the clipmap, in translated world space
float3 GetRadianceProbeClipmapCornerTWS(uint ClipmapIndex)
{
return ClipmapCornerTWSAndCellSize[ClipmapIndex].xyz; // must match with LumenRadianceCacheInterpolation.h
}
float GetRadianceProbeClipmapCellSize(uint ClipmapIndex)
{
return ClipmapCornerTWSAndCellSize[ClipmapIndex].w; // must match with LumenRadianceCacheInterpolation.h
}
float3 GetRadianceProbeCoordFloat(float3 ProbeWorldPosition, uint ClipmapIndex)
{
const float3 ProbeTranslatedWorldPosition = ProbeWorldPosition + DFHackToFloat(PrimaryView.PreViewTranslation);
const float3 CornerTranslatedWorldPosition = GetRadianceProbeClipmapCornerTWS(ClipmapIndex);
const float3 CornerToProbe = ProbeTranslatedWorldPosition - CornerTranslatedWorldPosition;
const float CellSize = GetRadianceProbeClipmapCellSize(ClipmapIndex);
return CornerToProbe / CellSize;
}
int3 GetRadianceProbeCoord(float3 ProbeWorldPosition, uint ClipmapIndex)
{
// Use floor() to round negative numbers down
return floor(GetRadianceProbeCoordFloat(ProbeWorldPosition, ClipmapIndex));
}
int3 GetRadianceProbeBottomCornerCoord(float3 ProbeWorldPosition, uint ClipmapIndex)
{
return GetRadianceProbeCoord(ProbeWorldPosition - 0.5f, ClipmapIndex);
}
float3 GetProbeTranslatedWorldPositionNoOffset(uint3 ProbeCoord, uint ClipmapIndex)
{
const float3 CornerTranslatedWorldPosition = GetRadianceProbeClipmapCornerTWS(ClipmapIndex);
const float CellSize = GetRadianceProbeClipmapCellSize(ClipmapIndex);
const float3 CornerToProbe = (ProbeCoord + 0.5) * CellSize;
return CornerTranslatedWorldPosition + CornerToProbe;
}
float3 GetProbeWorldPosition(uint3 ProbeCoord, uint ClipmapIndex, uint ProbeIndex)
{
return GetProbeTranslatedWorldPositionNoOffset(ProbeCoord, ClipmapIndex) + ProbeWorldOffset[ProbeIndex].xyz - DFHackToFloat(PrimaryView.PreViewTranslation);
}
bool IsValidRadianceCacheClipmap(uint FRadianceCacheCoverage)
{
return FRadianceCacheCoverage < NumRadianceProbeClipmaps;
}
uint GetRadianceProbeClipmap(float3 WorldSpacePosition, float ClipmapDitherRandom)
{
uint ClipmapIndex = 0;
for (; ClipmapIndex < NumRadianceProbeClipmaps; ++ClipmapIndex)
{
float3 ProbeCoordFloat = GetRadianceProbeCoordFloat(WorldSpacePosition, ClipmapIndex);
float3 BottomEdgeFades = saturate((ProbeCoordFloat - .5f) * InvClipmapFadeSize);
float3 TopEdgeFades = saturate(((float3)RadianceProbeClipmapResolution - .5f - ProbeCoordFloat) * InvClipmapFadeSize);
float EdgeFade = min(min3(BottomEdgeFades.x, BottomEdgeFades.y, BottomEdgeFades.z), min3(TopEdgeFades.x, TopEdgeFades.y, TopEdgeFades.z));
if (EdgeFade > ClipmapDitherRandom)
{
return ClipmapIndex;
}
}
return NumRadianceProbeClipmaps;
}
struct FRadianceCacheCoverage
{
uint ClipmapIndex;
// The minimum distance that must be traced before interpolating from the Radiance Cache, to prevent leaking
float MinTraceDistanceBeforeInterpolation;
// Whether the Radiance Cache covers the queried position
bool bValid;
};
FRadianceCacheCoverage InitRadianceCacheCoverage()
{
FRadianceCacheCoverage Out;
Out.ClipmapIndex = 0;
Out.MinTraceDistanceBeforeInterpolation = 10000000.0f;
Out.bValid = false;
return Out;
}
// Only positions that were marked during FMarkUsedRadianceCacheProbes can be queried, this version does not check if the position was marked correctly
// See UnmappedDebugColor for visualizing these errors
// GetRadianceCacheCoverageWithUncertainCoverage can be used for interpolating to positions with uncertain coverage
FRadianceCacheCoverage GetRadianceCacheCoverage(float3 RayOrigin, float3 RayDirection, float ClipmapDitherRandom)
{
FRadianceCacheCoverage Coverage = InitRadianceCacheCoverage();
Coverage.ClipmapIndex = GetRadianceProbeClipmap(RayOrigin, ClipmapDitherRandom);
if (Coverage.ClipmapIndex < NumRadianceProbeClipmaps)
{
Coverage.bValid = true;
float CellOcclusionDistance = GetRadianceProbeClipmapCellSize(Coverage.ClipmapIndex) * sqrt(3.0f);
Coverage.MinTraceDistanceBeforeInterpolation = GetRadianceProbeTMin(Coverage.ClipmapIndex) + CellOcclusionDistance;
}
return Coverage;
}
struct FRadianceCacheSample
{
float3 Radiance;
float SkyVisibility;
float TraceHitDistance;
};
FRadianceCacheSample SampleRadianceCacheProbe(uint ProbeIndex, float3 WorldSpaceDirection, float MipLevel)
{
FRadianceCacheSample Sample;
Sample.Radiance = 0.0f;
Sample.SkyVisibility = 0.0f;
Sample.TraceHitDistance = 0.0f;
float2 ProbeUV = InverseEquiAreaSphericalMapping(WorldSpaceDirection);
#define VISUALIZE_PROBE_DEPTH 0
#if VISUALIZE_PROBE_DEPTH
{
uint2 ProbeAtlasCoord = RadianceProbeResolution * uint2(ProbeIndex & ProbeAtlasResolutionModuloMask, ProbeIndex >> ProbeAtlasResolutionDivideShift);
float2 ProbeTexelCoord = ProbeUV * RadianceProbeResolution;
float2 ProbeAtlasUV = (ProbeAtlasCoord + ProbeTexelCoord) * InvProbeDepthAtlasResolution;
Sample.Radiance = RadianceCacheDepthAtlas.SampleLevel(GlobalPointClampedSampler, ProbeAtlasUV, MipLevel).x / 10000.0f;
}
#else
{
uint2 ProbeAtlasCoord = FinalProbeResolution * uint2(ProbeIndex & ProbeAtlasResolutionModuloMask, ProbeIndex >> ProbeAtlasResolutionDivideShift);
float2 ProbeTexelCoord = ProbeUV * RadianceProbeResolution + (1u << FinalRadianceAtlasMaxMip);
float2 ProbeAtlasUV = (ProbeAtlasCoord + ProbeTexelCoord) * InvProbeFinalRadianceAtlasResolution;
Sample.Radiance = 0.0f;
// Uncomment to show bright green when an unallocated probe is sampled
//Sample.Radiance = float3(0.0f, 10.0f, 0.0f);
if (ProbeIndex != INVALID_PROBE_INDEX)
{
Sample.Radiance = RadianceCacheFinalRadianceAtlas.SampleLevel(GlobalBilinearClampedSampler, ProbeAtlasUV, MipLevel) * RadianceCacheOneOverCachedLightingPreExposure;
}
#if RADIANCE_CACHE_SKY_VISIBILITY
if (ProbeIndex != INVALID_PROBE_INDEX)
{
Sample.SkyVisibility = RadianceCacheFinalSkyVisibilityAtlas.SampleLevel(GlobalBilinearClampedSampler, ProbeAtlasUV, MipLevel);
}
#endif
}
#endif
{
uint2 ProbeAtlasCoord = RadianceProbeResolution * uint2(ProbeIndex & ProbeAtlasResolutionModuloMask, ProbeIndex >> ProbeAtlasResolutionDivideShift);
float2 ProbeTexelCoord = ProbeUV * RadianceProbeResolution;
float2 ProbeAtlasUV = (ProbeAtlasCoord + ProbeTexelCoord) * InvProbeDepthAtlasResolution;
Sample.TraceHitDistance = RadianceCacheDepthAtlas.SampleLevel(GlobalPointClampedSampler, ProbeAtlasUV, MipLevel).x;
}
return Sample;
}
uint GetProbeIndexFromIndirectionTexture(uint3 ProbeCoord, uint ClipmapIndex)
{
uint3 ProbeIndirectionTextureCoord = uint3(ProbeCoord.x + ClipmapIndex * RadianceProbeClipmapResolution, ProbeCoord.yz);
return RadianceProbeIndirectionTexture.Load(uint4(ProbeIndirectionTextureCoord, 0));
}
FRadianceCacheSample SampleRadianceCacheProbeWithParallaxCorrection(uint3 ProbeCoord, uint ProbeClipmapIndex, float3 WorldSpacePosition, float3 WorldSpaceDirection, float MipLevel)
{
float ProbeTMin = GetRadianceProbeTMin(ProbeClipmapIndex);
uint ProbeIndex = GetProbeIndexFromIndirectionTexture(ProbeCoord, ProbeClipmapIndex);
float3 ProbeWorldPosition = GetProbeWorldPosition(ProbeCoord, ProbeClipmapIndex, ProbeIndex);
float3 ReprojectedDirection = WorldSpaceDirection;
float CorrectionFactor = 1.0f;
#define SIMPLE_SPHERE_PARALLAX 1
#define TRACE_THROUGH_PROBE_DEPTHS_REFERENCE 0
#if SIMPLE_SPHERE_PARALLAX
float ReprojectionRadius = ReprojectionRadiusScale * ProbeTMin;
float T = RayIntersectSphere(WorldSpacePosition, WorldSpaceDirection, float4(ProbeWorldPosition, ReprojectionRadius)).y;
float3 IntersectionPosition = WorldSpacePosition + WorldSpaceDirection * T;
ReprojectedDirection = IntersectionPosition - ProbeWorldPosition;
// Cancel out the attenuation effect when moving towards/away from a probe texel to mitigate the grid like pattern
// CorrectionFactor = T^2 / R^2 / dot(normalize(ReprojectedDirection), WorldSpaceDirection)
CorrectionFactor = T * T / (ReprojectionRadius * dot(ReprojectedDirection, WorldSpaceDirection));
// Depth test the parallax corrected direction, if it's distant lighting then we can skip the parallax correction and the bias that comes with it without leaking
// Currently disabled, needs more work
#if RADIANCE_CACHE_DEPTH_TEST_SPHERE_PARALLAX && 0
{
float2 ProbeUV = InverseEquiAreaSphericalMapping(ReprojectedDirection);
uint2 ProbeAtlasCoord = RadianceProbeResolution * uint2(ProbeIndex & ProbeAtlasResolutionModuloMask, ProbeIndex >> ProbeAtlasResolutionDivideShift);
float2 ProbeAtlasUV = (ProbeAtlasCoord + ProbeUV * RadianceProbeResolution) * InvProbeDepthAtlasResolution;
float2 ProbeGatherAtlasUV = (floor(ProbeAtlasUV / InvProbeDepthAtlasResolution - .5f) + 1.0f) * InvProbeDepthAtlasResolution;
float4 HitDistanceFromProbeGather = RadianceCacheDepthAtlas.GatherRed(GlobalPointClampedSampler, ProbeGatherAtlasUV).wzxy;
float HitDistanceFromProbe = min(min(HitDistanceFromProbeGather.x, HitDistanceFromProbeGather.y), min(HitDistanceFromProbeGather.z, HitDistanceFromProbeGather.w));
if (HitDistanceFromProbe > ReprojectionRadius * 2.0f)
{
ReprojectedDirection = WorldSpaceDirection;
}
}
#endif
#elif TRACE_THROUGH_PROBE_DEPTHS_REFERENCE
//@note - no depth mips implemented
float3 ProbeTraceStart = WorldSpacePosition + WorldSpaceDirection * RayIntersectSphere(WorldSpacePosition, WorldSpaceDirection, float4(ProbeWorldPosition, ProbeTMin)).y;
float3 ProbeTraceEnd = ProbeWorldPosition + WorldSpaceDirection * 10000.0f;
float3 ProbeTraceDirection = ProbeTraceEnd - ProbeTraceStart;
uint2 ProbeAtlasCoord = RadianceProbeResolution * uint2(ProbeIndex & ProbeAtlasResolutionModuloMask, ProbeIndex >> ProbeAtlasResolutionDivideShift);
float NumSamples = 100.0f;
for (float StepIndex = 0; StepIndex < NumSamples; StepIndex++)
{
float3 StepPosition = ProbeTraceStart + StepIndex / (NumSamples - 1) * ProbeTraceDirection;
float3 ProbeToStepPosition = StepPosition - ProbeWorldPosition;
float2 ProbeUV = InverseEquiAreaSphericalMapping(ProbeToStepPosition);
float2 ProbeAtlasUV = (ProbeAtlasCoord + ProbeUV * RadianceProbeResolution) * InvProbeDepthAtlasResolution;
float StepHitDistanceFromProbe = RadianceCacheDepthAtlas.SampleLevel(GlobalPointClampedSampler, ProbeAtlasUV, MipLevel).x;
float StepRayDistanceFromProbeSq = dot(ProbeToStepPosition, ProbeToStepPosition);
if (StepHitDistanceFromProbe * StepHitDistanceFromProbe < StepRayDistanceFromProbeSq)
{
ReprojectedDirection = ProbeToStepPosition;
break;
}
}
#endif
FRadianceCacheSample RadianceCacheSample = SampleRadianceCacheProbe(ProbeIndex, ReprojectedDirection, MipLevel);
RadianceCacheSample.Radiance *= CorrectionFactor;
return RadianceCacheSample;
}
FRadianceCacheSample LerpRadianceCacheSample(FRadianceCacheSample SampleA, FRadianceCacheSample SampleB, float Alpha)
{
FRadianceCacheSample Sample;
Sample.Radiance = lerp(SampleA.Radiance, SampleB.Radiance, Alpha);
Sample.SkyVisibility = lerp(SampleA.SkyVisibility, SampleB.SkyVisibility, Alpha);
Sample.TraceHitDistance = lerp(SampleA.TraceHitDistance, SampleB.TraceHitDistance, Alpha);
return Sample;
}
FRadianceCacheSample RadianceCacheResolveSky(FRadianceCacheSample Sample, float3 WorldSpaceDirection)
{
#if RADIANCE_CACHE_SKY_VISIBILITY
if (Sample.SkyVisibility > 0.0f && ReflectionStruct.SkyLightParameters.y > 0)
{
float SkyAverageBrightness = 1.0f;
float3 SkyRadiance = GetSkyLightReflection(WorldSpaceDirection, /*Roughness*/ 0.0f, SkyAverageBrightness);
Sample.Radiance = lerp(Sample.Radiance, SkyRadiance, Sample.SkyVisibility);
}
#endif
return Sample;
}
FRadianceCacheSample SampleRadianceCacheInterpolated(FRadianceCacheCoverage Coverage, float3 WorldSpacePosition, float3 WorldSpaceDirection, float ConeHalfAngle, float RandomScalarForStochasticInterpolation)
{
// Caller should have branched
#define DEBUG_VISUALIZE_INVALID_COVERAGE 0
#if DEBUG_VISUALIZE_INVALID_COVERAGE
if (!Coverage.bValid)
{
return View.StateFrameIndexMod8 == 0 ? float3(10, 0, 0) : float3(0, 0, 0);
}
#endif
float3 ProbeCoordFloat = GetRadianceProbeCoordFloat(WorldSpacePosition, Coverage.ClipmapIndex);
//float ConeHalfAngle = acosFast(1.0f - NumTexels * NumTexels / (float)(RadianceProbeResolution * RadianceProbeResolution));
float NumTexels = sqrt(1.0f - cos(ConeHalfAngle)) * RadianceProbeResolution;
float MipLevel = clamp(log2(NumTexels), 0, (float)FinalRadianceAtlasMaxMip);
float3 CornerProbeCoordFloat = ProbeCoordFloat - 0.5f;
int3 CornerProbeCoord = floor(CornerProbeCoordFloat);
float3 LerpAlphas = frac(CornerProbeCoordFloat);
#if RADIANCE_CACHE_STOCHASTIC_INTERPOLATION
// Stochastic interpolation approximating full 2x2x2 trilinear filter
float4 InterpolationWeights0;
InterpolationWeights0.x = (1.0f - LerpAlphas.x) * (1.0f - LerpAlphas.y) * (1.0f - LerpAlphas.z);
InterpolationWeights0.y = LerpAlphas.x * (1.0f - LerpAlphas.y) * (1.0f - LerpAlphas.z);
InterpolationWeights0.z = (1.0f - LerpAlphas.x) * LerpAlphas.y * (1.0f - LerpAlphas.z);
InterpolationWeights0.w = LerpAlphas.x * LerpAlphas.y * (1.0f - LerpAlphas.z);
float4 InterpolationWeights1 = 1.0f;
InterpolationWeights1.x = (1.0f - LerpAlphas.x) * (1.0f - LerpAlphas.y) * LerpAlphas.z;
InterpolationWeights1.y = LerpAlphas.x * (1.0f - LerpAlphas.y) * LerpAlphas.z;
InterpolationWeights1.z = (1.0f - LerpAlphas.x) * LerpAlphas.y * LerpAlphas.z;
InterpolationWeights1.w = LerpAlphas.x * LerpAlphas.y * LerpAlphas.z;
// Two-sample variant
//uint3 StochasticOffset0 = GetStochasticTrilinearOffset((RandomScalarForStochasticInterpolation + 0.0f) / 2.0f, InterpolationWeights0, InterpolationWeights1);
//uint3 StochasticOffset1 = GetStochasticTrilinearOffset((RandomScalarForStochasticInterpolation + 1.0f) / 2.0f, InterpolationWeights0, InterpolationWeights1);
//FRadianceCacheSample Sample0 = SampleRadianceCacheProbeWithParallaxCorrection(CornerProbeCoord + StochasticOffset0, Coverage.ClipmapIndex, WorldSpacePosition, WorldSpaceDirection, MipLevel);
//FRadianceCacheSample Sample1 = SampleRadianceCacheProbeWithParallaxCorrection(CornerProbeCoord + StochasticOffset1, Coverage.ClipmapIndex, WorldSpacePosition, WorldSpaceDirection, MipLevel);
//FRadianceCacheSample InterpolatedSample = LerpRadianceCacheSample(Sample0, Sample1, 0.5f);
uint3 StochasticOffset = GetStochasticTrilinearOffset(RandomScalarForStochasticInterpolation, InterpolationWeights0, InterpolationWeights1);
FRadianceCacheSample InterpolatedSample = SampleRadianceCacheProbeWithParallaxCorrection(CornerProbeCoord + StochasticOffset, Coverage.ClipmapIndex, WorldSpacePosition, WorldSpaceDirection, MipLevel);
#else
FRadianceCacheSample Sample000 = SampleRadianceCacheProbeWithParallaxCorrection(CornerProbeCoord + int3(0, 0, 0), Coverage.ClipmapIndex, WorldSpacePosition, WorldSpaceDirection, MipLevel);
FRadianceCacheSample Sample001 = SampleRadianceCacheProbeWithParallaxCorrection(CornerProbeCoord + int3(0, 0, 1), Coverage.ClipmapIndex, WorldSpacePosition, WorldSpaceDirection, MipLevel);
FRadianceCacheSample Sample010 = SampleRadianceCacheProbeWithParallaxCorrection(CornerProbeCoord + int3(0, 1, 0), Coverage.ClipmapIndex, WorldSpacePosition, WorldSpaceDirection, MipLevel);
FRadianceCacheSample Sample011 = SampleRadianceCacheProbeWithParallaxCorrection(CornerProbeCoord + int3(0, 1, 1), Coverage.ClipmapIndex, WorldSpacePosition, WorldSpaceDirection, MipLevel);
FRadianceCacheSample Sample100 = SampleRadianceCacheProbeWithParallaxCorrection(CornerProbeCoord + int3(1, 0, 0), Coverage.ClipmapIndex, WorldSpacePosition, WorldSpaceDirection, MipLevel);
FRadianceCacheSample Sample101 = SampleRadianceCacheProbeWithParallaxCorrection(CornerProbeCoord + int3(1, 0, 1), Coverage.ClipmapIndex, WorldSpacePosition, WorldSpaceDirection, MipLevel);
FRadianceCacheSample Sample110 = SampleRadianceCacheProbeWithParallaxCorrection(CornerProbeCoord + int3(1, 1, 0), Coverage.ClipmapIndex, WorldSpacePosition, WorldSpaceDirection, MipLevel);
FRadianceCacheSample Sample111 = SampleRadianceCacheProbeWithParallaxCorrection(CornerProbeCoord + int3(1, 1, 1), Coverage.ClipmapIndex, WorldSpacePosition, WorldSpaceDirection, MipLevel);
FRadianceCacheSample ZLerp00 = LerpRadianceCacheSample(Sample000, Sample001, LerpAlphas.z);
FRadianceCacheSample ZLerp01 = LerpRadianceCacheSample(Sample010, Sample011, LerpAlphas.z);
FRadianceCacheSample ZLerp10 = LerpRadianceCacheSample(Sample100, Sample101, LerpAlphas.z);
FRadianceCacheSample ZLerp11 = LerpRadianceCacheSample(Sample110, Sample111, LerpAlphas.z);
FRadianceCacheSample YLerp0 = LerpRadianceCacheSample(ZLerp00, ZLerp01, LerpAlphas.y);
FRadianceCacheSample YLerp1 = LerpRadianceCacheSample(ZLerp10, ZLerp11, LerpAlphas.y);
FRadianceCacheSample InterpolatedSample = LerpRadianceCacheSample(YLerp0, YLerp1, LerpAlphas.x);
#endif
InterpolatedSample = RadianceCacheResolveSky(InterpolatedSample, WorldSpaceDirection);
return InterpolatedSample;
}
void SampleRadianceCacheAndApply(FRadianceCacheCoverage Coverage, float3 WorldSpacePosition, float3 WorldSpaceDirection, float ConeHalfAngle, float RandomScalarForStochasticInterpolation, inout float3 Lighting, inout float Transparency, out float TraceHitDistance)
{
FRadianceCacheSample RadianceCacheSample = SampleRadianceCacheInterpolated(Coverage, WorldSpacePosition, WorldSpaceDirection, ConeHalfAngle, RandomScalarForStochasticInterpolation);
if (OverrideCacheOcclusionLighting > 0)
{
Lighting = RadianceCacheSample.Radiance;
}
else if (ShowBlackRadianceCacheLighting == 0)
{
Lighting += RadianceCacheSample.Radiance * Transparency;
}
TraceHitDistance = RadianceCacheSample.TraceHitDistance;
Transparency = 0.0f;
}
void SampleRadianceCacheAndApply(FRadianceCacheCoverage Coverage, float3 WorldSpacePosition, float3 WorldSpaceDirection, float ConeHalfAngle, float RandomScalarForStochasticInterpolation, inout float3 Lighting, inout float Transparency)
{
float UnusedTraceHitDistance;
SampleRadianceCacheAndApply(Coverage, WorldSpacePosition, WorldSpaceDirection, ConeHalfAngle, RandomScalarForStochasticInterpolation, Lighting, Transparency, UnusedTraceHitDistance);
}
float3 SampleIrradianceCacheProbe(uint ProbeIndex, float3 WorldSpaceDirection)
{
//@todo - move out of loop
float2 ProbeUV = InverseEquiAreaSphericalMapping(WorldSpaceDirection);
uint FinalIrradianceProbeResolution = IrradianceProbeResolution + 2 * (1u << FinalRadianceAtlasMaxMip);
uint2 ProbeAtlasCoord = FinalIrradianceProbeResolution * uint2(ProbeIndex & ProbeAtlasResolutionModuloMask, ProbeIndex >> ProbeAtlasResolutionDivideShift);
float2 ProbeTexelCoord = ProbeUV * IrradianceProbeResolution + (1u << FinalRadianceAtlasMaxMip);
float2 ProbeAtlasUV = (ProbeAtlasCoord + ProbeTexelCoord) * InvProbeFinalIrradianceAtlasResolution;
float3 UnmappedDebugColor = 0.0f;
// Show bright green when an unallocated probe is sampled
//UnmappedDebugColor = float3(0.0f, 10.0f, 0.0f);
return ProbeIndex == INVALID_PROBE_INDEX ? UnmappedDebugColor : RadianceCacheFinalIrradianceAtlas.SampleLevel(GlobalBilinearClampedSampler, ProbeAtlasUV, 0.0f);
}
float2 SampleProbeOcclusion(uint ProbeIndex, float3 WorldSpaceDirection)
{
float2 ProbeUV = InverseEquiAreaSphericalMapping(WorldSpaceDirection);
uint FinalOcclusionProbeResolution = OcclusionProbeResolution + 2 * (1u << FinalRadianceAtlasMaxMip);
uint2 ProbeAtlasCoord = FinalOcclusionProbeResolution * uint2(ProbeIndex & ProbeAtlasResolutionModuloMask, ProbeIndex >> ProbeAtlasResolutionDivideShift);
float2 ProbeTexelCoord = ProbeUV * OcclusionProbeResolution + (1u << FinalRadianceAtlasMaxMip);
float2 ProbeAtlasUV = (ProbeAtlasCoord + ProbeTexelCoord) / float2(ProbeAtlasResolutionInProbes * FinalOcclusionProbeResolution);
return RadianceCacheProbeOcclusionAtlas.SampleLevel(GlobalBilinearClampedSampler, ProbeAtlasUV, 0.0f);
}
float3 SampleIrradianceCacheProbeCoord(uint3 ProbeCoord, uint ProbeClipmapIndex, float3 WorldSpaceDirection)
{
uint ProbeIndex = GetProbeIndexFromIndirectionTexture(ProbeCoord, ProbeClipmapIndex);
return SampleIrradianceCacheProbe(ProbeIndex, WorldSpaceDirection);
}
float3 SampleIrradianceCacheInterpolated(float3 WorldSpacePosition, float3 WorldSpaceDirection, float3 BiasOffset, uint ClipmapIndex)
{
float3 ProbeCoordFloat = GetRadianceProbeCoordFloat(WorldSpacePosition, ClipmapIndex);
float3 CornerProbeCoordFloat = ProbeCoordFloat - .5f;
int3 CornerProbeCoord = floor(CornerProbeCoordFloat);
float3 LerpAlphas = frac(CornerProbeCoordFloat);
float3 Irradiance = 0;
float TotalWeight = 0;
#define PROBE_OCCLUSION_INTERPOLATION 1
#if PROBE_OCCLUSION_INTERPOLATION
for (uint NeighborIndex = 0; NeighborIndex < 8; NeighborIndex++)
{
uint3 ProbeOffset = uint3((NeighborIndex & 4) >> 2, (NeighborIndex & 2) >> 1, NeighborIndex & 1);
uint3 ProbeCoord = CornerProbeCoord + ProbeOffset;
uint ProbeIndex = GetProbeIndexFromIndirectionTexture(ProbeCoord, ClipmapIndex);
float3 ProbeWorldPosition = GetProbeWorldPosition(ProbeCoord, ClipmapIndex, ProbeIndex);
float3 SamplePosition = WorldSpacePosition + BiasOffset;
float3 SamplePositionToProbe = ProbeWorldPosition - SamplePosition;
float DistanceToProbe = length(SamplePositionToProbe);
float SoftFalloff = (dot(normalize(ProbeWorldPosition - WorldSpacePosition), WorldSpaceDirection) + 1) * .5f;
float Weight = SoftFalloff * SoftFalloff + .2f;
//@todo - trilinear weight ignores probe offset
float3 TrilinearWeights = max(select(ProbeOffset > 0, LerpAlphas, 1 - LerpAlphas), .001f);
Weight *= TrilinearWeights.x * TrilinearWeights.y * TrilinearWeights.z;
float2 MeanAndMeanSq = SampleProbeOcclusion(ProbeIndex, -SamplePositionToProbe);
if (DistanceToProbe > MeanAndMeanSq.x)
{
float Variance = abs(Square(MeanAndMeanSq.x) - MeanAndMeanSq.y);
float VisibilityWeight = Variance / (Variance + Square(DistanceToProbe - MeanAndMeanSq.x));
Weight *= max(VisibilityWeight * VisibilityWeight * VisibilityWeight, 0);
}
float WeightThreshold = .2f;
if (Weight < WeightThreshold)
{
Weight *= Square(Weight) / Square(WeightThreshold);
}
float3 SampleIrradiance = SampleIrradianceCacheProbe(ProbeIndex, WorldSpaceDirection);
Irradiance += sqrt(SampleIrradiance) * Weight;
TotalWeight += Weight;
}
Irradiance = Square(Irradiance / TotalWeight);
#else
float3 Lighting000 = SampleIrradianceCacheProbeCoord(CornerProbeCoord + int3(0, 0, 0), ClipmapIndex, WorldSpaceDirection);
float3 Lighting001 = SampleIrradianceCacheProbeCoord(CornerProbeCoord + int3(0, 0, 1), ClipmapIndex, WorldSpaceDirection);
float3 Lighting010 = SampleIrradianceCacheProbeCoord(CornerProbeCoord + int3(0, 1, 0), ClipmapIndex, WorldSpaceDirection);
float3 Lighting011 = SampleIrradianceCacheProbeCoord(CornerProbeCoord + int3(0, 1, 1), ClipmapIndex, WorldSpaceDirection);
float3 Lighting100 = SampleIrradianceCacheProbeCoord(CornerProbeCoord + int3(1, 0, 0), ClipmapIndex, WorldSpaceDirection);
float3 Lighting101 = SampleIrradianceCacheProbeCoord(CornerProbeCoord + int3(1, 0, 1), ClipmapIndex, WorldSpaceDirection);
float3 Lighting110 = SampleIrradianceCacheProbeCoord(CornerProbeCoord + int3(1, 1, 0), ClipmapIndex, WorldSpaceDirection);
float3 Lighting111 = SampleIrradianceCacheProbeCoord(CornerProbeCoord + int3(1, 1, 1), ClipmapIndex, WorldSpaceDirection);
float3 ZLerp00 = lerp(Lighting000, Lighting001, LerpAlphas.z);
float3 ZLerp01 = lerp(Lighting010, Lighting011, LerpAlphas.z);
float3 ZLerp10 = lerp(Lighting100, Lighting101, LerpAlphas.z);
float3 ZLerp11 = lerp(Lighting110, Lighting111, LerpAlphas.z);
float3 YLerp0 = lerp(ZLerp00, ZLerp01, LerpAlphas.y);
float3 YLerp1 = lerp(ZLerp10, ZLerp11, LerpAlphas.y);
Irradiance = lerp(YLerp0, YLerp1, LerpAlphas.x);
#endif
return Irradiance;
}