Files
UnrealEngine/Engine/Shaders/Private/Lumen/Radiosity/LumenRadiosity.usf
2025-05-18 13:04:45 +08:00

543 lines
20 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#include "../../Common.ush"
#include "../../MonteCarlo.ush"
#include "../../SHCommon.ush"
#include "../LumenCardCommon.ush"
#include "../LumenTracingCommon.ush"
#include "../LumenSoftwareRayTracing.ush"
#include "../LumenCardTile.ush"
#include "../LumenFloatQuantization.ush"
#include "LumenRadiosity.ush"
#include "../../RayTracing/RayGenUtils.ush"
#ifndef THREADGROUP_SIZE
#define THREADGROUP_SIZE 1
#endif
RWBuffer<uint> RWIndirectArgs;
uint HardwareRayTracingThreadGroupSize;
void SetIndirectArgs(uint ArgIndex, uint NumThreads, uint ThreadGroupSize)
{
WriteDispatchIndirectArgs(RWIndirectArgs, ArgIndex, (NumThreads + ThreadGroupSize - 1) / ThreadGroupSize, 1, 1);
}
void SetHardwareRayTracingIndirectArgs(uint ArgIndex, uint NumThreads, uint ThreadGroupSize)
{
uint NumGroups = (NumThreads + HardwareRayTracingThreadGroupSize - 1) / HardwareRayTracingThreadGroupSize;
int3 DispatchDimension = GetRayTracingThreadCountWrapped(NumGroups, ThreadGroupSize);
WriteDispatchIndirectArgs(RWIndirectArgs, ArgIndex, DispatchDimension);
}
[numthreads(THREADGROUP_SIZE, 1, 1)]
void LumenRadiosityIndirectArgsCS(uint3 DispatchThreadId : SV_DispatchThreadID)
{
uint ViewIndex = DispatchThreadId.x;
if (ViewIndex < NumViews)
{
// View offset
uint BaseOffset = ViewIndex * 5;
uint NumTraces = CardTileAllocator[ViewIndex] * RadiosityTileSize * RadiosityTileSize * NumTracesPerProbe;
// ERadiosityIndirectArgs::NumTracesDiv64
SetIndirectArgs(BaseOffset + 0, NumTraces, /*ThreadGroupSize*/ 64);
// ERadiosityIndirectArgs::NumTracesDiv32
SetIndirectArgs(BaseOffset + 1, NumTraces, /*ThreadGroupSize*/ 32);
// ERadiosityIndirectArgs::ThreadPerProbe
SetIndirectArgs(BaseOffset + 2, CardTileAllocator[ViewIndex] * RadiosityTileSize * RadiosityTileSize, /*ThreadGroupSize*/ 64);
// ERadiosityIndirectArgs::ThreadPerRadiosityTexel
SetIndirectArgs(BaseOffset + 3, CardTileAllocator[ViewIndex] * CARD_TILE_SIZE * CARD_TILE_SIZE, /*ThreadGroupSize*/ 64);
// ERadiosityIndirectArgs::HardwareRayTracingThreadPerTrace
SetHardwareRayTracingIndirectArgs(BaseOffset + 4, NumTraces, /*ThreadGroupSize*/ 64);
}
}
RWTexture2D<float3> RWTraceRadianceAtlas;
RWTexture2D<float> RWTraceHitDistanceAtlas;
float SurfaceBias;
float MinTraceDistance;
float MaxTraceDistance;
float MaxMeshSDFTraceDistance;
float MaxRayIntensity;
float CachedLightingPreExposure;
#if THREADGROUP_SIZE_32
#define RADIOSITY_TRACE_THREADGROUP_SIZE 32
#else
#define RADIOSITY_TRACE_THREADGROUP_SIZE 64
#endif
[numthreads(RADIOSITY_TRACE_THREADGROUP_SIZE, 1, 1)]
void LumenRadiosityDistanceFieldTracingCS(
uint DispatchThreadId : SV_DispatchThreadID)
{
uint CardTileIndex;
uint2 CoordInCardTile;
uint2 TraceTexelCoord;
UnswizzleTexelTraceCoords(DispatchThreadId, CardTileIndex, CoordInCardTile, TraceTexelCoord);
FRadiosityTexel RadiosityTexel = GetRadiosityTexelFromCardTile(CardTileIndex, CoordInCardTile);
if (RadiosityTexel.bInsideAtlas)
{
float3 Radiance = 0.0f;
float TraceHitDistance = MaxTraceDistance;
if (RadiosityTexel.bValid)
{
float3 WorldPosition = RadiosityTexel.WorldPosition;
float3 WorldNormal = RadiosityTexel.WorldNormal;
float3 WorldRayDirection;
float ConeHalfAngle;
float PDF;
GetRadiosityRay(RadiosityTexel, RadiosityTexel.CardCoord >> ProbeSpacingInRadiosityTexelsDivideShift, TraceTexelCoord, WorldRayDirection, ConeHalfAngle, PDF);
//#lumen_todo: derive bias from texel world size
WorldPosition += WorldNormal * SurfaceBias;
float VoxelTraceStartDistance = CalculateVoxelTraceStartDistance(MinTraceDistance, MaxTraceDistance, MaxMeshSDFTraceDistance, false);
//#lumen_todo: derive bias from texel world size
float3 SamplePosition = WorldPosition + SurfaceBias * WorldRayDirection;
float3 TranslatedSamplePosition = SamplePosition + DFHackToFloat(PrimaryView.PreViewTranslation); // LUMEN_LWC_TODO
FConeTraceInput TraceInput;
TraceInput.Setup(SamplePosition, TranslatedSamplePosition, WorldRayDirection, ConeHalfAngle, 0, MinTraceDistance, MaxTraceDistance, 1);
TraceInput.VoxelTraceStartDistance = VoxelTraceStartDistance;
TraceInput.SDFStepFactor = 1;
TraceInput.DitherScreenCoord = (RadiosityTexel.CardCoord >> ProbeSpacingInRadiosityTexelsDivideShift) + TraceTexelCoord;
FConeTraceResult TraceResult = (FConeTraceResult)0;
TraceResult.Transparency = 1;
#if TRACE_GLOBAL_SDF
{
RayTraceGlobalDistanceField(TraceInput, TraceResult);
}
#endif
if (TraceResult.Transparency < 0.5f)
{
Radiance = TraceResult.Lighting;
// Recalculate TraceHitDistance to incorporate biases
//@todo - Self intersection from grazing angle traces breaks probe occlusion
float3 HitPosition = SamplePosition + WorldRayDirection * (TraceResult.OpaqueHitDistance + TraceResult.ExpandSurfaceAmount);
TraceHitDistance = length(RadiosityTexel.WorldPosition - HitPosition);
}
else
{
Radiance = EvaluateSkyRadiance(WorldRayDirection);
}
float MaxLighting = max3(Radiance.x, Radiance.y, Radiance.z);
if (MaxLighting > MaxRayIntensity * View.OneOverPreExposure)
{
Radiance *= MaxRayIntensity * View.OneOverPreExposure / MaxLighting;
}
}
FCardTileData CardTile = GetCardTile(CardTileIndex);
FLumenCardPageData CardPage = GetLumenCardPageData(CardTile.CardPageIndex);
uint2 RadiosityProbeTracingAtlasCoord = GetRadiosityProbeAtlasCoord(CardPage, CardTile, CoordInCardTile) * HemisphereProbeResolution + TraceTexelCoord;
RWTraceRadianceAtlas[RadiosityProbeTracingAtlasCoord] = Radiance * CachedLightingPreExposure;
#if PROBE_OCCLUSION
{
RWTraceHitDistanceAtlas[RadiosityProbeTracingAtlasCoord] = TraceHitDistance;
}
#endif
}
}
RWTexture2D<float3> RWFilteredTraceRadianceAtlas;
float ProbePlaneWeightingDepthScale;
float CalculatePlaneWeight(float3 WorldPosition, float3 WorldNormal, float3 ProbeWorldPosition)
{
float4 TexelPlane = float4(WorldNormal, dot(ProbeWorldPosition, WorldNormal));
float PlaneDistance = abs(dot(float4(WorldPosition, -1), TexelPlane));
float RelativeDistance = max(PlaneDistance / (length(ProbeWorldPosition - WorldPosition) + .01f), .1f);
float DepthWeight = exp2(ProbePlaneWeightingDepthScale * (RelativeDistance * RelativeDistance));
return DepthWeight > .01f ? 1 : 0;
}
void SampleTraceRadianceAtlas(
FRadiosityTexel GatherProbeTexel,
uint2 GatherProbeAtlasCoord,
uint ResLevelPageTableOffset,
uint2 ResLevelSizeInProbes,
int2 ProbeCoordInCard,
uint2 TraceTexelCoord,
float InterpolationWeight,
inout float3 Radiance,
inout float WeightSum)
{
if (all(ProbeCoordInCard >= 0))
{
uint2 ResLevelSizeInPages = (ResLevelSizeInProbes * ProbeSpacingInRadiosityTexels) / PHYSICAL_PAGE_SIZE;
uint2 CoordInCard = ProbeCoordInCard * ProbeSpacingInRadiosityTexels;
// First find page to sample from
uint2 PageCoordInCard = CoordInCard / PHYSICAL_PAGE_SIZE;
//@todo - breaks SW tracing
//if (all(PageCoordInCard < ResLevelSizeInPages))
{
uint LinearCardPageIndex = PageCoordInCard.x + PageCoordInCard.y * ResLevelSizeInPages.x;
FLumenCardPageData CardPage = GetLumenCardPageData(ResLevelPageTableOffset + LinearCardPageIndex);
// Don't sample if page doesn't have a valid probe
if (CardPage.bMapped && CardPage.LastIndirectLightingUpdateFrameIndex != 0)
{
// Then tile and probe coordinates
uint2 CoordInCardPage = CoordInCard - (PageCoordInCard * PHYSICAL_PAGE_SIZE);
uint2 ProbeAtlasCoord = ((uint2)CardPage.PhysicalAtlasCoord + CoordInCardPage) >> ProbeSpacingInRadiosityTexelsDivideShift;
FRadiosityTexel ProbeTexel = GetRadiosityTexel(CardPage, CoordInCardPage);
if (ProbeTexel.bInsideAtlas)
{
float Weight = ProbeTexel.bValid ? InterpolationWeight : 0.0f;
#if FILTERING_PLANE_WEIGHTING
{
float PlaneWeight = CalculatePlaneWeight(GatherProbeTexel.WorldPosition, GatherProbeTexel.WorldNormal, ProbeTexel.WorldPosition);
Weight = min(Weight, PlaneWeight);
}
#endif
#if FILTERING_PROBE_OCCLUSION
{
float VisibilityWeight = CalculateProbeVisibility(GatherProbeTexel.WorldPosition, ProbeTexel, ProbeAtlasCoord);
Weight = min(Weight, VisibilityWeight);
float VisibilityWeight2 = CalculateProbeVisibility(ProbeTexel.WorldPosition, GatherProbeTexel, GatherProbeAtlasCoord);
Weight = min(Weight, VisibilityWeight2);
}
#endif
if (Weight > 0)
{
Radiance += TraceRadianceAtlas[ProbeAtlasCoord * HemisphereProbeResolution + TraceTexelCoord] * Weight;
WeightSum += Weight;
}
}
}
}
}
}
[numthreads(THREADGROUP_SIZE, 1, 1)]
void LumenRadiositySpatialFilterProbeRadiance(
uint DispatchThreadId : SV_DispatchThreadID)
{
uint CardTileIndex;
uint2 CoordInCardTile;
uint2 TraceTexelCoord;
UnswizzleTexelTraceCoords(DispatchThreadId, CardTileIndex, CoordInCardTile, TraceTexelCoord);
FRadiosityTexel RadiosityTexel = GetRadiosityTexelFromCardTile(CardTileIndex, CoordInCardTile);
if (RadiosityTexel.bInsideAtlas)
{
FCardTileData CardTile = GetCardTile(CardTileIndex);
FLumenCardPageData CardPage = GetLumenCardPageData(CardTile.CardPageIndex);
uint2 ProbeAtlasCoord = GetRadiosityProbeAtlasCoord(CardPage, CardTile, CoordInCardTile);
uint2 RadiosityProbeTracingAtlasCoord = ProbeAtlasCoord * HemisphereProbeResolution + TraceTexelCoord;
float CenterWeight = 2.0f;
float3 Radiance = TraceRadianceAtlas[RadiosityProbeTracingAtlasCoord];
if (RadiosityTexel.bValid)
{
const float E = BlueNoiseScalar(RadiosityProbeTracingAtlasCoord, RadiosityTexel.IndirectLightingTemporalIndex + 1);
Radiance *= CenterWeight;
float TotalWeight = CenterWeight;
uint2 ResLevelSizeInProbes = CardPage.ResLevelSizeInTiles * RadiosityTileSize;
uint2 CardPageProbeCoord = CardPage.CardUVRect.xy * ResLevelSizeInProbes;
int2 ProbeCoordInCard = CardPageProbeCoord + CardTile.TileCoord * RadiosityTileSize + (CoordInCardTile >> ProbeSpacingInRadiosityTexelsDivideShift);
#if FILTERING_KERNEL_SIZE == 1
const uint NumSamples = 4;
int2 NeighborOffsets[NumSamples];
NeighborOffsets[0] = int2(0, 1);
NeighborOffsets[1] = int2(1, 0);
NeighborOffsets[2] = int2(0, -1);
NeighborOffsets[3] = int2(-1, 0);
#else
const uint NumSamples = 13;
int2 NeighborOffsets[NumSamples];
NeighborOffsets[0] = int2(0, 2);
NeighborOffsets[1] = int2(-1, 1);
NeighborOffsets[2] = int2(0, 1);
NeighborOffsets[3] = int2(1, 1);
NeighborOffsets[4] = int2(-2, 0);
NeighborOffsets[5] = int2(-1, 0);
NeighborOffsets[6] = int2(0, 0);
NeighborOffsets[7] = int2(1, 0);
NeighborOffsets[8] = int2(2, 0);
NeighborOffsets[9] = int2(-1, -1);
NeighborOffsets[10] = int2(0, -1);
NeighborOffsets[11] = int2(1, -1);
NeighborOffsets[12] = int2(0, -2);
#endif
UNROLL
for (uint i = 0; i < NumSamples; i++)
{
SampleTraceRadianceAtlas(
RadiosityTexel,
ProbeAtlasCoord,
CardPage.ResLevelPageTableOffset,
ResLevelSizeInProbes,
ProbeCoordInCard + NeighborOffsets[i],
TraceTexelCoord,
1.0f,
Radiance,
TotalWeight);
}
Radiance = QuantizeForFloatRenderTarget(Radiance / TotalWeight, E);
}
RWFilteredTraceRadianceAtlas[RadiosityProbeTracingAtlasCoord] = Radiance;
}
}
RWTexture2D<float4> RWRadiosityProbeSHRedAtlas;
RWTexture2D<float4> RWRadiosityProbeSHGreenAtlas;
RWTexture2D<float4> RWRadiosityProbeSHBlueAtlas;
[numthreads(THREADGROUP_SIZE, 1, 1)]
void LumenRadiosityConvertToSH(
uint DispatchThreadId : SV_DispatchThreadID)
{
uint CardTileIndex;
uint2 CoordInCardTile;
UnswizzleCardTileIndex(DispatchThreadId, CardTileIndex, CoordInCardTile);
if (CardTileIndex < CardTileAllocator[ViewIndex])
{
FRadiosityTexel RadiosityTexel = GetRadiosityTexelFromCardTile(CardTileIndex, CoordInCardTile);
FCardTileData CardTile = GetCardTile(CardTileIndex);
FLumenCardPageData CardPage = GetLumenCardPageData(CardTile.CardPageIndex);
uint2 RadiosityProbeAtlasCoord = GetRadiosityProbeAtlasCoord(CardPage, CardTile, CoordInCardTile);
FTwoBandSHVectorRGB IrradianceSH = (FTwoBandSHVectorRGB)0;
float NumValidSamples = 0.0f;
if (RadiosityTexel.bInsideAtlas && RadiosityTexel.bValid)
{
for (uint TraceY = 0; TraceY < HemisphereProbeResolution; ++TraceY)
{
for (uint TraceX = 0; TraceX < HemisphereProbeResolution; ++TraceX)
{
uint2 TraceTexelCoord = uint2(TraceX, TraceY);
float3 TraceRadiance = TraceRadianceAtlas[RadiosityProbeAtlasCoord * HemisphereProbeResolution + TraceTexelCoord];
float3 WorldRayDirection;
float ConeHalfAngle;
float PDF;
GetRadiosityRay(RadiosityTexel, RadiosityTexel.CardCoord >> ProbeSpacingInRadiosityTexelsDivideShift, TraceTexelCoord, WorldRayDirection, ConeHalfAngle, PDF);
IrradianceSH = AddSH(IrradianceSH, MulSH(SHBasisFunction(WorldRayDirection), TraceRadiance / PDF));
NumValidSamples += 1.0f;
}
}
}
if (NumValidSamples > 0.0f)
{
IrradianceSH = MulSH(IrradianceSH, 1.0f / NumValidSamples);
}
RWRadiosityProbeSHRedAtlas[RadiosityProbeAtlasCoord] = IrradianceSH.R.V;
RWRadiosityProbeSHGreenAtlas[RadiosityProbeAtlasCoord] = IrradianceSH.G.V;
RWRadiosityProbeSHBlueAtlas[RadiosityProbeAtlasCoord] = IrradianceSH.B.V;
}
}
Texture2D<float4> RadiosityProbeSHRedAtlas;
Texture2D<float4> RadiosityProbeSHGreenAtlas;
Texture2D<float4> RadiosityProbeSHBlueAtlas;
FTwoBandSHVectorRGB GetRadiosityProbeSH(uint2 RadiosityProbeAtlasCoord)
{
float4 SHRed = RadiosityProbeSHRedAtlas.Load(int3(RadiosityProbeAtlasCoord, 0));
float4 SHGreen = RadiosityProbeSHGreenAtlas.Load(int3(RadiosityProbeAtlasCoord, 0));
float4 SHBlue = RadiosityProbeSHBlueAtlas.Load(int3(RadiosityProbeAtlasCoord, 0));
FTwoBandSHVectorRGB IrradianceSH;
IrradianceSH.R.V = SHRed;
IrradianceSH.G.V = SHGreen;
IrradianceSH.B.V = SHBlue;
return IrradianceSH;
}
void SampleRadiositySH(
uint2 TexelCoordInCard,
float3 TexelWorldPosition,
float3 TexelNormal,
uint ResLevelPageTableOffset,
uint2 ResLevelSizeInProbes,
uint2 ProbeCoordInCard,
float InterpolationWeight,
inout FTwoBandSHVectorRGB IrradianceSH,
inout float WeightSum,
inout float3 Debug)
{
if (all(ProbeCoordInCard < ResLevelSizeInProbes))
{
uint2 ResLevelSizeInPages = (ResLevelSizeInProbes * ProbeSpacingInRadiosityTexels) / PHYSICAL_PAGE_SIZE;
// First find page to sample from
uint2 PageCoordInCard = (ProbeCoordInCard * ProbeSpacingInRadiosityTexels) / PHYSICAL_PAGE_SIZE;
uint LinearCardPageIndex = PageCoordInCard.x + PageCoordInCard.y * ResLevelSizeInPages.x;
FLumenCardPageData CardPage = GetLumenCardPageData(ResLevelPageTableOffset + LinearCardPageIndex);
// Don't sample if page doesn't have a valid probe
if (CardPage.bMapped && CardPage.LastIndirectLightingUpdateFrameIndex != 0)
{
// Then tile and probe coordinates
uint2 CoordInCardPage = ProbeCoordInCard * ProbeSpacingInRadiosityTexels - (PageCoordInCard * PHYSICAL_PAGE_SIZE);
uint2 ProbeAtlasCoord = ((uint2)CardPage.PhysicalAtlasCoord + CoordInCardPage) >> ProbeSpacingInRadiosityTexelsDivideShift;
if (all(CoordInCardPage < CardPage.SizeInTexels))
{
FRadiosityTexel NeighborProbeTexel = GetRadiosityTexel(CardPage, CoordInCardPage);
float Weight = NeighborProbeTexel.bValid ? InterpolationWeight : 0.0f;
#if INTERPOLATION_PLANE_WEIGHTING
{
float PlaneWeight = CalculatePlaneWeight(TexelWorldPosition, TexelNormal, NeighborProbeTexel.WorldPosition);
Weight = min(Weight, PlaneWeight);
}
#endif
#if INTERPOLATION_PROBE_OCCLUSION
{
float VisibilityWeight = CalculateProbeVisibility(TexelWorldPosition, NeighborProbeTexel, ProbeAtlasCoord);
Weight = min(Weight, VisibilityWeight);
}
#endif
if (Weight > 0.0f)
{
FTwoBandSHVectorRGB ProbeSH = GetRadiosityProbeSH(ProbeAtlasCoord);
IrradianceSH = AddSH(IrradianceSH, MulSH(ProbeSH, Weight));
WeightSum += Weight;
}
}
}
}
}
RWTexture2D<float3> RWRadiosityAtlas;
RWTexture2D<UNORM float> RWRadiosityNumFramesAccumulatedAtlas;
[numthreads(THREADGROUP_SIZE, 1, 1)]
void LumenRadiosityIntegrateCS(
uint3 DispatchThreadId : SV_DispatchThreadID)
{
uint LinearTexelIndex = DispatchThreadId.x;
uint NumTexelsPerTile = CARD_TILE_SIZE * CARD_TILE_SIZE;
uint CardTileIndex = LinearTexelIndex / NumTexelsPerTile;
const uint LinearIndexInCardTile = LinearTexelIndex - CardTileIndex * NumTexelsPerTile;
uint2 CoordInCardTile = uint2(LinearIndexInCardTile % CARD_TILE_SIZE, LinearIndexInCardTile / CARD_TILE_SIZE);
float3 TexelRadiance = float3(0.0f, 0.0f, 0.0f);
float3 Debug = 0;
float WeightSum = 0.0f;
float NewNumFramesAccumulated = 0;
FRadiosityTexel RadiosityTexel = GetRadiosityTexelFromCardTile(CardTileIndex, CoordInCardTile);
if (RadiosityTexel.bInsideAtlas && RadiosityTexel.bValid)
{
FCardTileData CardTile = GetCardTile(CardTileIndex);
FLumenCardPageData CardPage = GetLumenCardPageData(CardTile.CardPageIndex);
FLumenCardData Card = GetLumenCardData(CardPage.CardIndex);
//@todo - seam if CardPage.IndirectLightingTemporalIndex different
uint2 CoordInCard = (CardPage.CardUVRect.xy * CardPage.ResLevelSizeInTiles + CardTile.TileCoord) * CARD_TILE_SIZE + CoordInCardTile;
uint2 ProbeFullResCoord = max((float2)CoordInCard - GetProbeJitter(CardPage.IndirectLightingTemporalIndex), 0.0f);
uint2 ProbeCoord00 = ProbeFullResCoord >> ProbeSpacingInRadiosityTexelsDivideShift;
uint2 ProbeCoord10 = ProbeCoord00 + uint2(1, 0);
uint2 ProbeCoord01 = ProbeCoord00 + uint2(0, 1);
uint2 ProbeCoord11 = ProbeCoord00 + uint2(1, 1);
// Guarantee that no probe will have a bilinear weight of zero, when other probes might be discarded due to occlusion
float BilinearExpand = 1;
float2 BilinearWeights = ((float2)ProbeFullResCoord - ProbeCoord00 * ProbeSpacingInRadiosityTexels + BilinearExpand) / (float)(ProbeSpacingInRadiosityTexels + 2 * BilinearExpand);
float4 Weights = float4(
(1.0f - BilinearWeights.x) * (1.0f - BilinearWeights.y),
BilinearWeights.x * (1.0f - BilinearWeights.y),
(1.0f - BilinearWeights.x) * BilinearWeights.y,
BilinearWeights.x * BilinearWeights.y);
FTwoBandSHVectorRGB IrradianceSHWeighted = (FTwoBandSHVectorRGB)0;
uint2 ResLevelSizeInProbes = CardPage.ResLevelSizeInTiles * RadiosityTileSize;
SampleRadiositySH(CoordInCard, RadiosityTexel.WorldPosition, RadiosityTexel.WorldNormal, CardPage.ResLevelPageTableOffset, ResLevelSizeInProbes, ProbeCoord00, Weights.x, IrradianceSHWeighted, WeightSum, Debug);
SampleRadiositySH(CoordInCard, RadiosityTexel.WorldPosition, RadiosityTexel.WorldNormal, CardPage.ResLevelPageTableOffset, ResLevelSizeInProbes, ProbeCoord10, Weights.y, IrradianceSHWeighted, WeightSum, Debug);
SampleRadiositySH(CoordInCard, RadiosityTexel.WorldPosition, RadiosityTexel.WorldNormal, CardPage.ResLevelPageTableOffset, ResLevelSizeInProbes, ProbeCoord01, Weights.z, IrradianceSHWeighted, WeightSum, Debug);
SampleRadiositySH(CoordInCard, RadiosityTexel.WorldPosition, RadiosityTexel.WorldNormal, CardPage.ResLevelPageTableOffset, ResLevelSizeInProbes, ProbeCoord11, Weights.w, IrradianceSHWeighted, WeightSum, Debug);
FTwoBandSHVector DiffuseTransferSH = CalcDiffuseTransferSH(RadiosityTexel.WorldNormal, 1.0f);
TexelRadiance = max(float3(0.0f, 0.0f, 0.0f), DotSH(IrradianceSHWeighted, DiffuseTransferSH));
if (WeightSum > 0.0f)
{
TexelRadiance /= WeightSum;
}
#if TEMPORAL_ACCUMULATION
float NumFramesAccumulated = RWRadiosityNumFramesAccumulatedAtlas[RadiosityTexel.AtlasCoord] * 255.0f;
NewNumFramesAccumulated = min(NumFramesAccumulated + 1, (float)MaxFramesAccumulated);
float Alpha = 1.0f / (1.0f + NumFramesAccumulated);
float3 HistoryRadiosity = RWRadiosityAtlas[RadiosityTexel.AtlasCoord];
TexelRadiance = lerp(HistoryRadiosity, TexelRadiance, Alpha);
TexelRadiance = QuantizeForFloatRenderTarget(TexelRadiance, int3(RadiosityTexel.AtlasCoord, CardPage.IndirectLightingTemporalIndex + 2));
#endif
////////////////////////
//TexelRadiance = Debug;
//TexelRadiance = WeightSum > 0 ? 1 : 0;
#define DEBUG_VISUALIZE_PROBE_PLACEMENT 0
#if DEBUG_VISUALIZE_PROBE_PLACEMENT
if (all(CoordInCard == ProbeCoord00 * ProbeSpacingInRadiosityTexels + GetProbeJitter(CardPage.IndirectLightingTemporalIndex)))
{
TexelRadiance = float3(10, 0, 10);
}
#endif
}
RWRadiosityNumFramesAccumulatedAtlas[RadiosityTexel.AtlasCoord] = NewNumFramesAccumulated / 255.0f;
RWRadiosityAtlas[RadiosityTexel.AtlasCoord] = TexelRadiance;
}