576 lines
28 KiB
HLSL
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;
|
|
} |