// 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 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 RWTraceRadianceAtlas; RWTexture2D 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 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 RWRadiosityProbeSHRedAtlas; RWTexture2D RWRadiosityProbeSHGreenAtlas; RWTexture2D 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 RadiosityProbeSHRedAtlas; Texture2D RadiosityProbeSHGreenAtlas; Texture2D 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 RWRadiosityAtlas; RWTexture2D 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; }