Files
UnrealEngine/Engine/Shaders/Private/StochasticLighting/StochasticLightingTileClassification.usf
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

916 lines
38 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#define USE_HAIR_COMPLEX_TRANSMITTANCE 1
#define SUBSTRATE_MATERIALCONTAINER_IS_VIEWRESOURCE 1
// For now only Substrate Format=0 uses this path, which does not support Special complex classification
#define SUBSTRATE_COMPLEXSPECIALPATH 0
#include "../Common.ush"
#include "../LightGridCommon.ush"
#include "../Lumen/LumenMaterial.ush"
#include "../Lumen/LumenReflectionCommon.ush"
#include "../Lumen/LumenScreenProbeTileClassication.ush"
#include "../MegaLights/MegaLightsTileClassification.ush"
#include "StochasticLightingUpsample.ush"
#include "StochasticLightingCommon.ush"
MAX_OCCUPANCY
#define DOWNSAMPLE_FACTOR 2
#define TILE_SIZE 8
#define TILE_SIZE_DIV_SHIFT 3
#define STOCHASTIC_SAMPLE_OFFSET_2x1 1
#define STOCHASTIC_SAMPLE_OFFSET_2x2 2
Texture2D DepthHistoryTexture;
Texture2D<float4> NormalAndShadingInfoHistory;
Texture2D<half> MegaLightsNumFramesAccumulatedHistory;
RWTexture2D<float> RWDepthTexture;
RWTexture2D<float4> RWNormalTexture;
RWTexture2DArray<uint> RWLumenTileBitmask;
RWTexture2D<uint> RWMegaLightsTileBitmask;
RWTexture2D<uint> RWEncodedHistoryScreenCoord;
RWTexture2DArray<uint> RWLumenPackedPixelData;
RWTexture2D<uint> RWMegaLightsPackedPixelData;
uint EnableTexturedRectLights;
float4 HistoryScreenPositionScaleBias;
float4 HistoryUVMinMax;
float4 HistoryGatherUVMinMax;
float4 HistoryBufferSizeAndInvSize;
float4 HistorySubPixelGridSizeAndInvSize;
float LumenHistoryDistanceThreshold;
float LumenHistoryDistanceThresholdForFoliage;
float LumenHistoryNormalCosThreshold;
uint2 HistoryScreenCoordDecodeShift;
uint2 DownsampledViewMin2x1;
uint2 DownsampledViewMin2x2;
uint2 DownsampledViewSize2x1;
uint2 DownsampledViewSize2x2;
uint LumenStochasticSampleMode;
uint MegaLightsStochasticSampleMode;
#if TILE_CLASSIFY_LUMEN || TILE_CLASSIFY_MEGALIGHTS || TILE_CLASSIFY_SUBSTRATE
#if PLATFORM_SUPPORTS_SM6_0_WAVE_OPERATIONS
groupshared uint SharedTileBitmask;
#else
groupshared uint SharedTileBitmask[THREADGROUP_SIZE * THREADGROUP_SIZE];
#endif
#define TILE_BITMASK_INDEX_LUMEN 0
#define TILE_BITMASK_INDEX_MEGALIGHTS 1
#define TILE_BITMASK_INDEX_SUBSTRATE 2
#define TILE_BITMASK_BIT_COUNT 10
#define TILE_BITMASK_BIT_MASK 0x3FF
#if PLATFORM_SUPPORTS_SM6_0_WAVE_OPERATIONS
#define WriteSharedTileBitmask(LinearThreadIndex, InBitmask, InBitmaskIndex) { GroupTileBitmask |= (TILE_BITMASK_BIT_MASK & InBitmask) << (InBitmaskIndex*TILE_BITMASK_BIT_COUNT); }
#define ReadSharedTileBitmask(LinearThreadIndex, InBitmaskIndex) BitFieldExtractU32(GroupTileBitmask, TILE_BITMASK_BIT_COUNT, InBitmaskIndex * TILE_BITMASK_BIT_COUNT)
#else
void WriteSharedTileBitmask(uint LinearThreadIndex, uint InBitmask, uint InBitmaskIndex) { SharedTileBitmask[LinearThreadIndex] |= (TILE_BITMASK_BIT_MASK & InBitmask) << (InBitmaskIndex*TILE_BITMASK_BIT_COUNT); }
uint ReadSharedTileBitmask(uint LinearThreadIndex, uint InBitmaskIndex) { return BitFieldExtractU32(SharedTileBitmask[LinearThreadIndex], TILE_BITMASK_BIT_COUNT, InBitmaskIndex*TILE_BITMASK_BIT_COUNT); }
#endif
#endif
RWTexture2D<float> RWDownsampledSceneDepth2x1;
RWTexture2D<float> RWDownsampledSceneDepth2x2;
RWTexture2D<UNORM float3> RWDownsampledWorldNormal2x1;
RWTexture2D<UNORM float3> RWDownsampledWorldNormal2x2;
groupshared float SharedSceneDepth2x1[6][10];
groupshared uint SharedEncodedNormal2x1[6][10];
groupshared float SharedSceneDepth2x2[6][6];
groupshared uint SharedEncodedNormal2x2[6][6];
void StoreDepthToCache(uint2 SharedCoord, const uint2 DownsampleFactor, float Depth)
{
if (all(DownsampleFactor == 2))
{
SharedSceneDepth2x2[SharedCoord.x][SharedCoord.y] = Depth;
}
else
{
SharedSceneDepth2x1[SharedCoord.x][SharedCoord.y] = Depth;
}
}
float LoadDepthFromCache(uint2 SharedCoord, const uint2 DownsampleFactor)
{
if (all(DownsampleFactor == 2))
{
return SharedSceneDepth2x2[SharedCoord.x][SharedCoord.y];
}
else
{
return SharedSceneDepth2x1[SharedCoord.x][SharedCoord.y];
}
}
void StoreNormalToCache(uint2 SharedCoord, const uint2 DownsampleFactor, float3 Normal)
{
Normal = saturate(EncodeNormal(Normal));
uint Packed = 0;
Packed |= uint(Normal.x * 1023.0 + 0.5);
Packed |= uint(Normal.y * 1023.0 + 0.5) << 10;
Packed |= uint(Normal.z * 1023.0 + 0.5) << 20;
if (all(DownsampleFactor == 2))
{
SharedEncodedNormal2x2[SharedCoord.x][SharedCoord.y] = Packed;
}
else
{
SharedEncodedNormal2x1[SharedCoord.x][SharedCoord.y] = Packed;
}
}
float3 LoadNormalFromCache(uint2 SharedCoord, const uint2 DownsampleFactor)
{
uint Packed;
if (all(DownsampleFactor == 2))
{
Packed = SharedEncodedNormal2x2[SharedCoord.x][SharedCoord.y];
}
else
{
Packed = SharedEncodedNormal2x1[SharedCoord.x][SharedCoord.y];
}
float3 Normal;
Normal.x = BitFieldExtractU32(Packed, 10, 0) / 1023.0;
Normal.y = BitFieldExtractU32(Packed, 10, 10) / 1023.0;
Normal.z = BitFieldExtractU32(Packed, 10, 20) / 1023.0;
return DecodeNormal(Normal);
}
float GetNormalWeight(float3 SceneWorldNormal, float3 SampleWorldNormal)
{
float AngleBetweenNormals = acosFast(saturate(dot(SampleWorldNormal, SceneWorldNormal)));
float NormalWeight = 1.0f - saturate(AngleBetweenNormals);
return Pow2(NormalWeight);
}
uint2 ThreadIndexToBorderCoord(uint LinearThreadIndex, const uint2 DownsampleFactor)
{
const uint TileBorder = 1;
const uint2 TileSize = TILE_SIZE / DownsampleFactor + TileBorder * 2;
const uint NumPartialRows = TileSize.y - TileBorder * 2;
const uint PixelsPerPartialRow = TileBorder * 2;
uint2 BorderCoord;
if (LinearThreadIndex < TileSize.x * TileBorder)
{
BorderCoord = uint2(LinearThreadIndex % TileSize.x, LinearThreadIndex / TileSize.x);
}
else if (LinearThreadIndex - TileSize.x * TileBorder < NumPartialRows * PixelsPerPartialRow)
{
uint LocalIndex = LinearThreadIndex - TileSize.x * TileBorder;
BorderCoord = uint2(LocalIndex % PixelsPerPartialRow, LocalIndex / PixelsPerPartialRow);
BorderCoord.x += BorderCoord.x < TileBorder ? 0 : (TileSize.x - TileBorder * 2);
BorderCoord.y += TileBorder;
}
else
{
uint LocalIndex = LinearThreadIndex - TileSize.x * TileBorder - NumPartialRows * PixelsPerPartialRow;
BorderCoord = uint2(LocalIndex % TileSize.x, TileSize.y - TileBorder + LocalIndex / TileSize.x);
}
return BorderCoord;
}
uint2 GetDownsampleJitter(uint2 DownsampledCoord, const uint2 DownsampleFactor)
{
uint2 Jitter = 0;
if (all(DownsampleFactor == 2))
{
Jitter = GetDownsampleJitter2x2(DownsampledCoord);
}
else if (all(DownsampleFactor == uint2(2, 1)))
{
Jitter = GetDownsampleJitter2x1(DownsampledCoord);
}
return Jitter;
}
void DownsampleDepthAndNormal(
uint2 GroupId,
uint2 GroupThreadId,
uint2 ScreenCoord,
FLumenMaterialData Material,
const uint2 DownsampleFactor,
RWTexture2D<float> RWDownsampledDepth,
RWTexture2D<UNORM float3> RWDownsampledNormal)
{
const uint TileBorder = 1;
const uint2 TileSize = TILE_SIZE / DownsampleFactor + TileBorder * 2;
uint2 DownsampledScreenCoord = ScreenCoord / DownsampleFactor;
uint2 Jitter = GetDownsampleJitter(DownsampledScreenCoord, DownsampleFactor);
if (all(Jitter == ScreenCoord % DownsampleFactor))
{
RWDownsampledDepth[DownsampledScreenCoord] = Material.IsValid() ? Material.SceneDepth : -1.0f;
RWDownsampledNormal[DownsampledScreenCoord] = EncodeNormal(Material.WorldNormalForPositionBias);
uint2 SharedCoord = GroupThreadId / DownsampleFactor + TileBorder;
StoreDepthToCache(SharedCoord, DownsampleFactor, Material.IsValid() ? Material.SceneDepth : -1.0f);
StoreNormalToCache(SharedCoord, DownsampleFactor, Material.WorldNormalForPositionBias);
}
const uint NumBorderPixels = (TileSize.x + TileSize.y - TileBorder * 2) * TileBorder * 2;
uint LinearThreadIndex = GroupThreadId.x + THREADGROUP_SIZE * GroupThreadId.y;
if (LinearThreadIndex < NumBorderPixels)
{
uint2 BorderSharedCoord = ThreadIndexToBorderCoord(LinearThreadIndex, DownsampleFactor);
uint2 BorderDownsampledCoord = (View.ViewRectMinAndSize.xy + GroupId * TILE_SIZE) / DownsampleFactor - TileBorder + BorderSharedCoord;
uint2 BorderScreenCoord = BorderDownsampledCoord * DownsampleFactor + GetDownsampleJitter(BorderDownsampledCoord, DownsampleFactor);
bool bCanLoadFromCache = false;
#if STOCHASTIC_SAMPLE_OFFSET == (STOCHASTIC_SAMPLE_OFFSET_2x1 | STOCHASTIC_SAMPLE_OFFSET_2x2)
if (all(DownsampleFactor == uint2(2, 1)))
{
uint2 DownsampledCoord2x2 = BorderDownsampledCoord / uint2(1, 2);
uint2 ScreenCoord2x2 = DownsampledCoord2x2 * 2 + GetDownsampleJitter2x2(DownsampledCoord2x2);
bCanLoadFromCache = all(BorderScreenCoord == ScreenCoord2x2);
}
#endif
float BorderDepth = -1.0f;
float3 BorderNormal = 0.0f;
if (bCanLoadFromCache)
{
uint2 SharedCoord2x2 = BorderSharedCoord / uint2(1, 2);
BorderDepth = LoadDepthFromCache(SharedCoord2x2, uint2(2, 2));
BorderNormal = LoadNormalFromCache(SharedCoord2x2, uint2(2, 2));
}
else if (all(BorderScreenCoord - View.ViewRectMinAndSize.xy < View.ViewRectMinAndSize.zw))
{
FLumenMaterialCoord BorderCoord = (FLumenMaterialCoord)0;
BorderCoord.SvPosition = BorderScreenCoord;
FLumenMaterialData BorderMaterial = ReadMaterialData(BorderCoord, MaxRoughnessToTrace);
BorderDepth = BorderMaterial.IsValid() ? BorderMaterial.SceneDepth : -1.0f;
BorderNormal = BorderMaterial.WorldNormalForPositionBias;
}
StoreDepthToCache(BorderSharedCoord, DownsampleFactor, BorderDepth);
StoreNormalToCache(BorderSharedCoord, DownsampleFactor, BorderNormal);
}
}
bool IsValidDownsampledCoord(uint2 DownsampledScreenCoord, const uint2 DownsampleFactor)
{
if (all(DownsampleFactor == 2))
{
return all(DownsampledScreenCoord.xy < DownsampledViewMin2x2 + DownsampledViewSize2x2);
}
else
{
return all(DownsampledScreenCoord.xy < DownsampledViewMin2x1 + DownsampledViewSize2x1);
}
}
float4 ComputeUpsampleWeights(uint2 GroupThreadId, uint2 ScreenCoord, float2 ScreenUV, float2 ScreenPosition, float3 TranslatedWorldPosition, FLumenMaterialData Material, const uint2 DownsampleFactor)
{
const uint TileBorder = 1;
const bool bDownsample2x1 = all(DownsampleFactor == uint2(2, 1));
int2 SampleOffsets[4];
int2 SharedCoord00;
int2 DownsampledCoord00;
if (bDownsample2x1)
{
SampleOffsets[0] = int2(0, 0);
SampleOffsets[1] = int2(ScreenCoord.x % DownsampleFactor.x == 0 ? -1 : 1, 0);
SampleOffsets[2] = int2(0, -1);
SampleOffsets[3] = int2(0, 1);
SharedCoord00 = GroupThreadId / DownsampleFactor + TileBorder;
DownsampledCoord00 = ScreenCoord / DownsampleFactor;
}
else
{
SampleOffsets[0] = int2(0, 0);
SampleOffsets[1] = int2(1, 0);
SampleOffsets[2] = int2(0, 1);
SampleOffsets[3] = int2(1, 1);
SharedCoord00 = (GroupThreadId + TileBorder * 2 - 1) / DownsampleFactor;
DownsampledCoord00 = floor(ScreenUV * View.BufferSizeAndInvSize.xy / DownsampleFactor - 0.5f);
}
float4 CornerDepths;
CornerDepths.x = LoadDepthFromCache(SharedCoord00 + SampleOffsets[0], DownsampleFactor);
CornerDepths.y = LoadDepthFromCache(SharedCoord00 + SampleOffsets[1], DownsampleFactor);
CornerDepths.z = LoadDepthFromCache(SharedCoord00 + SampleOffsets[2], DownsampleFactor);
CornerDepths.w = LoadDepthFromCache(SharedCoord00 + SampleOffsets[3], DownsampleFactor);
int2 ScreenCoordOffset = ScreenCoord - DownsampledCoord00 * DownsampleFactor;
int2 SampleScreenOffset00 = GetDownsampleJitter(DownsampledCoord00 + SampleOffsets[0], DownsampleFactor) + SampleOffsets[0] * 2 - ScreenCoordOffset;
int2 SampleScreenOffset10 = GetDownsampleJitter(DownsampledCoord00 + SampleOffsets[1], DownsampleFactor) + SampleOffsets[1] * 2 - ScreenCoordOffset;
int2 SampleScreenOffset01 = GetDownsampleJitter(DownsampledCoord00 + SampleOffsets[2], DownsampleFactor) + SampleOffsets[2] * 2 - ScreenCoordOffset;
int2 SampleScreenOffset11 = GetDownsampleJitter(DownsampledCoord00 + SampleOffsets[3], DownsampleFactor) + SampleOffsets[3] * 2 - ScreenCoordOffset;
// Triangle filter weights between pixel and 4 samples
float4 UpsampleWeights;
if (bDownsample2x1)
{
UpsampleWeights = select(SampleScreenOffset00.x == 0, float4(1.0, 0.0, 0.0, 0.0), float4(0.25, 0.25, 0.25, 0.25));
}
else
{
UpsampleWeights.x = (2.0f - abs(SampleScreenOffset00.x)) * (2.0f - abs(SampleScreenOffset00.y));
UpsampleWeights.y = (2.0f - abs(SampleScreenOffset10.x)) * (2.0f - abs(SampleScreenOffset10.y));
UpsampleWeights.z = (2.0f - abs(SampleScreenOffset01.x)) * (2.0f - abs(SampleScreenOffset01.y));
UpsampleWeights.w = (2.0f - abs(SampleScreenOffset11.x)) * (2.0f - abs(SampleScreenOffset11.y));
}
float4 DepthWeights = 1.0f;
{
float4 ScenePlane = float4(Material.WorldNormalForPositionBias, dot(TranslatedWorldPosition, Material.WorldNormalForPositionBias));
float2 ScreenPosition00 = ScreenPosition + SampleScreenOffset00 * View.BufferSizeAndInvSize.zw / View.ScreenPositionScaleBias.xy;
float2 ScreenPosition10 = ScreenPosition + SampleScreenOffset10 * View.BufferSizeAndInvSize.zw / View.ScreenPositionScaleBias.xy;
float2 ScreenPosition01 = ScreenPosition + SampleScreenOffset01 * View.BufferSizeAndInvSize.zw / View.ScreenPositionScaleBias.xy;
float2 ScreenPosition11 = ScreenPosition + SampleScreenOffset11 * View.BufferSizeAndInvSize.zw / View.ScreenPositionScaleBias.xy;
float3 Position00 = mul(float4(GetScreenPositionForProjectionType(ScreenPosition00, CornerDepths.x), CornerDepths.x, 1), View.ScreenToTranslatedWorld).xyz;
float3 Position10 = mul(float4(GetScreenPositionForProjectionType(ScreenPosition10, CornerDepths.y), CornerDepths.y, 1), View.ScreenToTranslatedWorld).xyz;
float3 Position01 = mul(float4(GetScreenPositionForProjectionType(ScreenPosition01, CornerDepths.z), CornerDepths.x, 1), View.ScreenToTranslatedWorld).xyz;
float3 Position11 = mul(float4(GetScreenPositionForProjectionType(ScreenPosition11, CornerDepths.w), CornerDepths.w, 1), View.ScreenToTranslatedWorld).xyz;
float4 PlaneDistances;
PlaneDistances.x = abs(dot(float4(Position00, -1), ScenePlane));
PlaneDistances.y = abs(dot(float4(Position10, -1), ScenePlane));
PlaneDistances.z = abs(dot(float4(Position01, -1), ScenePlane));
PlaneDistances.w = abs(dot(float4(Position11, -1), ScenePlane));
float4 RelativeDepthDifference = PlaneDistances / Material.SceneDepth;
DepthWeights = select(CornerDepths > 0.0f, exp2(-5000.0f * (RelativeDepthDifference * RelativeDepthDifference)), 0.0f);
}
UpsampleWeights *= DepthWeights;
float4 NormalWeights = 1.0f;
{
float3 SampleWorldNormal00 = LoadNormalFromCache(SharedCoord00 + SampleOffsets[0], DownsampleFactor);
float3 SampleWorldNormal10 = LoadNormalFromCache(SharedCoord00 + SampleOffsets[1], DownsampleFactor);
float3 SampleWorldNormal01 = LoadNormalFromCache(SharedCoord00 + SampleOffsets[2], DownsampleFactor);
float3 SampleWorldNormal11 = LoadNormalFromCache(SharedCoord00 + SampleOffsets[3], DownsampleFactor);
NormalWeights.x = GetNormalWeight(Material.WorldNormalForPositionBias, SampleWorldNormal00);
NormalWeights.y = GetNormalWeight(Material.WorldNormalForPositionBias, SampleWorldNormal10);
NormalWeights.z = GetNormalWeight(Material.WorldNormalForPositionBias, SampleWorldNormal01);
NormalWeights.w = GetNormalWeight(Material.WorldNormalForPositionBias, SampleWorldNormal11);
}
UpsampleWeights *= NormalWeights;
// Skip out of view samples
UpsampleWeights.x = IsValidDownsampledCoord(DownsampledCoord00 + SampleOffsets[0], DownsampleFactor) ? UpsampleWeights.x : 0.0f;
UpsampleWeights.y = IsValidDownsampledCoord(DownsampledCoord00 + SampleOffsets[1], DownsampleFactor) ? UpsampleWeights.y : 0.0f;
UpsampleWeights.z = IsValidDownsampledCoord(DownsampledCoord00 + SampleOffsets[2], DownsampleFactor) ? UpsampleWeights.z : 0.0f;
UpsampleWeights.w = IsValidDownsampledCoord(DownsampledCoord00 + SampleOffsets[3], DownsampleFactor) ? UpsampleWeights.w : 0.0f;
return UpsampleWeights;
}
#if TILE_CLASSIFY_SUBSTRATE
RWBuffer<uint> TileDrawIndirectDataBufferUAV;
RWBuffer<uint> TileListBufferUAV;
uint bRectPrimitive;
uint TileEncoding;
uint4 TileListBufferOffsets[SUBSTRATE_TILE_TYPE_COUNT];
uint GetTileListBufferOffsets(uint Type)
{
return TileListBufferOffsets[Type].x;
}
#endif
/**
* Load GBuffer data once and transform it for subsequent lighting passes
* This includes full res depth and normal copy for opaque before it gets overwritten by water or other translucency writing depth
*/
[numthreads(THREADGROUP_SIZE, THREADGROUP_SIZE, 1)]
void StochasticLightingTileClassificationMarkCS(
uint2 GroupId : SV_GroupID,
uint2 GroupThreadId : SV_GroupThreadID,
uint2 DispatchThreadId : SV_DispatchThreadID)
{
uint LinearThreadIndex = GroupThreadId.x + THREADGROUP_SIZE * GroupThreadId.y;
#if TILE_CLASSIFY_LUMEN || TILE_CLASSIFY_MEGALIGHTS || TILE_CLASSIFY_SUBSTRATE
#if PLATFORM_SUPPORTS_SM6_0_WAVE_OPERATIONS
uint GroupTileBitmask = 0;
#else
SharedTileBitmask[LinearThreadIndex] = 0;
GroupMemoryBarrierWithGroupSync();
#endif
#endif
FLumenMaterialData Material = (FLumenMaterialData) 0;
Material.SceneDepth = -1.0f;
bool bIsValid = false;
bool bIsAnyValid = false;
const FLumenMaterialCoord Coord = GetLumenMaterialCoord(DispatchThreadId, GroupId, GroupThreadId, bIsValid, bIsAnyValid, true /* bAddMinRect*/);
if (bIsValid)
{
Material = ReadMaterialData(Coord, MaxRoughnessToTrace);
}
#if COPY_DEPTH_AND_NORMAL
if (Coord.ClosureIndex == 0)
{
RWDepthTexture[Coord.SvPosition] = ConvertToDeviceZ(Material.SceneDepth);
FNormalAndShadingInfo Info;
Info.Normal = Material.WorldNormalForPositionBias;
Info.bIsHair = Material.bIsHair;
Info.bHasBackfaceDiffuse = Material.bHasBackfaceDiffuse;
RWNormalTexture[Coord.SvPosition] = PackNormalAndShadingInfo(Info);
}
#endif
if (Coord.ClosureIndex == 0)
{
#if (STOCHASTIC_SAMPLE_OFFSET & STOCHASTIC_SAMPLE_OFFSET_2x2) != 0
DownsampleDepthAndNormal(GroupId, GroupThreadId, Coord.SvPosition, Material, uint2(2, 2), RWDownsampledSceneDepth2x2, RWDownsampledWorldNormal2x2);
#endif
#if (STOCHASTIC_SAMPLE_OFFSET & STOCHASTIC_SAMPLE_OFFSET_2x1) != 0
DownsampleDepthAndNormal(GroupId, GroupThreadId, Coord.SvPosition, Material, uint2(2, 1), RWDownsampledSceneDepth2x1, RWDownsampledWorldNormal2x1);
#endif
}
#if TILE_CLASSIFY_LUMEN
if (bIsValid && IsValid(Material))
{
uint TileBitmask = 0;
uint DiffuseIntegrationMethod = GetDiffuseIntegrationMethod(Material);
const float DiffuseLerp = RoughReflectionsDiffuseLerp(Material);
if (IsHair(Material))
{
TileBitmask |= LUMEN_TILE_BITMASK_GI_ALL;
}
else if (DiffuseIntegrationMethod == DIFFUSE_INTEGRATION_IMPORTANCE_SAMPLE_BRDF || DiffuseLerp < 1.0f)
{
TileBitmask |= LUMEN_TILE_BITMASK_GI_IMPORTANCE_SAMPLE_BRDF;
}
else
{
TileBitmask |= LUMEN_TILE_BITMASK_GI_SIMPLE_DIFFUSE;
}
Material = ApplySmoothBias(Material, false /*bTopLayerRoughness*/);
if (NeedRayTracedReflections(Material.Roughness, Material))
{
TileBitmask |= LUMEN_TILE_BITMASK_REFLECTIONS;
}
// Pack into shared tile bitmask for 8x8 reduction
WriteSharedTileBitmask(LinearThreadIndex, TileBitmask, TILE_BITMASK_INDEX_LUMEN);
}
#endif
#if TILE_CLASSIFY_MEGALIGHTS
if (bIsValid)
{
uint TileBitmask = 0;
if (Material.IsSimple())
{
TileBitmask |= MEGALIGHTS_TILE_BITMASK_SIMPLE;
}
else if (Material.IsSingle())
{
TileBitmask |= MEGALIGHTS_TILE_BITMASK_SINGLE;
}
else if (Material.IsComplexSpecial())
{
TileBitmask |= MEGALIGHTS_TILE_BITMASK_COMPLEX_SPECIAL;
}
else if (Material.IsValid())
{
TileBitmask |= MEGALIGHTS_TILE_BITMASK_COMPLEX;
}
const uint EyeIndex = 0;
const uint GridIndex = ComputeLightGridCellIndex(Coord.SvPosition - View.ViewRectMin.xy, Material.SceneDepth, EyeIndex);
const FCulledLightsGridHeader CulledLightGridHeader = GetCulledLightsGridHeader(GridIndex);
if (CulledLightGridHeader.bHasRectLight)
{
TileBitmask |= MEGALIGHTS_TILE_BITMASK_RECT_LIGHT;
}
if (CulledLightGridHeader.bHasTexturedLight && EnableTexturedRectLights != 0)
{
TileBitmask |= MEGALIGHTS_TILE_BITMASK_TEXTURED_RECT_LIGHT;
}
// Pack into shared tile bitmask for 8x8 reduction
WriteSharedTileBitmask(LinearThreadIndex, TileBitmask, TILE_BITMASK_INDEX_MEGALIGHTS);
}
#endif
#if TILE_CLASSIFY_SUBSTRATE
// Init primitive index
if (DispatchThreadId.x < SUBSTRATE_TILE_TYPE_COUNT && DispatchThreadId.y == 0)
{
const uint TileType = DispatchThreadId.x;
const uint IndexCountPerInstance = bRectPrimitive > 0 ? 4 : 6;
TileDrawIndirectDataBufferUAV[GetSubstrateTileTypeDrawIndirectArgOffset_DWord(TileType) + 0] = IndexCountPerInstance;
}
if (Material.bIsValid)
{
uint TileBitmask = 0;
#if SUBSTRATE_COMPLEXSPECIALPATH
if (Material.bIsComplexSpecial)
{
TileBitmask |= SUBSTRATE_TILE_BITMASK_COMPLEXSPECIAL;
}
else
#endif
if (Material.bIsComplex)
{
TileBitmask |= SUBSTRATE_TILE_BITMASK_COMPLEX;
}
else if (Material.bIsSingle)
{
TileBitmask |= SUBSTRATE_TILE_BITMASK_SINGLE;
}
else if (Material.bIsSimple)
{
TileBitmask |= SUBSTRATE_TILE_BITMASK_SIMPLE;
}
// Pack into shared tile bitmask for 8x8 reduction
WriteSharedTileBitmask(LinearThreadIndex, TileBitmask, TILE_BITMASK_INDEX_SUBSTRATE);
}
#endif
#if TILE_CLASSIFY_LUMEN || TILE_CLASSIFY_MEGALIGHTS || TILE_CLASSIFY_SUBSTRATE
#if PLATFORM_SUPPORTS_SM6_0_WAVE_OPERATIONS
GroupTileBitmask = WaveActiveBitOr(GroupTileBitmask);
if (WaveGetLaneCount() < THREADGROUP_SIZE * THREADGROUP_SIZE)
{
if (LinearThreadIndex == 0)
{
SharedTileBitmask = 0;
}
GroupMemoryBarrierWithGroupSync();
if (WaveIsFirstLane())
{
uint Unused;
InterlockedOr(SharedTileBitmask, GroupTileBitmask, Unused);
}
GroupMemoryBarrierWithGroupSync();
GroupTileBitmask = SharedTileBitmask;
}
#else // PLATFORM_SUPPORTS_SM6_0_WAVE_OPERATIONS
GroupMemoryBarrierWithGroupSync();
// GroupShared reduction
if (LinearThreadIndex < 32)
{
SharedTileBitmask[LinearThreadIndex] = SharedTileBitmask[LinearThreadIndex] | SharedTileBitmask[LinearThreadIndex + 32];
}
GroupMemoryBarrierWithGroupSync();
if (LinearThreadIndex < 16)
{
SharedTileBitmask[LinearThreadIndex] = SharedTileBitmask[LinearThreadIndex] | SharedTileBitmask[LinearThreadIndex + 16];
}
GroupMemoryBarrierWithGroupSync();
if (LinearThreadIndex < 8)
{
SharedTileBitmask[LinearThreadIndex] = SharedTileBitmask[LinearThreadIndex] | SharedTileBitmask[LinearThreadIndex + 8];
}
GroupMemoryBarrierWithGroupSync();
if (LinearThreadIndex < 4)
{
SharedTileBitmask[LinearThreadIndex] = SharedTileBitmask[LinearThreadIndex] | SharedTileBitmask[LinearThreadIndex + 4];
}
GroupMemoryBarrierWithGroupSync();
if (LinearThreadIndex < 2)
{
SharedTileBitmask[LinearThreadIndex] = SharedTileBitmask[LinearThreadIndex] | SharedTileBitmask[LinearThreadIndex + 2];
}
GroupMemoryBarrierWithGroupSync();
if (LinearThreadIndex < 1)
{
SharedTileBitmask[LinearThreadIndex] = SharedTileBitmask[LinearThreadIndex] | SharedTileBitmask[LinearThreadIndex + 1];
}
#endif // PLATFORM_SUPPORTS_SM6_0_WAVE_OPERATIONS
if (LinearThreadIndex == 0)
{
#if TILE_CLASSIFY_LUMEN
{
RWLumenTileBitmask[int3(Coord.SvPosition / TILE_SIZE, Coord.ClosureIndex)] = ReadSharedTileBitmask(0, TILE_BITMASK_INDEX_LUMEN);
}
#endif
#if TILE_CLASSIFY_MEGALIGHTS
if (Coord.ClosureIndex == 0)
{
RWMegaLightsTileBitmask[Coord.SvPosition / TILE_SIZE] = ReadSharedTileBitmask(0, TILE_BITMASK_INDEX_MEGALIGHTS);
}
#endif
#if TILE_CLASSIFY_SUBSTRATE
{
const uint TileBitmask = ReadSharedTileBitmask(0, TILE_BITMASK_INDEX_SUBSTRATE);
if (TileBitmask != 0)
{
const uint2 TileCoord = (Coord.SvPosition.xy - uint2(View.ViewRectMin.xy)) >> TILE_SIZE_DIV_SHIFT;
const uint EncodedTile = SubstratePackTile(TileCoord, TileEncoding);
#if SUBSTRATE_COMPLEXSPECIALPATH
if (TileBitmask & SUBSTRATE_TILE_BITMASK_COMPLEXSPECIAL)
{
uint WriteToIndex;
InterlockedAdd(TileDrawIndirectDataBufferUAV[GetSubstrateTileTypeDrawIndirectArgOffset_DWord(SUBSTRATE_TILE_TYPE_COMPLEX_SPECIAL) + 1], 1, WriteToIndex);
TileListBufferUAV[GetTileListBufferOffsets(SUBSTRATE_TILE_TYPE_COMPLEX_SPECIAL) + WriteToIndex] = EncodedTile;
}
else
#endif
if (TileBitmask & SUBSTRATE_TILE_BITMASK_COMPLEX)
{
uint WriteToIndex;
InterlockedAdd(TileDrawIndirectDataBufferUAV[GetSubstrateTileTypeDrawIndirectArgOffset_DWord(SUBSTRATE_TILE_TYPE_COMPLEX) + 1], 1, WriteToIndex);
TileListBufferUAV[GetTileListBufferOffsets(SUBSTRATE_TILE_TYPE_COMPLEX) + WriteToIndex] = EncodedTile;
}
else if (TileBitmask & SUBSTRATE_TILE_BITMASK_SINGLE)
{
uint WriteToIndex;
InterlockedAdd(TileDrawIndirectDataBufferUAV[GetSubstrateTileTypeDrawIndirectArgOffset_DWord(SUBSTRATE_TILE_TYPE_SINGLE) + 1], 1, WriteToIndex);
TileListBufferUAV[GetTileListBufferOffsets(SUBSTRATE_TILE_TYPE_SINGLE) + WriteToIndex] = EncodedTile;
}
else // (TileBitmask & SUBSTRATE_TILE_BITMASK_SIMPLE)
{
uint WriteToIndex;
InterlockedAdd(TileDrawIndirectDataBufferUAV[GetSubstrateTileTypeDrawIndirectArgOffset_DWord(SUBSTRATE_TILE_TYPE_SIMPLE) + 1], 1, WriteToIndex);
TileListBufferUAV[GetTileListBufferOffsets(SUBSTRATE_TILE_TYPE_SIMPLE) + WriteToIndex] = EncodedTile;
}
}
}
#endif
}
#endif // TILE_CLASSIFY_LUMEN || TILE_CLASSIFY_MEGALIGHTS || TILE_CLASSIFY_SUBSTRATE
#if REPROJECT_LUMEN || REPROJECT_MEGALIGHTS
if (bIsValid)
{
uint2 ScreenCoord = Coord.SvPosition;
FLumenPackedPixelData LumenPackedPixelData = (FLumenPackedPixelData)LUMEN_INVALID_PACKED_PIXEL_DATA;
FMegaLightsPackedPixelData MegaLightsPackedPixelData = (FMegaLightsPackedPixelData)MEGALIGHTS_INVALID_PACKED_PIXEL_DATA;
if (Material.IsValid())
{
LumenPackedPixelData.Packed = 0;
LumenPackedPixelData.SetHasBackfaceDiffuse(Material.bHasBackfaceDiffuse);
MegaLightsPackedPixelData.Packed = 0;
float2 ScreenUV = (ScreenCoord + 0.5f) * View.BufferSizeAndInvSize.zw;
float2 ScreenPosition = (ScreenUV - View.ScreenPositionScaleBias.wz) / View.ScreenPositionScaleBias.xy;
float3 TranslatedWorldPosition = mul(float4(GetScreenPositionForProjectionType(ScreenPosition, Material.SceneDepth), Material.SceneDepth, 1), View.ScreenToTranslatedWorld).xyz;
const float RandomScalar = BlueNoiseScalar(ScreenCoord, StochasticLightingStateFrameIndex);
#if (STOCHASTIC_SAMPLE_OFFSET & STOCHASTIC_SAMPLE_OFFSET_2x2) != 0
{
float4 UpsampleWeights = ComputeUpsampleWeights(GroupThreadId, ScreenCoord, ScreenUV, ScreenPosition, TranslatedWorldPosition, Material, uint2(2, 2));
const uint2 StochasticBilinearOffset = GetStochasticBilinearOffset(RandomScalar, UpsampleWeights);
bool bCanReconstruct = dot(UpsampleWeights, 1.0f) >= 0.01f;
bool bSetForLumen = true;
bool bSetForMegaLights = true;
#if STOCHASTIC_SAMPLE_OFFSET == (STOCHASTIC_SAMPLE_OFFSET_2x1 | STOCHASTIC_SAMPLE_OFFSET_2x2)
bSetForLumen = LumenStochasticSampleMode == STOCHASTIC_SAMPLE_OFFSET_2x2;
bSetForMegaLights = MegaLightsStochasticSampleMode == STOCHASTIC_SAMPLE_OFFSET_2x2;
#endif
if (bSetForLumen)
{
LumenPackedPixelData.SetStochasticSampleOffset(StochasticBilinearOffset, bCanReconstruct);
}
if (bSetForMegaLights)
{
MegaLightsPackedPixelData.SetStochasticSampleOffset(StochasticBilinearOffset, bCanReconstruct);
}
}
#endif
#if (STOCHASTIC_SAMPLE_OFFSET & STOCHASTIC_SAMPLE_OFFSET_2x1) != 0
{
float4 UpsampleWeights = ComputeUpsampleWeights(GroupThreadId, ScreenCoord, ScreenUV, ScreenPosition, TranslatedWorldPosition, Material, uint2(2, 1));
const uint2 StochasticBilinearOffset = GetStochasticBilinearOffset(RandomScalar, UpsampleWeights);
bool bCanReconstruct = dot(UpsampleWeights, 1.0f) >= 0.01f;
bool bSetForLumen = true;
bool bSetForMegaLights = true;
#if STOCHASTIC_SAMPLE_OFFSET == (STOCHASTIC_SAMPLE_OFFSET_2x1 | STOCHASTIC_SAMPLE_OFFSET_2x2)
bSetForLumen = LumenStochasticSampleMode == STOCHASTIC_SAMPLE_OFFSET_2x1;
bSetForMegaLights = MegaLightsStochasticSampleMode == STOCHASTIC_SAMPLE_OFFSET_2x1;
#endif
if (bSetForLumen)
{
LumenPackedPixelData.SetStochasticSampleOffset(StochasticBilinearOffset, bCanReconstruct);
}
if (bSetForMegaLights)
{
MegaLightsPackedPixelData.SetStochasticSampleOffset(StochasticBilinearOffset, bCanReconstruct);
}
}
#endif
float3 HistoryScreenPosition = GetHistoryScreenPosition(ScreenPosition, ScreenUV, ConvertToDeviceZ(Material.SceneDepth));
float2 HistoryScreenUV = HistoryScreenPosition.xy * HistoryScreenPositionScaleBias.xy + HistoryScreenPositionScaleBias.wz;
bool bHistoryWasOnScreen = all(HistoryScreenUV >= HistoryUVMinMax.xy) && all(HistoryScreenUV <= HistoryUVMinMax.zw);
float2 HistoryScreenCoord;
half2 HistoryBilinearWeights;
// Encode and write out HistoryScreenCoord with subpixel precision
{
HistoryScreenUV = clamp(HistoryScreenUV, HistoryGatherUVMinMax.xy, HistoryGatherUVMinMax.zw);
HistoryScreenCoord = HistoryScreenUV * HistoryBufferSizeAndInvSize.xy - 0.5f;
uint Encoded = EncodeHistoryScreenCoord(HistoryScreenCoord, HistorySubPixelGridSizeAndInvSize.xy);
RWEncodedHistoryScreenCoord[ScreenCoord] = Encoded;
float4 Decoded = DecodeHistoryScreenCoord(Encoded, HistoryScreenCoordDecodeShift, HistorySubPixelGridSizeAndInvSize.zw);
HistoryScreenCoord = Decoded.xy;
HistoryBilinearWeights = half2(Decoded.zw);
}
if (bHistoryWasOnScreen)
{
float2 HistoryGatherUV = (HistoryScreenCoord + 1.0f) * HistoryBufferSizeAndInvSize.zw;
float4 HistorySampleSceneDepth4 = DepthHistoryTexture.GatherRed(GlobalPointClampedSampler, HistoryGatherUV).wzxy;
HistorySampleSceneDepth4.x = ConvertFromDeviceZ(HistorySampleSceneDepth4.x);
HistorySampleSceneDepth4.y = ConvertFromDeviceZ(HistorySampleSceneDepth4.y);
HistorySampleSceneDepth4.z = ConvertFromDeviceZ(HistorySampleSceneDepth4.z);
HistorySampleSceneDepth4.w = ConvertFromDeviceZ(HistorySampleSceneDepth4.w);
float ReprojectedSceneDepth = ConvertFromDeviceZ(HistoryScreenPosition.z);
float4 DistanceToHistoryValue;
{
#define PLANE_DISOCCLUSION_WEIGHTS 0
#define EXPAND_HISTORY_DISTANCE_THRESHOLD_FOR_JITTER !PLANE_DISOCCLUSION_WEIGHTS
#if PLANE_DISOCCLUSION_WEIGHTS
float3 PrevTranslatedPrevWorldPosition = mul(float4(GetScreenPositionForProjectionType(HistoryScreenPosition.xy, ReprojectedSceneDepth), ReprojectedSceneDepth, 1), View.PrevScreenToTranslatedWorld).xyz;
float4 PrevTranslatedPrevScenePlane = float4(Material.WorldNormalForPositionBias, dot(PrevTranslatedPrevWorldPosition, Material.WorldNormalForPositionBias));
float2 HistoryGatherUV00 = HistoryGatherUV + HistoryBufferSizeAndInvSize.zw * float2(-0.5, -0.5);
float2 HistoryGatherUV10 = HistoryGatherUV + HistoryBufferSizeAndInvSize.zw * float2(+0.5, -0.5);
float2 HistoryGatherUV01 = HistoryGatherUV + HistoryBufferSizeAndInvSize.zw * float2(-0.5, +0.5);
float2 HistoryGatherUV11 = HistoryGatherUV + HistoryBufferSizeAndInvSize.zw * float2(+0.5, +0.5);
float2 HistoryScreenPosition00 = (HistoryGatherUV00 - HistoryScreenPositionScaleBias.wz) / HistoryScreenPositionScaleBias.xy;
float3 PrevTranslatedHistoryWorldPosition00 = mul(float4(GetScreenPositionForProjectionType(HistoryScreenPosition00, HistorySampleSceneDepth4.x), HistorySampleSceneDepth4.x, 1), View.PrevScreenToTranslatedWorld).xyz;
DistanceToHistoryValue.x = abs(dot(float4(PrevTranslatedHistoryWorldPosition00, -1), PrevTranslatedPrevScenePlane));
float2 HistoryScreenPosition10 = (HistoryGatherUV10.x - HistoryScreenPositionScaleBias.wz) / HistoryScreenPositionScaleBias.xy;
float3 PrevTranslatedHistoryWorldPosition10 = mul(float4(GetScreenPositionForProjectionType(HistoryScreenPosition10, HistorySampleSceneDepth4.y), HistorySampleSceneDepth4.y, 1), View.PrevScreenToTranslatedWorld).xyz;
DistanceToHistoryValue.y = abs(dot(float4(PrevTranslatedHistoryWorldPosition10, -1), PrevTranslatedPrevScenePlane));
float2 HistoryScreenPosition01 = (HistoryGatherUV01 - HistoryScreenPositionScaleBias.wz) / HistoryScreenPositionScaleBias.xy;
float3 PrevTranslatedHistoryWorldPosition01 = mul(float4(GetScreenPositionForProjectionType(HistoryScreenPosition01, HistorySampleSceneDepth4.z), HistorySampleSceneDepth4.z, 1), View.PrevScreenToTranslatedWorld).xyz;
DistanceToHistoryValue.z = abs(dot(float4(PrevTranslatedHistoryWorldPosition01, -1), PrevTranslatedPrevScenePlane));
float2 HistoryScreenPosition11 = (HistoryGatherUV11 - HistoryScreenPositionScaleBias.wz) / HistoryScreenPositionScaleBias.xy;
float3 PrevTranslatedHistoryWorldPosition11 = mul(float4(GetScreenPositionForProjectionType(HistoryScreenPosition11.xy, HistorySampleSceneDepth4.w), HistorySampleSceneDepth4.w, 1), View.PrevScreenToTranslatedWorld).xyz;
DistanceToHistoryValue.w = abs(dot(float4(PrevTranslatedHistoryWorldPosition11, -1), PrevTranslatedPrevScenePlane));
#else
DistanceToHistoryValue = abs(HistorySampleSceneDepth4 - ReprojectedSceneDepth);
#endif
}
half4 LumenDepthWeights;
{
float LumenDisocclusionDistanceThreshold = Material.bHasBackfaceDiffuse ? LumenHistoryDistanceThresholdForFoliage : LumenHistoryDistanceThreshold;
LumenDisocclusionDistanceThreshold *= lerp(0.5f, 1.5f, RandomScalar);
#if EXPAND_HISTORY_DISTANCE_THRESHOLD_FOR_JITTER
const float3 V = normalize(-TranslatedWorldPosition);
// Raise the threshold at grazing angles to compensate for TAA jitter causing a depth mismatch dependent on the angle
// This also introduces some ghosting around characters, needs a better solution
LumenDisocclusionDistanceThreshold /= clamp(saturate(dot(V, Material.WorldNormalForPositionBias)), 0.1f, 1.0f);
#endif
LumenDepthWeights = select(DistanceToHistoryValue >= ReprojectedSceneDepth * LumenDisocclusionDistanceThreshold, half(0.0), half(1.0));
}
float MegaLightsDisocclusionDistanceThreshold = 0.1f;
half4 MegaLightsDepthWeights = select(DistanceToHistoryValue >= ReprojectedSceneDepth * MegaLightsDisocclusionDistanceThreshold, half(0.0), half(1.0));
// Any history neighbor depth valid bit for GenerateLightSamplesCS
MegaLightsPackedPixelData.SetAnyHistoryDepthValid(any(MegaLightsDepthWeights > 0.01));
half4 HistoryWeights = half4(
(1 - HistoryBilinearWeights.y) * (1 - HistoryBilinearWeights.x),
(1 - HistoryBilinearWeights.y) * HistoryBilinearWeights.x,
HistoryBilinearWeights.y * (1 - HistoryBilinearWeights.x),
HistoryBilinearWeights.y * HistoryBilinearWeights.x);
half4 LumenHistoryWeights = HistoryWeights * LumenDepthWeights;
half4 MegaLightsHistoryWeights = HistoryWeights * MegaLightsDepthWeights;
#if HISTORY_REJECT_BASED_ON_NORMAL
{
// UnpackNormalAndShadingInfo(Texture2DSampleLevel(DiffuseIndirectNormalHistory, GlobalPointClampedSampler, InUV, 0)).Normal
float4 NormalR = NormalAndShadingInfoHistory.GatherRed(GlobalPointClampedSampler, HistoryGatherUV).wzxy;
float4 NormalG = NormalAndShadingInfoHistory.GatherGreen(GlobalPointClampedSampler, HistoryGatherUV).wzxy;
float4 NormalB = NormalAndShadingInfoHistory.GatherBlue(GlobalPointClampedSampler, HistoryGatherUV).wzxy;
const float3 HistoryNormal00 = UnpackNormalAndShadingInfo(float4(NormalR.x, NormalG.x, NormalB.x, 0)).Normal;
const float3 HistoryNormal10 = UnpackNormalAndShadingInfo(float4(NormalR.y, NormalG.y, NormalB.y, 0)).Normal;
const float3 HistoryNormal01 = UnpackNormalAndShadingInfo(float4(NormalR.z, NormalG.z, NormalB.z, 0)).Normal;
const float3 HistoryNormal11 = UnpackNormalAndShadingInfo(float4(NormalR.w, NormalG.w, NormalB.w, 0)).Normal;
const float4 HistoryNormalWeights = select(float4(
dot(HistoryNormal00, Material.WorldNormalForPositionBias),
dot(HistoryNormal10, Material.WorldNormalForPositionBias),
dot(HistoryNormal01, Material.WorldNormalForPositionBias),
dot(HistoryNormal11, Material.WorldNormalForPositionBias)) < LumenHistoryNormalCosThreshold, half(0.0), half(1.0));
LumenHistoryWeights *= HistoryNormalWeights;
}
#endif
{
const float4 PackedW = NormalAndShadingInfoHistory.GatherAlpha(GlobalPointClampedSampler, HistoryGatherUV).wzxy;
// Reject based on the foliage material flag (bHasBackfaceDiffuse)
const bool4 bHasBackfaceDiffuse = bool4(
UnpackNormalAndShadingInfo(float4(0, 0, 0, PackedW.x)).bHasBackfaceDiffuse,
UnpackNormalAndShadingInfo(float4(0, 0, 0, PackedW.y)).bHasBackfaceDiffuse,
UnpackNormalAndShadingInfo(float4(0, 0, 0, PackedW.z)).bHasBackfaceDiffuse,
UnpackNormalAndShadingInfo(float4(0, 0, 0, PackedW.w)).bHasBackfaceDiffuse);
LumenHistoryWeights *= select(Material.bHasBackfaceDiffuse.xxxx == bHasBackfaceDiffuse, half(1.0), half(0.0));
// If shading info history is available, used it to only fetch compatible pixels.
// For now only hair pixel are filtered out as their shading model is too different from the others
const bool4 bIsHair4 = bool4(
UnpackNormalAndShadingInfo(float4(0, 0, 0, PackedW.x)).bIsHair,
UnpackNormalAndShadingInfo(float4(0, 0, 0, PackedW.y)).bIsHair,
UnpackNormalAndShadingInfo(float4(0, 0, 0, PackedW.z)).bIsHair,
UnpackNormalAndShadingInfo(float4(0, 0, 0, PackedW.w)).bIsHair);
MegaLightsHistoryWeights *= select(Material.bIsHair.xxxx == bIsHair4, half(1.0), half(0.0));
#if REPROJECT_MEGALIGHTS
// History might have invalid lighting pixels. Only use history lighting if it has valid data.
half4 FramesAccumulatedHistory4 = MegaLightsNumFramesAccumulatedHistory.GatherRed(GlobalPointClampedSampler, HistoryGatherUV).wzxy;
MegaLightsHistoryWeights = select(FramesAccumulatedHistory4 > 0.0, MegaLightsHistoryWeights, 0.0);
#endif
}
// History neighbor valid mask for ScreenProbeTemporalReprojectionCS and DenoiserTemporalCS
LumenPackedPixelData.SetHistorySampleValidity(LumenHistoryWeights > 0.01);
MegaLightsPackedPixelData.SetHistorySampleValidity(MegaLightsHistoryWeights > 0.01);
}
}
#if REPROJECT_LUMEN
RWLumenPackedPixelData[uint3(ScreenCoord, Coord.ClosureIndex)] = LumenPackedPixelData.Packed;
#endif
#if REPROJECT_MEGALIGHTS
if (Coord.ClosureIndex == 0)
{
RWMegaLightsPackedPixelData[ScreenCoord] = MegaLightsPackedPixelData.Packed;
}
#endif
}
#endif
}