1080 lines
39 KiB
HLSL
1080 lines
39 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#ifndef NUM_SAMPLES_PER_PIXEL_1D
|
|
#define NUM_SAMPLES_PER_PIXEL_1D 1
|
|
#endif
|
|
|
|
#define SUPPORT_CONTACT_SHADOWS 0
|
|
|
|
#ifndef USE_RECT_LIGHT
|
|
#define USE_RECT_LIGHT 0
|
|
#endif
|
|
|
|
#if USE_RECT_LIGHT
|
|
#define USE_SOURCE_TEXTURE 1
|
|
#endif
|
|
|
|
#include "../Common.ush"
|
|
#include "../BlueNoise.ush"
|
|
#include "LumenSceneDirectLightingStochastic.ush"
|
|
#include "../MegaLights/MegaLightsRayTracing.ush"
|
|
#include "../DeferredLightingCommon.ush"
|
|
#include "../IntersectionUtils.ush"
|
|
#include "../ShaderPrint.ush"
|
|
|
|
#if SHADER_STANDALONE_EVALUATE
|
|
#if LIGHT_FUNCTION
|
|
#include "/Engine/Generated/Material.ush"
|
|
#include "../LightFunctionCommon.ush"
|
|
#endif
|
|
#if USE_CLOUD_TRANSMITTANCE
|
|
#include "../VolumetricCloudCommon.ush"
|
|
#endif
|
|
#endif // SHADER_STANDALONE_EVALUATE
|
|
|
|
#include "LumenCardCommon.ush"
|
|
#include "LumenCardTile.ush"
|
|
#include "LumenSceneLighting.ush"
|
|
#include "LumenSceneDirectLighting.ush"
|
|
#include "SurfaceCache/LumenSurfaceCache.ush"
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Helper functions
|
|
|
|
// Transient atlas coord used for storing card tile data during the update pass.
|
|
// The coord are only valid for the current frame. These atlas coords are allocated with a simple
|
|
// linear allocator
|
|
struct FTransientCoord
|
|
{
|
|
uint2 TileCoord;
|
|
uint2 TexelCoord;
|
|
uint2 BaseTexelCoord;
|
|
uint2 TexelCoordWithinTile;
|
|
};
|
|
|
|
uint2 GetTile1dToTile2D(uint LinearTileIndex)
|
|
{
|
|
//return uint2(LinearTileIndex & 0x7F, LinearTileIndex >> 7u);
|
|
return uint2(LinearTileIndex % 128u, LinearTileIndex / 128u);
|
|
}
|
|
|
|
FTransientCoord GetTransientCoord(uint LinearTileIndex, uint2 GroupThreadId)
|
|
{
|
|
FTransientCoord Out;
|
|
Out.TileCoord = GetTile1dToTile2D(LinearTileIndex);
|
|
Out.TexelCoord = Out.TileCoord * CARD_TILE_SIZE + GroupThreadId.xy;
|
|
Out.BaseTexelCoord = Out.TileCoord * CARD_TILE_SIZE;
|
|
Out.TexelCoordWithinTile = GroupThreadId.xy;
|
|
return Out;
|
|
}
|
|
|
|
// Init debug context for the texel under the mouse cursor
|
|
FShaderPrintContext InitDebugContext(StructuredBuffer<uint> InLumenSceneDebugData, FCardTileData InCardTile, FLumenCardPageData InCardPage, float2 InAtlasUV, uint2 InStartCoord = uint2(50, 50))
|
|
{
|
|
FShaderPrintContext Out = InitShaderPrintContext(false, 0);
|
|
const FLumenSceneDebugData DebugData = ReadDebugData(InLumenSceneDebugData);
|
|
if (DebugData.bValid && DebugData.CardPageIndex == InCardTile.CardPageIndex)
|
|
{
|
|
const bool bDebug = all(uint2(DebugData.PhysicalAtlasUV/InCardPage.PhysicalAtlasUVTexelScale) == uint2(InAtlasUV/InCardPage.PhysicalAtlasUVTexelScale));
|
|
if (bDebug)
|
|
{
|
|
Out = InitShaderPrintContext(true, InStartCoord);
|
|
}
|
|
}
|
|
return Out;
|
|
}
|
|
|
|
struct FLightTargetPDF
|
|
{
|
|
float3 Diffuse;
|
|
float Weight;
|
|
};
|
|
|
|
FLightTargetPDF InitLightTargetPDF()
|
|
{
|
|
FLightTargetPDF LightTargetPDF;
|
|
LightTargetPDF.Diffuse = 0.0f;
|
|
LightTargetPDF.Weight = 0.0f;
|
|
return LightTargetPDF;
|
|
}
|
|
|
|
FLightTargetPDF GetLocalLightTargetPDF(FDeferredLightData LightData, float3 TranslatedWorldPosition, float3 WorldNormal, float Exposure)
|
|
{
|
|
FLightTargetPDF LightTargetPDF = InitLightTargetPDF();
|
|
float3 CameraVector = normalize(TranslatedWorldPosition - View.TranslatedWorldCameraOrigin);
|
|
LightTargetPDF.Diffuse = GetIrradianceForLight(LightData, WorldNormal, TranslatedWorldPosition, USE_RECT_LIGHT);
|
|
|
|
#if USE_IES_PROFILE
|
|
if (LightData.IESAtlasIndex >= 0 && Luminance(LightTargetPDF.Diffuse) > 0.01f)
|
|
{
|
|
const float LightProfileMult = ComputeLightProfileMultiplier(TranslatedWorldPosition, LightData.TranslatedWorldPosition, -LightData.Direction, LightData.Tangent, LightData.IESAtlasIndex);
|
|
LightTargetPDF.Diffuse *= LightProfileMult;
|
|
}
|
|
#endif
|
|
|
|
#if USE_LIGHT_FUNCTION_ATLAS
|
|
if (LightData.LightFunctionAtlasLightIndex > 0 && Luminance(LightTargetPDF.Diffuse) > 0.01f)
|
|
{
|
|
LightTargetPDF.Diffuse *= GetLocalLightFunctionCommon(TranslatedWorldPosition, LightData.LightFunctionAtlasLightIndex);
|
|
}
|
|
#endif
|
|
|
|
// Simulate tonemapping
|
|
LightTargetPDF.Weight = log2(Luminance(LightTargetPDF.Diffuse) + 1.0f);
|
|
|
|
return LightTargetPDF;
|
|
}
|
|
|
|
#if SHADER_GENERATE_SAMPLE || SHADER_SHADING
|
|
FLumenLight GetLumenLightData(uint LightIndex, uint ViewIndex)
|
|
{
|
|
const FDFVector3 PreViewTranslation = GetPreViewTranslation(ViewIndex);
|
|
return LoadLumenLight(LightIndex, DFHackToFloat(PreViewTranslation), ViewExposure[ViewIndex]);
|
|
}
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if SHADER_GENERATE_SAMPLE
|
|
|
|
bool DoesLightAffectCardPageUVRange(FLumenLight LumenLight, FLumenCardPageData CardPage, FLumenCardData Card, float2 UVMin, float2 UVMax, inout float3 OutCardPageWorldCenter)
|
|
{
|
|
// Lighting channels test
|
|
if (!(Card.LightingChannelMask & LumenLight.LightingChannelMask))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
float3 CardPageLocalCenter;
|
|
float3 CardPageLocalExtent;
|
|
GetCardLocalBBox(CardPage, Card, UVMin, UVMax, CardPageLocalCenter, CardPageLocalExtent);
|
|
|
|
float3 CardPageWorldCenter = mul(Card.WorldToLocalRotation, CardPageLocalCenter) + Card.Origin;
|
|
float3 CardPageWorldExtent = mul(abs(Card.WorldToLocalRotation), CardPageLocalExtent);
|
|
float CardPageWorldBoundingSphere = length(CardPageLocalExtent);
|
|
OutCardPageWorldCenter = CardPageWorldCenter;
|
|
|
|
float4 InfluenceSphere = LumenLight.InfluenceSphere;
|
|
float3 LightInfluenceSphereLocalCenter = mul(InfluenceSphere.xyz - Card.Origin, Card.WorldToLocalRotation);
|
|
const float BoxDistanceSq = ComputeSquaredDistanceFromBoxToPoint(CardPageLocalCenter, CardPageLocalExtent, LightInfluenceSphereLocalCenter);
|
|
const bool bCardAffectedByInfluenceSphere = BoxDistanceSq < InfluenceSphere.w * InfluenceSphere.w;
|
|
|
|
const uint LightType = LumenLight.Type;
|
|
const float3 LightPosition = LumenLight.ProxyPosition;
|
|
const float3 LightDirection = LumenLight.ProxyDirection;
|
|
const float LightRadius = LumenLight.ProxyRadius;
|
|
|
|
// Fast out
|
|
if (LightType != LIGHT_TYPE_DIRECTIONAL && !bCardAffectedByInfluenceSphere)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (LightType == LIGHT_TYPE_DIRECTIONAL)
|
|
{
|
|
return true;
|
|
}
|
|
else if (LightType == LIGHT_TYPE_POINT)
|
|
{
|
|
// Point light
|
|
return bCardAffectedByInfluenceSphere;
|
|
}
|
|
else if (LightType == LIGHT_TYPE_SPOT)
|
|
{
|
|
float CosConeAngle = LumenLight.CosConeAngle;
|
|
float SinConeAngle = LumenLight.SinConeAngle;
|
|
|
|
float ConeAxisDistance = dot(CardPageWorldCenter - LightPosition, LightDirection);
|
|
float2 ConeAxisDistanceMinMax = float2(ConeAxisDistance + CardPageWorldBoundingSphere, ConeAxisDistance - CardPageWorldBoundingSphere);
|
|
|
|
// Spot light
|
|
return bCardAffectedByInfluenceSphere
|
|
&& SphereIntersectCone(float4(CardPageWorldCenter, CardPageWorldBoundingSphere), LightPosition, LightDirection, CosConeAngle, SinConeAngle)
|
|
&& ConeAxisDistanceMinMax.x > 0 && ConeAxisDistanceMinMax.y < LightRadius;
|
|
}
|
|
#if USE_RECT_LIGHT
|
|
else if (LightType == LIGHT_TYPE_RECT)
|
|
{
|
|
// Rect light
|
|
float4 BackPlane = float4(LightDirection, dot(LightPosition, LightDirection));
|
|
float DistanceFromBoxCenterToPlane = dot(BackPlane.xyz, CardPageWorldCenter) - BackPlane.w;
|
|
float MaxExtent = dot(CardPageWorldExtent, abs(BackPlane.xyz));
|
|
bool bInFrontOfPlane = DistanceFromBoxCenterToPlane + MaxExtent > 0.0f;
|
|
return bCardAffectedByInfluenceSphere && bInFrontOfPlane;
|
|
}
|
|
#endif
|
|
|
|
// Error: Unknown light type
|
|
return false;
|
|
}
|
|
|
|
uint MaxCompositeTiles;
|
|
float SamplingMinWeight;
|
|
uint NumSamplesPerPixel1d;
|
|
uint NumLights;
|
|
uint NumStandaloneLights;
|
|
uint NumViews;
|
|
uint StateFrameIndex;
|
|
|
|
RWTexture2D<float4> RWSceneData;
|
|
RWTexture2D<uint> RWUniqueLightIndices;
|
|
RWTexture2D<uint> RWUniqueLightCount;
|
|
RWStructuredBuffer<uint> RWCardTilePerLightCounters;
|
|
|
|
RWTexture2D<float> RWDiffuseLighting;
|
|
RWTexture2D<float> RWSampleLuminanceSum;
|
|
RWTexture2DArray<uint> RWLightSamples;
|
|
RWTexture2DArray<float4> RWSampleDiffuseLighting;
|
|
|
|
float4 HistoryScreenPositionScaleBias;
|
|
float4 HistoryUVMinMax;
|
|
|
|
// Workaround for a console shader compiler bug generating incorrect code. Likely can be removed in next SDK.
|
|
uint DummyZeroForFixingShaderCompilerBug;
|
|
|
|
groupshared uint SharedCandidateLightCount;
|
|
groupshared uint SharedCandidateLightHiMask;
|
|
groupshared uint SharedCandidateLightMask[SHARED_LIGHT_MASK_SIZE];
|
|
groupshared uint SharedStandaloneLightMask[SHARED_LIGHT_MASK_SIZE];
|
|
|
|
StructuredBuffer<uint> TileAllocator;
|
|
StructuredBuffer<uint> TileData;
|
|
StructuredBuffer<uint> LumenSceneDebugData;
|
|
|
|
#define DEBUG_ENABLE 0
|
|
|
|
#if THREADGROUP_SIZE != 8
|
|
#error The code assume THREADGROUP_SIZE == 8
|
|
#endif
|
|
#define THREADGROUP_COUNT 64
|
|
/**
|
|
* Run one thread per sample and generate new light samples for tracing
|
|
*/
|
|
[numthreads(THREADGROUP_SIZE, THREADGROUP_SIZE, 1)]
|
|
void GenerateLightSamplesCS(
|
|
uint3 GroupId : SV_GroupID,
|
|
uint3 GroupThreadId : SV_GroupThreadID,
|
|
uint LinearThreadIndex : SV_GroupIndex,
|
|
uint3 DispatchThreadId : SV_DispatchThreadID)
|
|
{
|
|
if (LinearThreadIndex == 0)
|
|
{
|
|
SharedCandidateLightHiMask = 0;
|
|
SharedCandidateLightCount = 0;
|
|
}
|
|
|
|
if (LinearThreadIndex < SHARED_LIGHT_MASK_SIZE)
|
|
{
|
|
SharedCandidateLightMask[LinearThreadIndex] = 0;
|
|
SharedStandaloneLightMask[LinearThreadIndex] = 0;
|
|
}
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
uint TileIndex = GroupId.x;
|
|
if (TileIndex < TileAllocator[0])
|
|
{
|
|
uint LocalCandidateLightHiMask = 0;
|
|
|
|
// 1. Load cards data
|
|
const uint CardTileIndex = TileIndex;
|
|
const FCardTileData CardTile = UnpackCardTileData(TileData[CardTileIndex]);
|
|
const FLumenCardPageData CardPage = GetLumenCardPageData(CardTile.CardPageIndex + DummyZeroForFixingShaderCompilerBug);
|
|
const uint2 CoordInCardTile = GroupThreadId.xy;
|
|
const uint2 TexelInCardPageCoord = CardTile.TileCoord * CARD_TILE_SIZE + CoordInCardTile;
|
|
const float2 AtlasUV = CardPage.PhysicalAtlasUVRect.xy + CardPage.PhysicalAtlasUVTexelScale * (TexelInCardPageCoord + 0.5f);
|
|
const float2 CardUV = CardPage.CardUVRect.xy + CardPage.CardUVTexelScale * (TexelInCardPageCoord + 0.5f);
|
|
|
|
const FLumenCardData Card = GetLumenCardData(CardPage.CardIndex);
|
|
const FLumenSurfaceCacheData SurfaceCacheData = GetSurfaceCacheData(Card, CardUV, AtlasUV);
|
|
|
|
const uint2 SizeInTiles = CardPage.SizeInTexels / CARD_TILE_SIZE;
|
|
float2 UVMin = float2(CardTile.TileCoord) / SizeInTiles;
|
|
float2 UVMax = float2(CardTile.TileCoord + 1) / SizeInTiles;
|
|
float SwapY = UVMin.y;
|
|
UVMin.y = 1.0f - UVMax.y;
|
|
UVMax.y = 1.0f - SwapY;
|
|
|
|
// Debug
|
|
#if DEBUG_ENABLE
|
|
FShaderPrintContext Ctx = InitDebugContext(LumenSceneDebugData, CardTile, CardPage, AtlasUV);
|
|
#endif
|
|
|
|
const FTransientCoord TransientCoord = GetTransientCoord(TileIndex, GroupThreadId.xy);
|
|
const uint ViewIndex = GetCardViewIndex(CardPage, Card, UVMin, UVMax, float2(0, 1), NumViews, true);
|
|
|
|
// 2. Cull & Sample lights
|
|
{
|
|
// 2.1 Cull lights - 1 light per lane
|
|
{
|
|
const uint PassCount = DivideAndRoundUp64(NumLights);
|
|
for (uint PassIt = 0; PassIt < NumLights; ++PassIt)
|
|
{
|
|
const uint LightIndex = LinearThreadIndex + THREADGROUP_COUNT * PassIt;
|
|
if (LightIndex < NumLights && LightIndex < MAX_LOCAL_LIGHT_INDEX)
|
|
{
|
|
const FDFVector3 PreViewTranslation = GetPreViewTranslation(ViewIndex);
|
|
const FLumenLight LumenLight = LoadLumenLight(LightIndex, DFHackToFloat(PreViewTranslation), ViewExposure[ViewIndex]);
|
|
|
|
float3 CardPageWorldCenter = 0.0f; // LWC_TODO:
|
|
bool bLightAffectsCard = DoesLightAffectCardPageUVRange(LumenLight, CardPage, Card, UVMin, UVMax, CardPageWorldCenter);
|
|
if (bLightAffectsCard)
|
|
{
|
|
uint DWORDIndex = LightIndex / 32;
|
|
uint BitMask = 1u << (LightIndex % 32);
|
|
InterlockedOr(SharedCandidateLightMask[DWORDIndex], BitMask);
|
|
if (LumenLight.bIsStandaloneLight)
|
|
{
|
|
InterlockedOr(SharedStandaloneLightMask[DWORDIndex], BitMask);
|
|
}
|
|
uint HiBitMask = 1u << DWORDIndex;
|
|
LocalCandidateLightHiMask |= HiBitMask;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
uint WaveHiMask = WaveActiveBitOr(LocalCandidateLightHiMask);
|
|
if (WaveIsFirstLane())
|
|
{
|
|
InterlockedOr(SharedCandidateLightHiMask, WaveHiMask);
|
|
}
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
if (LinearThreadIndex < SHARED_LIGHT_MASK_SIZE)
|
|
{
|
|
const uint LocalLightCount = countbits(SharedCandidateLightMask[LinearThreadIndex]);
|
|
InterlockedAdd(SharedCandidateLightCount, LocalLightCount);
|
|
}
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
// 2.2 Sample lights - 1 texel per lane
|
|
{
|
|
uint LocalLightIndices[NUM_SAMPLES_PER_PIXEL_1D];
|
|
float3 SampleDiffuseLighting[NUM_SAMPLES_PER_PIXEL_1D];
|
|
FLightSample LightSamples[NUM_SAMPLES_PER_PIXEL_1D];
|
|
for (uint LightSampleIndex = 0; LightSampleIndex < NUM_SAMPLES_PER_PIXEL_1D; ++LightSampleIndex)
|
|
{
|
|
LightSamples[LightSampleIndex] = InitLightSample();
|
|
SampleDiffuseLighting[LightSampleIndex] = 0.f;
|
|
LocalLightIndices[LightSampleIndex] = MAX_LOCAL_LIGHT_INDEX;
|
|
}
|
|
|
|
float3 DiffuseSum = 0.0f;
|
|
float WeightSum = 0.0f;
|
|
|
|
if (SurfaceCacheData.bValid)
|
|
{
|
|
const FDFVector3 PreViewTranslation = GetPreViewTranslation(ViewIndex);
|
|
const float3 TranslatedWorldPosition = SurfaceCacheData.WorldPosition + DFHackToFloat(PreViewTranslation);
|
|
{
|
|
// Initialize random variables using spatiotemporal Blue Noise
|
|
float LightIndexRandom[NUM_SAMPLES_PER_PIXEL_1D];
|
|
{
|
|
float RandomScalar = BlueNoiseScalar(TexelInCardPageCoord, StateFrameIndex);
|
|
|
|
for (uint LightSampleIndex = 0; LightSampleIndex < NUM_SAMPLES_PER_PIXEL_1D; ++LightSampleIndex)
|
|
{
|
|
LightIndexRandom[LightSampleIndex] = (RandomScalar + LightSampleIndex) / NUM_SAMPLES_PER_PIXEL_1D;
|
|
}
|
|
}
|
|
|
|
// Iterate through all the light affecting the card tile
|
|
uint CandidateLightHiMask = SharedCandidateLightHiMask;
|
|
while (CandidateLightHiMask != 0)
|
|
{
|
|
const uint NextHiBitIndex = firstbitlow(CandidateLightHiMask);
|
|
const uint NextHiBitMask = 1u << NextHiBitIndex;
|
|
CandidateLightHiMask ^= NextHiBitMask;
|
|
|
|
const uint MaskIndex = NextHiBitIndex;
|
|
uint CandidateLightMask = SharedCandidateLightMask[MaskIndex];
|
|
|
|
while (CandidateLightMask != 0)
|
|
{
|
|
const uint NextBitIndex = firstbitlow(CandidateLightMask);
|
|
const uint NextBitMask = 1u << NextBitIndex;
|
|
CandidateLightMask ^= NextBitMask;
|
|
|
|
const uint LocalLightIndex = MaskIndex * 32 + NextBitIndex;
|
|
const FDeferredLightData LightData = GetLumenLightData(LocalLightIndex, ViewIndex).DeferredLightData;
|
|
|
|
FLightTargetPDF LightTargetPDF = GetLocalLightTargetPDF(LightData, TranslatedWorldPosition, SurfaceCacheData.WorldNormal, ViewExposure[ViewIndex]);
|
|
if (LightTargetPDF.Weight > SamplingMinWeight)
|
|
{
|
|
float Tau = WeightSum / (WeightSum + LightTargetPDF.Weight);
|
|
WeightSum += LightTargetPDF.Weight;
|
|
DiffuseSum += LightTargetPDF.Diffuse;
|
|
|
|
for (uint LightSampleIndex = 0; LightSampleIndex < NUM_SAMPLES_PER_PIXEL_1D; ++LightSampleIndex)
|
|
{
|
|
if (LightIndexRandom[LightSampleIndex] < Tau)
|
|
{
|
|
LightIndexRandom[LightSampleIndex] /= Tau;
|
|
}
|
|
else
|
|
{
|
|
// Select this sample
|
|
LightIndexRandom[LightSampleIndex] = (LightIndexRandom[LightSampleIndex] - Tau) / (1.0f - Tau);
|
|
LightSamples[LightSampleIndex].LocalLightIndex = LocalLightIndex;
|
|
LightSamples[LightSampleIndex].Weight = LightTargetPDF.Weight;
|
|
SampleDiffuseLighting[LightSampleIndex] = LightTargetPDF.Diffuse;
|
|
}
|
|
|
|
LightIndexRandom[LightSampleIndex] = clamp(LightIndexRandom[LightSampleIndex], 0, 0.9999f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Deduplicate samples selecting the same light
|
|
#define DEDUPLICATE_SAMPLE 0
|
|
#if DEDUPLICATE_SAMPLE
|
|
for (uint LightSampleIndex = 0; LightSampleIndex < NUM_SAMPLES_PER_PIXEL_1D; ++LightSampleIndex)
|
|
{
|
|
LocalLightIndices[LightSampleIndex] = LightSamples[LightSampleIndex].LocalLightIndex;
|
|
}
|
|
for (uint LightSampleIndex = 0; LightSampleIndex < NUM_SAMPLES_PER_PIXEL_1D; ++LightSampleIndex)
|
|
{
|
|
for (uint SubLightSampleIndex = LightSampleIndex+1; SubLightSampleIndex < NUM_SAMPLES_PER_PIXEL_1D; ++SubLightSampleIndex)
|
|
{
|
|
if (LocalLightIndices[LightSampleIndex] == LocalLightIndices[SubLightSampleIndex])
|
|
{
|
|
LocalLightIndices[SubLightSampleIndex] = MAX_LOCAL_LIGHT_INDEX;
|
|
SampleDiffuseLighting[SubLightSampleIndex] = 0;
|
|
|
|
LightSamples[LightSampleIndex].Weight += LightSamples[LightSampleIndex].Weight;
|
|
SampleDiffuseLighting[LightSampleIndex] += SampleDiffuseLighting[SubLightSampleIndex];
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Store sample scene data (position, normal, view index) to avoid loading card data during HW tracing
|
|
RWSceneData[TransientCoord.TexelCoord] = PackLumenSampleSceneData(TranslatedWorldPosition, SurfaceCacheData.WorldNormal, ViewIndex, Card.bHeightfield);
|
|
|
|
#if DEBUG_ENABLE
|
|
if (Ctx.bIsActive)
|
|
{
|
|
AddCrossTWS(Ctx, TranslatedWorldPosition, 100.f, ColorYellow);
|
|
Print(Ctx, TEXT("#Samples "), FontWhite); Print(Ctx, uint(NUM_SAMPLES_PER_PIXEL_1D), FontYellow); Newline(Ctx);
|
|
Print(Ctx, TEXT("WeightSum "), FontWhite); Print(Ctx, WeightSum, FontYellow); Newline(Ctx);
|
|
Newline(Ctx);
|
|
}
|
|
#endif
|
|
|
|
} // SurfaceCacheData.bValid
|
|
|
|
RWSampleLuminanceSum[TransientCoord.TexelCoord] = Luminance(DiffuseSum);
|
|
|
|
for (uint LightSampleIndex = 0; LightSampleIndex < NUM_SAMPLES_PER_PIXEL_1D; ++LightSampleIndex)
|
|
{
|
|
FLightSample LightSample = LightSamples[LightSampleIndex];
|
|
#if DEBUG_ENABLE
|
|
if (Ctx.bIsActive)
|
|
{
|
|
Print(Ctx, TEXT("Sample"), FontRed); Newline(Ctx);
|
|
Print(Ctx, TEXT("Weight "), FontWhite); Print(Ctx, LightSample.Weight, FontYellow); Newline(Ctx);
|
|
}
|
|
#endif
|
|
|
|
#if DEDUPLICATE_SAMPLE
|
|
const bool bValid = LocalLightIndices[LightSampleIndex] != MAX_LOCAL_LIGHT_INDEX;
|
|
#else
|
|
const bool bValid = LightSample.LocalLightIndex != MAX_LOCAL_LIGHT_INDEX;
|
|
#endif
|
|
if (bValid)
|
|
{
|
|
const bool bCastShadows = GetLumenLightData(LightSample.LocalLightIndex, ViewIndex).bHasShadowMask;
|
|
|
|
LightSample.bVisible = true;
|
|
LightSample.bCompleted = bCastShadows ? false : true;
|
|
LightSample.Weight = WeightSum / (NUM_SAMPLES_PER_PIXEL_1D * LightSample.Weight);
|
|
|
|
FShaderPrintContext Ctx2 = InitShaderPrintContext(true, uint2(0, 0));
|
|
uint OutOffset = 0;
|
|
SHADER_PRINT_INTERLOCKEDADD(SHADER_PRINT_RWENTRYBUFFER(Ctx2, SHADER_PRINT_COUNTER_OFFSET_FREE), 1, OutOffset);
|
|
}
|
|
else
|
|
{
|
|
LightSample.bVisible = false;
|
|
LightSample.bCompleted = true;
|
|
LightSample.Weight = 0;
|
|
}
|
|
|
|
#if DEBUG_ENABLE
|
|
if (Ctx.bIsActive)
|
|
{
|
|
Print(Ctx, TEXT("Weight "), FontWhite); Print(Ctx, LightSample.Weight, FontYellow); Newline(Ctx);
|
|
Print(Ctx, TEXT("bVisible "), FontWhite); Print(Ctx, LightSample.bVisible, FontYellow); Newline(Ctx);
|
|
Print(Ctx, TEXT("bCompleted "), FontWhite); Print(Ctx, LightSample.bCompleted, FontYellow); Newline(Ctx);
|
|
Print(Ctx, TEXT("LightIndex "), FontWhite); Print(Ctx, LightSample.LocalLightIndex, FontYellow); Newline(Ctx);
|
|
Print(Ctx, TEXT("Diffuse "), FontWhite); Print(Ctx, SampleDiffuseLighting[LightSampleIndex], FontYellow); Newline(Ctx);
|
|
}
|
|
#endif
|
|
|
|
RWLightSamples[uint3(TransientCoord.TexelCoord, LightSampleIndex)] = PackLightSample(LightSample);
|
|
RWSampleDiffuseLighting[uint3(TransientCoord.TexelCoord, LightSampleIndex)] = float4(SampleDiffuseLighting[LightSampleIndex], bValid ? 1 : 0);
|
|
}
|
|
}
|
|
}
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
// 3. Store list of unique (standalone) lights per tile
|
|
// Make this parallel, or computed within the loop above
|
|
if (LinearThreadIndex == 0 && NumStandaloneLights > 0)
|
|
{
|
|
uint CandidateLightHiMask = SharedCandidateLightHiMask;
|
|
uint UniqueLightCount = 0;
|
|
while (CandidateLightHiMask != 0)
|
|
{
|
|
const uint NextHiBitIndex = firstbitlow(CandidateLightHiMask);
|
|
const uint NextHiBitMask = 1u << NextHiBitIndex;
|
|
CandidateLightHiMask ^= NextHiBitMask;
|
|
const uint MaskIndex = NextHiBitIndex;
|
|
uint CandidateLightMask = SharedStandaloneLightMask[MaskIndex];
|
|
while (CandidateLightMask != 0)
|
|
{
|
|
const uint NextBitIndex = firstbitlow(CandidateLightMask);
|
|
const uint NextBitMask = 1u << NextBitIndex;
|
|
CandidateLightMask ^= NextBitMask;
|
|
|
|
const uint LocalLightIndex = MaskIndex * 32 + NextBitIndex;
|
|
const uint2 Offset = uint2(UniqueLightCount % 8u, UniqueLightCount / 8u);
|
|
|
|
RWUniqueLightIndices[TransientCoord.TileCoord * CARD_TILE_SIZE + Offset] = LocalLightIndex;
|
|
InterlockedAdd(RWCardTilePerLightCounters[LocalLightIndex], 1u);
|
|
++UniqueLightCount;
|
|
}
|
|
}
|
|
RWUniqueLightCount[TransientCoord.TileCoord] = UniqueLightCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // SHADER_GENERATE_SAMPLE
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if SHADER_COMPACTION
|
|
|
|
#include "../PackUnpack.ush"
|
|
|
|
int2 SampleViewSize;
|
|
Texture2DArray<uint> LightSamples;
|
|
RWStructuredBuffer<uint> RWCompactedTraceTexelData;
|
|
RWStructuredBuffer<uint> RWCompactedTraceTexelAllocator;
|
|
|
|
groupshared uint SharedGlobalTraceTexelStartOffset;
|
|
|
|
#if WAVE_OPS
|
|
groupshared uint SharedGroupSum;
|
|
#else
|
|
//@todo - ordered compaction for non-wave ops path
|
|
groupshared uint SharedTraceTexelAllocator;
|
|
groupshared uint SharedTraceTexels[THREADGROUP_SIZE * THREADGROUP_SIZE];
|
|
#endif
|
|
|
|
#if COMPILER_SUPPORTS_WAVE_SIZE && WAVE_OPS
|
|
WAVESIZE(32)
|
|
#endif
|
|
[numthreads(THREADGROUP_SIZE, THREADGROUP_SIZE, 1)]
|
|
void CompactLightSampleTracesCS(
|
|
uint3 GroupId : SV_GroupID,
|
|
uint3 GroupThreadId : SV_GroupThreadID,
|
|
uint3 DispatchThreadId : SV_DispatchThreadID)
|
|
{
|
|
const uint LinearThreadIndex = GroupThreadId.y * THREADGROUP_SIZE + GroupThreadId.x;
|
|
|
|
if (LinearThreadIndex == 0)
|
|
#if WAVE_OPS
|
|
{
|
|
SharedGroupSum = 0;
|
|
}
|
|
#else
|
|
{
|
|
SharedTraceTexelAllocator = 0;
|
|
}
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
#endif
|
|
|
|
uint2 SampleCoord = DispatchThreadId.xy;
|
|
uint SampleLayerIndex = GroupId.z;
|
|
bool bTraceValid = false;
|
|
uint TraceTexelForThisThread = 0;
|
|
|
|
if (all(SampleCoord < uint2(SampleViewSize)))
|
|
{
|
|
const FLightSample LightSample = UnpackLightSample(LightSamples[uint3(SampleCoord, SampleLayerIndex)]);
|
|
|
|
// FLightSample is cleared with 0, so used bVisible to see if this is a valid sample
|
|
if (LightSample.LocalLightIndex != MAX_LOCAL_LIGHT_INDEX && !LightSample.bCompleted && LightSample.bVisible)
|
|
{
|
|
#if WAVE_OPS
|
|
{
|
|
bTraceValid = true;
|
|
TraceTexelForThisThread = PackLumenSampleCoord(SampleCoord, SampleLayerIndex);
|
|
}
|
|
#else
|
|
{
|
|
uint SharedTexelOffset;
|
|
InterlockedAdd(SharedTraceTexelAllocator, 1, SharedTexelOffset);
|
|
SharedTraceTexels[SharedTexelOffset] = PackLumenSampleCoord(SampleCoord, SampleLayerIndex);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
#if WAVE_OPS
|
|
{
|
|
const uint LastLaneIndex = WaveGetLaneCount() - 1;
|
|
const uint LaneIndex = WaveGetLaneIndex();
|
|
const uint OffsetInWave = WavePrefixCountBits(bTraceValid);
|
|
uint OffsetInGroup = 0;
|
|
|
|
if (LaneIndex == LastLaneIndex)
|
|
{
|
|
const uint ThisWaveSum = OffsetInWave + (bTraceValid ? 1 : 0);
|
|
InterlockedAdd(SharedGroupSum, ThisWaveSum, OffsetInGroup);
|
|
}
|
|
OffsetInGroup = WaveReadLaneAt(OffsetInGroup, LastLaneIndex) + OffsetInWave;
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
// Allocate this group's compacted traces from the global allocator
|
|
if (LinearThreadIndex == 0)
|
|
{
|
|
InterlockedAdd(RWCompactedTraceTexelAllocator[0], SharedGroupSum, SharedGlobalTraceTexelStartOffset);
|
|
}
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
if (bTraceValid)
|
|
{
|
|
RWCompactedTraceTexelData[SharedGlobalTraceTexelStartOffset + OffsetInGroup] = TraceTexelForThisThread;
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
if (LinearThreadIndex == 0)
|
|
{
|
|
InterlockedAdd(RWCompactedTraceTexelAllocator[0], SharedTraceTexelAllocator, SharedGlobalTraceTexelStartOffset);
|
|
}
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
if (LinearThreadIndex < SharedTraceTexelAllocator)
|
|
{
|
|
RWCompactedTraceTexelData[SharedGlobalTraceTexelStartOffset + LinearThreadIndex] = SharedTraceTexels[LinearThreadIndex];
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#endif // SHADER_COMPACTION
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if SHADER_SHADING || SHADER_TEMPORAL_DENOISER
|
|
|
|
SamplerState BilinearClampedSampler;
|
|
RWTexture2D<float3> RWFinalLightingAtlas;
|
|
RWTexture2D<float3> RWDirectLightingAtlas;
|
|
|
|
float2 IndirectLightingAtlasHalfTexelSize;
|
|
Texture2D DirectLightingAtlas;
|
|
Texture2D IndirectLightingAtlas;
|
|
Texture2D AlbedoAtlas;
|
|
Texture2D EmissiveAtlas;
|
|
|
|
float3 GetFinalLighting(float2 AtlasUV, float3 DirectLighting, FLumenCardPageData CardPage)
|
|
{
|
|
#if RADIOSITY_ATLAS_DOWNSAMPLE_FACTOR == 1
|
|
float2 IndirectLightingAtlasUV = AtlasUV;
|
|
#else
|
|
// When sampling from a downsampled Indirect Lighting atlas we need to appropriately clamp input UVs to prevent bilinear reading outside of the valid area
|
|
float2 IndirectLightingAtlasUV = clamp(AtlasUV, CardPage.PhysicalAtlasUVRect.xy + IndirectLightingAtlasHalfTexelSize, CardPage.PhysicalAtlasUVRect.zw - IndirectLightingAtlasHalfTexelSize);
|
|
#endif
|
|
|
|
const float3 Albedo = Texture2DSampleLevel(AlbedoAtlas, BilinearClampedSampler, AtlasUV, 0).xyz;
|
|
const float3 Emissive = Texture2DSampleLevel(EmissiveAtlas, BilinearClampedSampler, AtlasUV, 0).xyz;
|
|
const float3 IndirectLighting = Texture2DSampleLevel(IndirectLightingAtlas, BilinearClampedSampler, IndirectLightingAtlasUV, 0).xyz;
|
|
|
|
return CombineFinalLighting(Albedo, Emissive, DirectLighting, IndirectLighting);
|
|
}
|
|
|
|
#endif // SHADER_SHADING || SHADER_TEMPORAL_DENOISER
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if SHADER_SHADING
|
|
|
|
#define DEBUG_ENABLE 0
|
|
|
|
// Workaround for a console shader compiler bug generating incorrect code. Likely can be removed in next SDK.
|
|
uint NumSamplesPerPixel1d;
|
|
uint DummyZeroForFixingShaderCompilerBug;
|
|
StructuredBuffer<uint> TileAllocator;
|
|
StructuredBuffer<uint> TileData;
|
|
#if USE_LIGHT_SAMPLES
|
|
Texture2DArray<float4> SampleDiffuseLighting;
|
|
Texture2DArray<uint> LightSamples;
|
|
#endif
|
|
StructuredBuffer<uint> LumenSceneDebugData;
|
|
|
|
#if THREADGROUP_SIZE != CARD_TILE_SIZE
|
|
#error The code assume THREADGROUP_SIZE == CARD_TILE_SIZE
|
|
#endif
|
|
[numthreads(THREADGROUP_SIZE, THREADGROUP_SIZE, 1)]
|
|
void ShadeLightSamplesCS(
|
|
uint3 GroupId : SV_GroupID,
|
|
uint3 GroupThreadId : SV_GroupThreadID)
|
|
{
|
|
uint TileIndex = GroupId.x;
|
|
{
|
|
// 1. Load cards data
|
|
const uint CardTileIndex = TileIndex;
|
|
const FCardTileData CardTile = UnpackCardTileData(TileData[CardTileIndex]);
|
|
const FLumenCardPageData CardPage = GetLumenCardPageData(CardTile.CardPageIndex + DummyZeroForFixingShaderCompilerBug);
|
|
|
|
const uint2 TexelCoordInTile = GroupThreadId.xy;
|
|
uint2 CoordInCardPage = CARD_TILE_SIZE * CardTile.TileCoord + TexelCoordInTile;
|
|
uint2 AtlasCoord = CardPage.PhysicalAtlasCoord + CoordInCardPage;
|
|
float2 AtlasUV = CardPage.PhysicalAtlasUVRect.xy + CardPage.PhysicalAtlasUVTexelScale * (CoordInCardPage + 0.5);
|
|
|
|
#if DEBUG_ENABLE
|
|
FShaderPrintContext Ctx = InitDebugContext(LumenSceneDebugData, CardTile, CardPage, AtlasUV, uint2(500, 50));
|
|
#endif
|
|
|
|
float3 DiffuseLighting = 0;
|
|
#if USE_LIGHT_SAMPLES
|
|
const FTransientCoord TransientCoord = GetTransientCoord(TileIndex, GroupThreadId.xy);
|
|
for (uint LightSampleIndex = 0; LightSampleIndex < NumSamplesPerPixel1d; ++LightSampleIndex)
|
|
{
|
|
const FLightSample LightSample = UnpackLightSample(LightSamples[uint3(TransientCoord.TexelCoord, LightSampleIndex)]);
|
|
const float3 LightSampleDiffuseLighting = SampleDiffuseLighting[uint3(TransientCoord.TexelCoord, LightSampleIndex)].xyz;
|
|
DiffuseLighting += LightSample.Weight * LightSampleDiffuseLighting * (LightSample.bVisible ? 1.f : 0.f);
|
|
|
|
#if DEBUG_ENABLE
|
|
if (Ctx.bIsActive)
|
|
{
|
|
Print(Ctx, TEXT("Weight "), FontWhite); Print(Ctx, LightSample.Weight, FontYellow); Newline(Ctx);
|
|
Print(Ctx, TEXT("bVisible "), FontWhite); PrintBool(Ctx, LightSample.bVisible); Newline(Ctx);
|
|
Print(Ctx, TEXT("bCompleted "), FontWhite); Print(Ctx, LightSample.bCompleted, FontYellow); Newline(Ctx);
|
|
Print(Ctx, TEXT("LightIndex "), FontWhite); Print(Ctx, LightSample.LocalLightIndex, FontYellow); Newline(Ctx);
|
|
Print(Ctx, TEXT("Diffuse "), FontWhite); Print(Ctx, LightSampleDiffuseLighting, FontYellow); Newline(Ctx);
|
|
}
|
|
#endif
|
|
}
|
|
#endif // USE_LIGHT_SAMPLES
|
|
|
|
// Final composition
|
|
RWDirectLightingAtlas[AtlasCoord] = DiffuseLighting;
|
|
RWFinalLightingAtlas[AtlasCoord] = GetFinalLighting(AtlasUV, DiffuseLighting, CardPage);
|
|
}
|
|
}
|
|
#endif // SHADER_SHADING
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if SHADER_TEMPORAL_DENOISER
|
|
|
|
uint DummyZeroForFixingShaderCompilerBug;
|
|
|
|
Texture2D<float2> SampleLuminanceSumTexture;
|
|
Texture2D ResolvedDirectLightingAtlas;
|
|
Texture2D DiffuseLightingAndSecondMomentHistoryTexture;
|
|
Texture2D<UNORM float> NumFramesAccumulatedHistoryTexture;
|
|
|
|
float PrevSceneColorPreExposureCorrection;
|
|
float TemporalMaxFramesAccumulated;
|
|
float TemporalNeighborhoodClampScale;
|
|
uint TemporalAdvanceFrame;
|
|
|
|
RWTexture2D<float4> RWDiffuseLightingAndSecondMoment;
|
|
RWTexture2D<UNORM float> RWNumFramesAccumulated;
|
|
|
|
StructuredBuffer<uint> TileAllocator;
|
|
StructuredBuffer<uint> TileData;
|
|
|
|
struct FNeighborhood
|
|
{
|
|
float3 Mean;
|
|
float3 ClampMin;
|
|
float3 ClampMax;
|
|
};
|
|
|
|
FNeighborhood GetNeighborhood(
|
|
uint2 BaseTexelCoord,
|
|
uint2 TexelCoordWithinTile,
|
|
Texture2D SampleTexture,
|
|
float3 CenterSample,
|
|
float2 CardPageMinInPixels,
|
|
float2 CardPageMaxInPixels)
|
|
{
|
|
float3 SampleSum = CenterSample;
|
|
float3 SampleSqSum = Pow2(CenterSample);
|
|
|
|
const int KernelSize = 2;
|
|
for (int NeigborOffsetY = -KernelSize; NeigborOffsetY <= KernelSize; ++NeigborOffsetY)
|
|
{
|
|
for (int NeigborOffsetX = -KernelSize; NeigborOffsetX <= KernelSize; ++NeigborOffsetX)
|
|
{
|
|
if (!(NeigborOffsetX == 0 && NeigborOffsetY == 0))
|
|
{
|
|
uint2 NeigborScreenCoord = int2(TexelCoordWithinTile) + int2(NeigborOffsetX, NeigborOffsetY) + BaseTexelCoord;
|
|
NeigborScreenCoord.x = clamp(NeigborScreenCoord.x, CardPageMinInPixels.x, CardPageMaxInPixels.x-1);
|
|
NeigborScreenCoord.y = clamp(NeigborScreenCoord.y, CardPageMinInPixels.y, CardPageMaxInPixels.y-1);
|
|
|
|
float3 NeigborSample = SampleTexture[NeigborScreenCoord].xyz;
|
|
SampleSum += NeigborSample;
|
|
SampleSqSum += Pow2(NeigborSample);
|
|
}
|
|
}
|
|
}
|
|
|
|
float NumSamples = Pow2(2.0f * KernelSize + 1.0f);
|
|
float3 M1 = SampleSum / NumSamples;
|
|
float3 M2 = SampleSqSum / NumSamples;
|
|
float3 Variance = max(M2 - Pow2(M1), 0.0f);
|
|
float3 StdDev = sqrt(Variance);
|
|
|
|
FNeighborhood Neighborhood = (FNeighborhood)0;
|
|
Neighborhood.Mean = M1;
|
|
Neighborhood.ClampMin = M1 - TemporalNeighborhoodClampScale * StdDev;
|
|
Neighborhood.ClampMax = M1 + TemporalNeighborhoodClampScale * StdDev;
|
|
return Neighborhood;
|
|
}
|
|
|
|
float3 ClampLuminance(float3 Lighting, float LuminanceClamp)
|
|
{
|
|
if (Luminance(Lighting) > LuminanceClamp)
|
|
{
|
|
Lighting *= LuminanceClamp / Luminance(Lighting);
|
|
}
|
|
|
|
return Lighting;
|
|
}
|
|
|
|
[numthreads(THREADGROUP_SIZE, THREADGROUP_SIZE, 1)]
|
|
void DenoiserTemporalCS(
|
|
uint3 GroupId : SV_GroupID,
|
|
uint3 GroupThreadId : SV_GroupThreadID,
|
|
uint3 DispatchThreadId : SV_DispatchThreadID)
|
|
{
|
|
uint TileIndex = GroupId.x;
|
|
const FTransientCoord TransientCoord = GetTransientCoord(TileIndex, GroupThreadId.xy);
|
|
|
|
// 1. Load cards data
|
|
const uint CardTileIndex = TileIndex;
|
|
const FCardTileData CardTile = UnpackCardTileData(TileData[CardTileIndex]);
|
|
const FLumenCardPageData CardPage = GetLumenCardPageData(CardTile.CardPageIndex + DummyZeroForFixingShaderCompilerBug);
|
|
|
|
const uint2 TexelCoordInTile = GroupThreadId.xy;
|
|
const uint2 CoordInCardPage = CARD_TILE_SIZE * CardTile.TileCoord + TexelCoordInTile;
|
|
const uint2 AtlasCoord = CardPage.PhysicalAtlasCoord + CoordInCardPage;
|
|
const float2 AtlasUV = CardPage.PhysicalAtlasUVRect.xy + CardPage.PhysicalAtlasUVTexelScale * (CoordInCardPage + 0.5);
|
|
|
|
const uint2 AtlasTileBaseCoord = CardPage.PhysicalAtlasCoord + CARD_TILE_SIZE * CardTile.TileCoord;
|
|
const float2 CardPageMinInPixels = CardPage.PhysicalAtlasCoord;
|
|
const float2 CardPageMaxInPixels = CardPage.PhysicalAtlasCoord + CardPage.SizeInTexels;
|
|
|
|
float2 SampleLuminanceSum = SampleLuminanceSumTexture[TransientCoord.TexelCoord];
|
|
float3 DiffuseLighting = ResolvedDirectLightingAtlas[AtlasCoord].xyz;
|
|
float DiffuseSecondMoment = Pow2(Luminance(DiffuseLighting));
|
|
float NumFramesAccumulated = 0;
|
|
|
|
#if VALID_HISTORY
|
|
// Use history only if we found at least one valid sample
|
|
{
|
|
const float4 DiffuseLightingAndSecondMomentHistory = DiffuseLightingAndSecondMomentHistoryTexture[AtlasCoord];
|
|
|
|
// Correct history for current frame exposure
|
|
float3 HistoryDiffuseLighting = DiffuseLightingAndSecondMomentHistory.xyz * PrevSceneColorPreExposureCorrection;
|
|
float HistoryDiffuseSecondMoment = DiffuseLightingAndSecondMomentHistory.w * Pow2(PrevSceneColorPreExposureCorrection);
|
|
|
|
// Reproject and rescale normalized NumFramesAccumulated
|
|
float NumFramesAccumulatedHistory = NumFramesAccumulatedHistoryTexture[AtlasCoord] * TemporalMaxFramesAccumulated;
|
|
|
|
// Advance the frame counter
|
|
if (TemporalAdvanceFrame != 0)
|
|
{
|
|
NumFramesAccumulatedHistory += 1.0f;
|
|
}
|
|
NumFramesAccumulated = min(NumFramesAccumulatedHistory, TemporalMaxFramesAccumulated);
|
|
|
|
// Clamp history to current neighborhood
|
|
FNeighborhood DiffuseNeighborhood = GetNeighborhood(AtlasTileBaseCoord, TexelCoordInTile, ResolvedDirectLightingAtlas, DiffuseLighting, CardPageMinInPixels, CardPageMaxInPixels);
|
|
float3 ClampedHistoryDiffuseLighting = clamp(HistoryDiffuseLighting, DiffuseNeighborhood.ClampMin, DiffuseNeighborhood.ClampMax);
|
|
|
|
// Clamp history to max sample luminance
|
|
ClampedHistoryDiffuseLighting = ClampLuminance(ClampedHistoryDiffuseLighting, SampleLuminanceSum.x);
|
|
float ClampedHistoryDiffuseSecondMoment = clamp(HistoryDiffuseSecondMoment, 0, Pow2(SampleLuminanceSum.x));
|
|
|
|
// Blend history with new samples
|
|
float Alpha = 1.0f / (1.0f + NumFramesAccumulated);
|
|
DiffuseLighting = lerp(ClampedHistoryDiffuseLighting, DiffuseLighting, Alpha);
|
|
DiffuseSecondMoment = lerp(ClampedHistoryDiffuseSecondMoment, DiffuseSecondMoment, Alpha);
|
|
}
|
|
#endif
|
|
|
|
DiffuseLighting = MakeFinite(DiffuseLighting);
|
|
|
|
RWDiffuseLightingAndSecondMoment[AtlasCoord] = float4(DiffuseLighting, DiffuseSecondMoment);
|
|
RWNumFramesAccumulated[AtlasCoord] = (NumFramesAccumulated + 0.5f) / TemporalMaxFramesAccumulated;
|
|
RWFinalLightingAtlas[AtlasCoord] = GetFinalLighting(AtlasUV, DiffuseLighting, CardPage);
|
|
RWDirectLightingAtlas[AtlasCoord] = DiffuseLighting;
|
|
}
|
|
#endif // SHADER_TEMPORAL_DENOISER
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Compute an offset at which all the card tiles affecting a standalone light will be stored
|
|
|
|
#if SHADER_STANDALONE_COMPACT_OFFSET
|
|
|
|
uint NumLights;
|
|
uint NumStandaloneLights;
|
|
uint NumSamplesPerPixel1d;
|
|
StructuredBuffer<uint> CardTilePerLightCounters;
|
|
RWStructuredBuffer<uint> RWCardTilePerLightOffsets;
|
|
RWBuffer<uint> RWCardTilePerLightArgs;
|
|
|
|
[numthreads(1, 1, 1)]
|
|
void MainCS(
|
|
uint3 GroupId : SV_GroupID,
|
|
uint3 GroupThreadId : SV_GroupThreadID,
|
|
uint3 DispatchThreadId : SV_DispatchThreadID)
|
|
{
|
|
// Make parallel version
|
|
uint Offset = 0;
|
|
for (uint LightIt=0; LightIt<NumLights; ++LightIt)
|
|
{
|
|
RWCardTilePerLightOffsets[LightIt] = Offset;
|
|
const uint NumTiles = CardTilePerLightCounters[LightIt];
|
|
Offset += NumTiles;
|
|
WriteDispatchIndirectArgs(RWCardTilePerLightArgs, LightIt, NumTiles, 1, NumSamplesPerPixel1d);
|
|
}
|
|
WriteDispatchIndirectArgs(RWCardTilePerLightArgs, NumLights, Offset /*Total num. tiles*/, 1, 1);
|
|
}
|
|
#endif // SHADER_STANDALONE_COMPACT_OFFSET
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Build list of all card tile affecting a standalone light (stored at the offset computed above)
|
|
|
|
#if SHADER_STANDALONE_COMPACT_LIST
|
|
|
|
Texture2D<uint> UniqueLightCount;
|
|
Texture2D<uint> UniqueLightIndices;
|
|
StructuredBuffer<uint> CardTilePerLightOffsets;
|
|
RWStructuredBuffer<uint> RWCardTilePerLightCounters;
|
|
RWStructuredBuffer<uint> RWCardTilePerLightDatas;
|
|
|
|
[numthreads(1, 1, 1)]
|
|
void MainCS(
|
|
uint3 GroupId : SV_GroupID,
|
|
uint LinearThreadIndex : SV_GroupIndex,
|
|
uint3 GroupThreadId : SV_GroupThreadID,
|
|
uint3 DispatchThreadId : SV_DispatchThreadID)
|
|
{
|
|
if (LinearThreadIndex == 0)
|
|
{
|
|
const uint TileIndex = GroupId.x; //DispatchThreadId.x;
|
|
const uint2 TileCoord = GetTile1dToTile2D(TileIndex);
|
|
const uint LightCount = UniqueLightCount[TileCoord];
|
|
for (uint LightIt = 0; LightIt < LightCount; ++LightIt)
|
|
{
|
|
const uint2 Offset = uint2(LightIt % 8u, LightIt / 8u);
|
|
const uint LightIndex = UniqueLightIndices[TileCoord * CARD_TILE_SIZE + Offset];
|
|
if (LightIndex != ~0 && LightIndex != MAX_LOCAL_LIGHT_INDEX)
|
|
{
|
|
const uint LightOffset = CardTilePerLightOffsets[LightIndex];
|
|
uint LocalOffset = 0;
|
|
InterlockedAdd(RWCardTilePerLightCounters[LightIndex], 1, LocalOffset);
|
|
RWCardTilePerLightDatas[LightOffset + LocalOffset] = TileIndex;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // SHADER_STANDALONE_COMPACT_LIST
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Evaluate lighting for cards tiles affected by a standalone light
|
|
|
|
#if SHADER_STANDALONE_EVALUATE
|
|
|
|
uint LightIndex;
|
|
uint ViewIndex;
|
|
StructuredBuffer<uint> CardTilePerLightCounters;
|
|
StructuredBuffer<uint> CardTilePerLightOffsets;
|
|
StructuredBuffer<uint> CardTilePerLightDatas;
|
|
Texture2D<float4> LumenSceneData;
|
|
RWTexture2DArray<uint> RWLightSamples;
|
|
RWTexture2DArray<float4> RWSampleDiffuseLighting;
|
|
|
|
[numthreads(THREADGROUP_SIZE, THREADGROUP_SIZE, 1)]
|
|
void MainCS(
|
|
uint3 GroupId : SV_GroupID,
|
|
uint LinearThreadIndex : SV_GroupIndex,
|
|
uint3 GroupThreadId : SV_GroupThreadID,
|
|
uint3 DispatchThreadId : SV_DispatchThreadID)
|
|
{
|
|
const uint BaseOffset = CardTilePerLightOffsets[LightIndex];
|
|
const uint TileCount = CardTilePerLightCounters[LightIndex];
|
|
const uint TileIndex = CardTilePerLightDatas[BaseOffset + GroupId.x];
|
|
const uint2 TileCoord = GetTile1dToTile2D(TileIndex);
|
|
const uint2 SampleCoord = TileCoord * CARD_TILE_SIZE + GroupThreadId.xy;
|
|
const uint SampleLayerIndex = GroupId.z;
|
|
const uint3 TexelCoord = uint3(SampleCoord, SampleLayerIndex);
|
|
const FDFVector3 PreViewTranslation = GetPreViewTranslation(ViewIndex);
|
|
|
|
const FLumenLight LumenLight = LoadLumenLight(LightIndex, DFHackToFloat(PreViewTranslation), ViewExposure[ViewIndex]);
|
|
FLightSample LightSample = UnpackLightSample(RWLightSamples[TexelCoord]);
|
|
if (LightSample.LocalLightIndex == LightIndex)
|
|
{
|
|
const FLumenSampleSceneData SampleSceneData = UnpackLumenSampleSceneData(LumenSceneData[SampleCoord]);
|
|
const float ShadowThreshold = 0.01f;
|
|
|
|
float ShadowFactor = 1.f;
|
|
#if USE_LIGHT_FUNCTION_ATLAS
|
|
if (ShadowFactor > ShadowThreshold && LumenLight.DeferredLightData.LightFunctionAtlasLightIndex > 0)
|
|
{
|
|
ShadowFactor *= GetLocalLightFunctionCommon(SampleSceneData.TranslatedWorldPosition, LumenLight.DeferredLightData.LightFunctionAtlasLightIndex);
|
|
}
|
|
#elif LIGHT_FUNCTION
|
|
if (ShadowFactor > ShadowThreshold)
|
|
{
|
|
ShadowFactor *= GetLumenLightFunction(SampleSceneData.TranslatedWorldPosition - DFHackToFloat(PreViewTranslation));
|
|
}
|
|
#endif
|
|
|
|
#if USE_CLOUD_TRANSMITTANCE
|
|
if (ShadowFactor > ShadowThreshold)
|
|
{
|
|
float OutOpticalDepth = 0.0f;
|
|
ShadowFactor *= lerp(1.0f, GetCloudVolumetricShadow(SampleSceneData.TranslatedWorldPosition, CloudShadowmapTranslatedWorldToLightClipMatrix, CloudShadowmapFarDepthKm, CloudShadowmapTexture, CloudShadowmapSampler, OutOpticalDepth), CloudShadowmapStrength);
|
|
}
|
|
#endif
|
|
|
|
// * If the sample luminance is below ShadowThreshold, culled the tracing.
|
|
// * Otherwise attenuate its lighting
|
|
if (ShadowFactor < ShadowThreshold)
|
|
{
|
|
LightSample.bVisible = false;
|
|
LightSample.bCompleted = true;
|
|
RWLightSamples[TexelCoord] = PackLightSample(LightSample);
|
|
}
|
|
else if (ShadowFactor < 1.f)
|
|
{
|
|
LightSample.Weight *= ShadowFactor;
|
|
RWLightSamples[TexelCoord] = PackLightSample(LightSample);
|
|
}
|
|
}
|
|
|
|
}
|
|
#endif // SHADER_STANDALONE_EVALUATE |