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

745 lines
29 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#ifndef NUM_SAMPLES_PER_PIXEL_1D
#define NUM_SAMPLES_PER_PIXEL_1D 1
#define NUM_SAMPLES_PER_PIXEL_2D_X 1
#define NUM_SAMPLES_PER_PIXEL_2D_Y 1
#endif
#ifndef TILE_TYPE
#define TILE_TYPE TILE_MODE_EMPTY
#endif
// When loading SSS checkerboard pixel, do not adjust DiffuseColor/SpecularColor to preserve specular and diffuse lighting values for each pixel
#define ALLOW_SSS_MATERIAL_OVERRIDE 0
// Disable hair backlit during sample generation as Hair's TT term creates too much false positive:
// the TT terms is very strong, and is in most case occluded by underlying geometry (head/body/...)
#define HAIR_BSDF_BACKLIT 0
#define USE_IES_PROFILE 1
#define USE_LIGHT_FUNCTION_ATLAS 1
#include "../Common.ush"
#include "/Engine/Shared/MegaLightsDefinitions.h"
// Substrate tile for faster shading
#if SUBSTRATE_ENABLED
#if TILE_TYPE == TILE_MODE_SIMPLE_SHADING || TILE_TYPE == TILE_MODE_SIMPLE_SHADING_RECT || TILE_TYPE == TILE_MODE_SIMPLE_SHADING_RECT_TEXTURED || TILE_TYPE == TILE_MODE_EMPTY
#define SUBSTRATE_FASTPATH 1
#define SUBSTRATE_SINGLEPATH 0
#define SUBSTRATE_COMPLEXSPECIALPATH 0
#elif TILE_TYPE == TILE_MODE_SINGLE_SHADING || TILE_TYPE == TILE_MODE_SINGLE_SHADING_RECT || TILE_TYPE == TILE_MODE_SINGLE_SHADING_RECT_TEXTURED
#define SUBSTRATE_FASTPATH 0
#define SUBSTRATE_SINGLEPATH 1
#define SUBSTRATE_COMPLEXSPECIALPATH 0
#elif TILE_TYPE == TILE_MODE_COMPLEX_SHADING || TILE_TYPE == TILE_MODE_COMPLEX_SHADING_RECT || TILE_TYPE == TILE_MODE_COMPLEX_SHADING_RECT_TEXTURED
#define SUBSTRATE_FASTPATH 0
#define SUBSTRATE_SINGLEPATH 0
#define SUBSTRATE_COMPLEXSPECIALPATH 0
#else // Special TILE_MODE_COMPLEX_SPECIAL_SHADING || TILE_MODE_COMPLEX_SPECIAL_SHADING_RECT || TILE_MODE_COMPLEX_SPECIAL_SHADING_RECT_TEXTURED
#define SUBSTRATE_FASTPATH 0
#define SUBSTRATE_SINGLEPATH 0
#define SUBSTRATE_COMPLEXSPECIALPATH 1
#endif
#endif
#if SUBSTRATE_FASTPATH || SUBSTRATE_SINGLEPATH
#define SUBSTRATE_LOAD_FROM_MATERIALCONTAINER 0
#endif
#include "../BlueNoise.ush"
#include "MegaLightsShading.ush"
#include "MegaLightsRayTracing.ush"
#include "MegaLightsSampling.ush"
#include "../LightFunctionAtlas/LightFunctionAtlasCommon.usf"
#include "../StochasticLighting/StochasticLightingCommon.ush"
// Allow register spilling on certain platform to avoid shader compilation issue
#if SUBSTRATE_ENABLED
#pragma warning(disable:7203)
#endif
ADAPTIVE_LICM
struct FLightTargetPDF
{
float Weight;
};
FLightTargetPDF InitLightTargetPDF()
{
FLightTargetPDF LightTargetPDF;
LightTargetPDF.Weight = 0.0f;
return LightTargetPDF;
}
FLightTargetPDF GetLocalLightTargetPDF(FDeferredLightData LightData, float3 TranslatedWorldPosition, FMegaLightsMaterial Material, uint2 ScreenCoord, inout FShaderPrintContext DebugContext)
{
float3 CameraVector = normalize(TranslatedWorldPosition - View.TranslatedWorldCameraOrigin);
float4 LightAttenuation = 1.0f;
float Dither = 0.5f;
float SurfaceShadow = 1.0f;
float AmbientOcclusion = 1.0f;
LightData.ShadowedBits = 0;
#if INPUT_TYPE == INPUT_TYPE_HAIRSTRANDS || USE_HAIR_COMPLEX_TRANSMITTANCE
if (Material.bNeedsComplexTransmittance)
{
LightData.HairTransmittance = EvaluateDualScattering(Material.DiffuseColor, Material.WorldNormal, Material.Roughness, CameraVector, -LightData.Direction);
}
#endif
FDeferredLightingSplit SplitLighting = GetMegaLightsSplitLighting(
TranslatedWorldPosition, CameraVector, Material, AmbientOcclusion,
LightData, LightAttenuation, Dither, ScreenCoord,
SurfaceShadow);
//float Lum = Luminance(SplitLighting.DiffuseLighting.xyz + SplitLighting.SpecularLighting.xyz) * View.PreExposure;
float Lum = SplitLighting.LightingLuminance * View.PreExposure;
if (LightData.IESAtlasIndex >= 0 && Lum > 0.01f)
{
Lum *= ComputeLightProfileMultiplier(TranslatedWorldPosition, LightData.TranslatedWorldPosition, -LightData.Direction, LightData.Tangent, LightData.IESAtlasIndex);
}
// Simulate tonemapping
FLightTargetPDF LightTargetPDF = InitLightTargetPDF();
LightTargetPDF.Weight = log2(Lum + 1.0f);
return LightTargetPDF;
}
uint2 DownsampledViewMin;
uint2 DownsampledViewSize;
float MinSampleWeight;
uint2 NumSamplesPerPixel;
int UseIESProfiles;
int UseLightFunctionAtlas;
int bVisualizeLightLoopIterations;
int DebugMode;
uint DebugVisualizeLight;
uint DebugLightId;
RWTexture2D<float> RWDownsampledSceneDepth;
RWTexture2D<UNORM float3> RWDownsampledSceneWorldNormal;
RWTexture2D<uint> RWLightSamples;
RWTexture2D<uint> RWLightSampleRays;
Texture2D<uint> PackedPixelDataTexture;
Texture2D<uint> EncodedHistoryScreenCoordTexture;
Texture2D MegaLightsDepthHistory;
float4 HistorySubPixelGridSizeAndInvSize;
uint2 HistoryScreenCoordDecodeShift;
StructuredBuffer<uint> DownsampledTileAllocator;
StructuredBuffer<uint> DownsampledTileData;
StructuredBuffer<uint> VisibleLightHashHistory;
StructuredBuffer<uint> VisibleLightMaskHashHistory;
uint2 HistoryVisibleLightHashViewMinInTiles;
uint2 HistoryVisibleLightHashViewSizeInTiles;
float LightHiddenPDFWeight;
float LightHiddenPDFWeightForHistoryMiss;
float AreaLightHiddenPDFWeight;
uint DownsampledTileDataStride;
float2 DownsampledBufferInvSize;
uint2 GetSampleCoord(uint2 DownsampledScreenCoord, uint LightSampleX, uint LightSampleY)
{
return DownsampledScreenCoord * NumSamplesPerPixel + uint2(LightSampleX, LightSampleY);
}
uint2 GetSampleCoord(uint2 DownsampledScreenCoord, uint LightSampleIndex)
{
return GetSampleCoord(DownsampledScreenCoord, LightSampleIndex % NUM_SAMPLES_PER_PIXEL_2D_X, LightSampleIndex / NUM_SAMPLES_PER_PIXEL_2D_X);
}
uint GetLightVisibilityMask(uint VisibleLightMaskHash[VISIBLE_LIGHT_HASH_SIZE], uint PrevLocalLightIndex)
{
uint Hash = PCGHash(PrevLocalLightIndex);
uint WrappedLocalLightIndex = (Hash >> 16) % 32;
uint VisibilityMask = (VisibleLightMaskHash[WrappedLocalLightIndex / 8] >> (4 * (WrappedLocalLightIndex % 8))) & 0xF;
return VisibilityMask;
}
void SampleLight(
uint2 ScreenCoord,
float3 TranslatedWorldPosition,
const FMegaLightsMaterial Material,
uint LightingChannelMask,
uint VisibleLightHash[VISIBLE_LIGHT_HASH_SIZE],
bool bHasValidHistory,
uint ForwardLightIndex,
FDeferredLightData LightData,
uint LightSceneId,
uint LightLightingChannelMask,
uint PrevForwardLightIndex,
inout FLightSampler LightSampler,
inout FShaderPrintContext DebugContext)
{
if ((LightingChannelMask & LightLightingChannelMask) == 0)
{
return;
}
if (UseLightFunctionAtlas == 0)
{
LightData.LightFunctionAtlasLightIndex = 0;
}
if (UseIESProfiles == 0)
{
LightData.IESAtlasIndex = -1;
}
if (!IsRectLightTileType(TILE_TYPE))
{
LightData.bRectLight = false;
}
if (!IsTexturedLightTileType(TILE_TYPE))
{
LightData.RectLightData.AtlasData.AtlasMaxLevel = MAX_RECT_ATLAS_MIP;
}
FLightTargetPDF LightTargetPDF = GetLocalLightTargetPDF(LightData, TranslatedWorldPosition, Material, ScreenCoord, DebugContext);
bool bWasVisibleInLastFrame = true;
#if GUIDE_BY_HISTORY
if (LightTargetPDF.Weight > MinSampleWeight && PrevForwardLightIndex >= 0)
{
bWasVisibleInLastFrame = GetLightVisibility(VisibleLightHash, PrevForwardLightIndex);
}
else
{
bWasVisibleInLastFrame = false;
}
#endif
if (DebugContext.bIsActive)
{
Newline(DebugContext);
Print(DebugContext, LightSceneId, Select(LightSceneId == DebugLightId, FontSelected, FontValue));
Print(DebugContext, ForwardLightIndex, Select(LightSceneId == DebugLightId, FontSelected, FontValue));
Print(DebugContext, LightTargetPDF.Weight, Select(LightTargetPDF.Weight > MinSampleWeight, FontWhite, FontGrey));
Print(DebugContext, LightData.LightFunctionAtlasLightIndex, Select(LightData.LightFunctionAtlasLightIndex != 0, FontWhite, FontGrey));
Print(DebugContext, LightData.IESAtlasIndex, Select(LightData.IESAtlasIndex != -1, FontWhite, FontGrey));
Print(DebugContext, bWasVisibleInLastFrame ? 1u : 0u, Select(bWasVisibleInLastFrame, FontWhite, FontGrey), 4, 1);
if ((DebugVisualizeLight != 0 && LightSceneId == DebugLightId) || DebugVisualizeLight == 2)
{
const float4 DebugLightColor = bWasVisibleInLastFrame ? float4(0, 1, 0, 1) : float4(0, 0.5f, 0, 1);
AddSphereTWS(DebugContext, LightData.TranslatedWorldPosition, 10.0f, DebugLightColor);
AddLineTWS(DebugContext, TranslatedWorldPosition, LightData.TranslatedWorldPosition, DebugLightColor);
}
}
if (LightTargetPDF.Weight > MinSampleWeight)
{
if (!bWasVisibleInLastFrame)
{
LightTargetPDF.Weight *= bHasValidHistory ? LightHiddenPDFWeight : LightHiddenPDFWeightForHistoryMiss;
}
AddLightSample(LightSampler, LightTargetPDF.Weight, ForwardLightIndex, bWasVisibleInLastFrame, LightData.bRadialLight);
}
}
void SampleLight(
uint2 ScreenCoord,
float3 TranslatedWorldPosition,
const FMegaLightsMaterial Material,
uint LightingChannelMask,
uint VisibleLightHash[VISIBLE_LIGHT_HASH_SIZE],
bool bHasValidHistory,
uint LocalLightIndex,
inout FLightSampler LightSampler,
inout FShaderPrintContext DebugContext)
{
const FLocalLightData LocalLightData = GetLocalLightData(LocalLightIndex, 0);
FDeferredLightData LightData = ConvertToDeferredLight(LocalLightData);
SampleLight(ScreenCoord, TranslatedWorldPosition, Material, LightingChannelMask, VisibleLightHash, bHasValidHistory,
LocalLightIndex, LightData, LocalLightData.Internal.LightSceneId, UnpackLightingChannelMask(LocalLightData), LocalLightData.Internal.PrevLocalLightIndex,
LightSampler, DebugContext);
}
void SampleDirectionalLight(
uint DirectionalLightIndex,
uint2 ScreenCoord,
float3 TranslatedWorldPosition,
const FMegaLightsMaterial Material,
uint LightingChannelMask,
uint VisibleLightHash[VISIBLE_LIGHT_HASH_SIZE],
bool bHasValidHistory,
inout FLightSampler LightSampler,
inout FShaderPrintContext DebugContext)
{
uint LightIndex = ForwardLightStruct.DirectionalLightIndices[DirectionalLightIndex];
FForwardLightData ForwardLightData = GetForwardLightData(LightIndex, 0);
FDeferredLightData LightData = ConvertToDeferredLight(ForwardLightData);
LightData.bRadialLight = false;
LightData.bInverseSquared = false;
SampleLight(ScreenCoord, TranslatedWorldPosition, Material, LightingChannelMask, VisibleLightHash, bHasValidHistory,
LightIndex, LightData, ForwardLightData.LightSceneId, UnpackLightingChannelMask(ForwardLightData), ForwardLightData.PrevLocalLightIndex,
LightSampler, DebugContext);
}
#if DEBUG_MODE
groupshared int SharedGridCellLightCount[2];
groupshared int SharedLightLoopIterationCount[2];
#endif
/**
* 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,
uint3 DispatchThreadId : SV_DispatchThreadID)
{
uint LinearThreadIndex = GroupThreadId.y * THREADGROUP_SIZE + GroupThreadId.x;
uint DownsampledTileIndex = GroupId.x;
if (DownsampledTileIndex < DownsampledTileAllocator[TILE_TYPE])
{
uint LocalCandidateLightHiMask = 0;
uint2 DownsampledTileCoord = UnpackTile(DownsampledTileData[DownsampledTileIndex + TILE_TYPE * DownsampledTileDataStride]);
uint2 DownsampledScreenCoord = DownsampledTileCoord * TILE_SIZE + GroupThreadId.xy + DownsampledViewMin;
int LightLoopIterationCount = 0;
int GridCellLightCount = 0;
const bool bForceSimpleShading = IsSimpleShadingTileType(TILE_TYPE);
// Use a separate context for iteration count visualization so we don't display all the debug texts
FShaderPrintContext DebugContext_VisIterations = InitDebugContext(all(GroupThreadId.xy == 0), float2(0, 0));
DebugContext_VisIterations.bIsActive = DebugContext_VisIterations.bIsActive && bVisualizeLightLoopIterations != 0;
if (all(DownsampledScreenCoord < DownsampledViewMin + DownsampledViewSize))
{
uint2 ScreenCoord = DownsampledScreenCoordToScreenCoord(DownsampledScreenCoord);
FShaderPrintContext DebugContext = InitDebugContext(DownsampledScreenCoord, /*bDownsampled*/ true, float2(0.05, 0.05));
DebugContext.bIsActive = DebugContext.bIsActive && !bVisualizeLightLoopIterations;
const float2 ScreenUV = (ScreenCoord + 0.5f) * View.BufferSizeAndInvSize.zw;
const FMegaLightsMaterial Material = LoadMaterial(ScreenUV, ScreenCoord, bForceSimpleShading);
const float SceneDepth = Material.Depth;
if (SceneDepth > 0)
{
const float3 TranslatedWorldPosition = GetTranslatedWorldPositionFromScreenUV(ScreenUV, SceneDepth);
const uint EyeIndex = 0; // TODO
const uint GridIndex = ComputeLightGridCellIndex(ScreenCoord - View.ViewRectMin.xy, SceneDepth, EyeIndex);
const FCulledLightsGridHeader CulledLightsGridHeader = GetCulledLightsGridHeader(GridIndex);
const uint NumLightsInGridCell = min(CulledLightsGridHeader.NumMegaLights, GetMaxLightsPerCell());
const uint NumLocalLights = GetNumLocalLights();
const uint LightingChannelMask = GetSceneLightingChannel(ScreenCoord);
bool bHasValidHistory = true;
uint2 PrevScreenCoord = ScreenCoord;
#define REPROJECT_HISTORY_FOR_GUIDING 1
#if GUIDE_BY_HISTORY && REPROJECT_HISTORY_FOR_GUIDING
{
FMegaLightsPackedPixelData PackedPixelData = (FMegaLightsPackedPixelData)PackedPixelDataTexture[ScreenCoord];
uint EncodedHistoryScreenCoord = EncodedHistoryScreenCoordTexture[ScreenCoord];
bHasValidHistory = PackedPixelData.AnyHistoryDepthValid();
float4 Decoded = DecodeHistoryScreenCoord(EncodedHistoryScreenCoord, HistoryScreenCoordDecodeShift, HistorySubPixelGridSizeAndInvSize.zw);
PrevScreenCoord = uint2(Decoded.xy);
}
#endif
if (DebugContext.bIsActive)
{
Print(DebugContext, TEXT("GenerateSamples"), FontTitle);
Newline(DebugContext);
Print(DebugContext, TEXT("ScreenCoord : "));
Print(DebugContext, ScreenCoord.x, FontValue);
Print(DebugContext, ScreenCoord.y, FontValue);
Newline(DebugContext);
Print(DebugContext, TEXT("Roughness : "));
Print(DebugContext, Material.Roughness, FontValue);
Newline(DebugContext);
Print(DebugContext, TEXT("TileType : "));
PrintTileTypeString(DebugContext, TILE_TYPE, Select(IsSimpleShadingTileType(TILE_TYPE), FontGreen, FontRed));
Newline(DebugContext);
Print(DebugContext, TEXT("View.PreExposure : "));
Print(DebugContext, View.PreExposure, FontValue);
Newline(DebugContext);
Print(DebugContext, TEXT("NumLightsInGridCell: "));
Print(DebugContext, NumLightsInGridCell, FontValue);
Newline(DebugContext);
Print(DebugContext, TEXT("NumLocalLights : "));
Print(DebugContext, NumLocalLights, Select(NumLocalLights < MAX_LOCAL_LIGHT_INDEX + 1, FontLightGreen, FontRed));
Newline(DebugContext);
Print(DebugContext, TEXT("ValidGuideHistory : "));
Print(DebugContext, bHasValidHistory, FontValue);
Newline(DebugContext);
Print(DebugContext, TEXT("LightId | LocalLightId | Weight | LFAtlas | IESAtlas | History"), FontSilver);
}
#if GUIDE_BY_HISTORY
// Bilinear filtering approximation using stochastic offset
const float2 RandomVec2 = BlueNoiseVec2(DownsampledScreenCoord, MegaLightsStateFrameIndex);
PrevScreenCoord += (RandomVec2 - 0.5f) * VISIBLE_LIGHT_HASH_TILE_SIZE;
uint2 PrevScreenTileCoord = clamp(PrevScreenCoord / VISIBLE_LIGHT_HASH_TILE_SIZE, HistoryVisibleLightHashViewMinInTiles, HistoryVisibleLightHashViewMinInTiles + HistoryVisibleLightHashViewSizeInTiles - 1);
uint HistoryBufferBase = VISIBLE_LIGHT_HASH_SIZE * (PrevScreenTileCoord.y * HistoryVisibleLightHashViewSizeInTiles.x + PrevScreenTileCoord.x);
#endif
uint VisibleLightHash[VISIBLE_LIGHT_HASH_SIZE];
uint VisibleLightMaskHash[VISIBLE_LIGHT_HASH_SIZE];
for (uint IndexInHash = 0; IndexInHash < VISIBLE_LIGHT_HASH_SIZE; ++IndexInHash)
{
VisibleLightHash[IndexInHash] = 0xFFFFFFFF;
VisibleLightMaskHash[IndexInHash] = 0xFFFFFFFF;
#if GUIDE_BY_HISTORY
{
VisibleLightHash[IndexInHash] = VisibleLightHashHistory[HistoryBufferBase + IndexInHash];
VisibleLightMaskHash[IndexInHash] = VisibleLightMaskHashHistory[HistoryBufferBase + IndexInHash];
}
#endif
}
#if REFERENCE_MODE
FLightSampler LightSampler;
InitLightSamplerFromSequence(LightSampler, DownsampledScreenCoord, MegaLightsStateFrameIndex);
#else
const float RandomScalar = BlueNoiseScalar(DownsampledScreenCoord, MegaLightsStateFrameIndex);
FLightSampler LightSampler = InitLightSamplerStratified(RandomScalar);
#endif
const uint ScalarGridIndex = WaveReadLaneFirst(GridIndex);
const bool bScalarGridCell = WaveActiveAllTrue(ScalarGridIndex == GridIndex);
GridCellLightCount = NumLightsInGridCell;
if (bScalarGridCell)
{
FCulledLightsGridHeader CulledLightsGridHeader = GetCulledLightsGridHeader(ScalarGridIndex);
uint NumLightsInGridCell = min(CulledLightsGridHeader.NumMegaLights, GetMaxLightsPerCell());
uint GridLightIndex = 0;
while(GridLightIndex < NumLightsInGridCell)
{
uint LocalLightIndex = GetCulledLightDataGrid(CulledLightsGridHeader.MegaLightsDataStartIndex + GridLightIndex);
if (LocalLightIndex >= MAX_LOCAL_LIGHT_INDEX)
{
break;
}
++GridLightIndex;
++LightLoopIterationCount;
SampleLight(ScreenCoord, TranslatedWorldPosition, Material, LightingChannelMask, VisibleLightHash, bHasValidHistory, LocalLightIndex, LightSampler, DebugContext);
}
}
else
{
uint GridLightIndex = 0;
while(GridLightIndex < NumLightsInGridCell)
{
const uint VectorLocalLightIndex = GetCulledLightDataGrid(CulledLightsGridHeader.MegaLightsDataStartIndex + GridLightIndex);
if (VectorLocalLightIndex >= MAX_LOCAL_LIGHT_INDEX)
{
break;
}
++LightLoopIterationCount;
uint LocalLightIndex = WaveActiveMin(VectorLocalLightIndex);
if (LocalLightIndex == VectorLocalLightIndex)
{
++GridLightIndex;
SampleLight(ScreenCoord, TranslatedWorldPosition, Material, LightingChannelMask, VisibleLightHash, bHasValidHistory, LocalLightIndex, LightSampler, DebugContext);
}
}
}
// sample directional lights
for (uint Index = ForwardLightStruct.DirectionalMegaLightsSupportedStartIndex; Index < ForwardLightStruct.NumDirectionalLights; ++Index)
{
SampleDirectionalLight(Index, ScreenCoord, TranslatedWorldPosition, Material, LightingChannelMask, VisibleLightHash, bHasValidHistory, LightSampler, DebugContext);
}
if (DebugContext.bIsActive)
{
Newline(DebugContext);
Print(DebugContext, TEXT("Weight sum : "));
Print(DebugContext, LightSampler.WeightSum, FontValue);
}
if (DebugContext.bIsActive)
{
Newline(DebugContext);
Newline(DebugContext);
Print(DebugContext, TEXT("LightId | Weight | History | AsVis | Trace | UV"), FontSilver);
}
// Finalize samples
for (uint LightSampleIndex = 0; LightSampleIndex < NUM_SAMPLES_PER_PIXEL_1D; ++LightSampleIndex)
{
FCandidateLightSample CandidateLightSample = UnpackCandidateLightSample(LightSampler.PackedSamples[LightSampleIndex]);
FLightSample LightSample = InitLightSample();
FLightSampleRay LightSampleRay = InitLightSampleRay();
LightSample.bVisible = true;
LightSample.bGuidedAsVisible = true;
LightSample.LocalLightIndex = CandidateLightSample.LocalLightIndex;
LightSample.Weight = CandidateLightSample.Weight;
const FForwardLightData ForwardLightData = GetForwardLightData(LightSample.LocalLightIndex, 0);
FDeferredLightData LightData = ConvertToDeferredLight(ForwardLightData);
uint2 SampleCoord = GetSampleCoord(DownsampledScreenCoord, LightSampleIndex);
const bool bAreaLight = LightData.SourceRadius > 0.001f || LightData.SourceLength > 0.01f;
if (bAreaLight)
{
#if REFERENCE_MODE
FRandomSequence RandSequence = RandomSequenceCreate(uint3(SampleCoord, MegaLightsStateFrameIndex), LightSampleIndex, NUM_SAMPLES_1D);
// The Get1D() are to skip the first two samples used in the lights selection above, ensuring area light uv sampling is uncorrelated to it.
RandSequence.Get1D();
LightSampleRay.UV = RandSequence.Get2D();
#else
LightSampleRay.UV = BlueNoiseVec2(SampleCoord, MegaLightsStateFrameIndex);
#endif
}
LightSampleRay.bHair = Material.bIsHair;
LightSampleRay.bBackfaceDiffuse = Material.bHasBackfaceDiffuse;
#if GUIDE_BY_HISTORY
if (bHasValidHistory && ForwardLightData.PrevLocalLightIndex >= 0)
{
uint VisibleInLastFrameMask = GetLightVisibilityMask(VisibleLightMaskHash, ForwardLightData.PrevLocalLightIndex);
LightSample.bGuidedAsVisible = CandidateLightSample.bLightWasVisible;
if (AreaLightHiddenPDFWeight < 1)
{
// If entire light is fully visible or invisible then no need to guide samples towards the visible parts
if (CandidateLightSample.bLightWasVisible && VisibleInLastFrameMask != 0 && VisibleInLastFrameMask != 0xF && bAreaLight)
{
// Probabilities of hitting of 2x2 light UV regions based on the visibility from the previous frame
float Weight00 = VisibleInLastFrameMask & 0x1 ? 1.0f : AreaLightHiddenPDFWeight;
float Weight10 = VisibleInLastFrameMask & 0x2 ? 1.0f : AreaLightHiddenPDFWeight;
float Weight01 = VisibleInLastFrameMask & 0x4 ? 1.0f : AreaLightHiddenPDFWeight;
float Weight11 = VisibleInLastFrameMask & 0x8 ? 1.0f : AreaLightHiddenPDFWeight;
// Warp samples across X to fit desired distribution
float SplitX = (Weight00 + Weight01) / (Weight00 + Weight10 + Weight01 + Weight11);
if (LightSampleRay.UV.x < SplitX)
{
LightSampleRay.UV.x = LightSampleRay.UV.x / SplitX;
LightSampleRay.UV.x *= 0.5f;
}
else
{
LightSampleRay.UV.x = (LightSampleRay.UV.x - SplitX) / (1.0f - SplitX);
LightSampleRay.UV.x = LightSampleRay.UV.x * 0.5f + 0.5f;
}
// Warp samples across Y to fit desired distribution
float SplitY = LightSampleRay.UV.x < 0.5f ? (Weight00 / (Weight00 + Weight01)) : (Weight10 / (Weight10 + Weight11));
if (LightSampleRay.UV.y < SplitY)
{
LightSampleRay.UV.y = LightSampleRay.UV.y / SplitY;
LightSampleRay.UV.y *= 0.5f;
}
else
{
LightSampleRay.UV.y = (LightSampleRay.UV.y - SplitY) / (1.0f - SplitY);
LightSampleRay.UV.y = LightSampleRay.UV.y * 0.5f + 0.5f;
}
float SelectedWeight;
if (LightSampleRay.UV.x < 0.5f)
{
SelectedWeight = LightSampleRay.UV.y < 0.5f ? Weight00 : Weight01;
}
else
{
SelectedWeight = LightSampleRay.UV.y < 0.5f ? Weight10 : Weight11;
}
// Fixup weights to match new sample distribution
float PerLightWeightSum = Weight00 + Weight10 + Weight01 + Weight11;
LightSample.bGuidedAsVisible = SelectedWeight >= PerLightWeightSum / 4.0f;
LightSample.Weight = LightSample.Weight * SelectedWeight * 4.0f / PerLightWeightSum;
}
}
}
#endif
if (LightSample.LocalLightIndex != MAX_LOCAL_LIGHT_INDEX)
{
const bool bCastShadows = UnpackCastShadow(ForwardLightData);
LightSampleRay.bCompleted = bCastShadows ? false : true;
LightSample.Weight = LightSampler.WeightSum / LightSample.Weight;
}
// Skip identical rays
if (!bAreaLight)
{
// Only check next sample for performance reasons
// Sometimes due to mixing hidden samples this may not merge all rays, but it should handle almost all of them
if (LightSampleIndex + 1 < NUM_SAMPLES_PER_PIXEL_1D)
{
FCandidateLightSample NextCandidateLightSample = UnpackCandidateLightSample(LightSampler.PackedSamples[LightSampleIndex + 1]);
if (NextCandidateLightSample.LocalLightIndex == CandidateLightSample.LocalLightIndex)
{
LightSample.Weight = 0.0f;
LightSample.bVisible = false;
LightSampleRay.bCompleted = true;
}
}
}
if (DebugContext.bIsActive)
{
Newline(DebugContext);
Print(DebugContext, ForwardLightData.LightSceneId, Select(ForwardLightData.LightSceneId == DebugLightId, FontSelected, FontValue));
Print(DebugContext, CandidateLightSample.Weight, FontValue);
const uint VisibleInLastFrameMask = GetLightVisibilityMask(VisibleLightMaskHash, ForwardLightData.PrevLocalLightIndex);
uint VisibleInLastFrameMaskDebug = 0;
VisibleInLastFrameMaskDebug += VisibleInLastFrameMask & 0x1 ? 1000 : 0;
VisibleInLastFrameMaskDebug += VisibleInLastFrameMask & 0x2 ? 100 : 0;
VisibleInLastFrameMaskDebug += VisibleInLastFrameMask & 0x4 ? 10 : 0;
VisibleInLastFrameMaskDebug += VisibleInLastFrameMask & 0x8 ? 1 : 0;
Print(DebugContext, VisibleInLastFrameMaskDebug, FontValue, 10, 0);
Print(DebugContext, LightSample.bGuidedAsVisible ? 1u : 0u, FontValue, 8, 0);
Print(DebugContext, LightSampleRay.bCompleted ? 0u : 1u, FontValue, 8, 0);
Print(DebugContext, LightSampleRay.UV, Select(bAreaLight, FontValue, FontSilver), 8, 3);
if (DebugMode == DEBUG_MODE_VISUALIZE_SAMPLING)
{
const uint2 SampleCoord = GetSampleCoord(DownsampledScreenCoord, LightSampleIndex);
const FLightSampleTrace LightSampleTrace = GetLightSampleTrace(TranslatedWorldPosition, LightSample.LocalLightIndex, LightSampleRay.UV);
float4 RayColor = float4(LightData.Color.xyz / Luminance(LightData.Color.xyz), 1.0f);
AddLineTWS(DebugContext, TranslatedWorldPosition, TranslatedWorldPosition + LightSampleTrace.Direction * LightSampleTrace.Distance, RayColor);
}
}
RWLightSamples[SampleCoord] = PackLightSample(LightSample);
RWLightSampleRays[SampleCoord] = PackLightSampleRay(LightSampleRay);
}
}
else
{
for (uint LightSampleIndex = 0; LightSampleIndex < NUM_SAMPLES_PER_PIXEL_1D; ++LightSampleIndex)
{
RWLightSamples[GetSampleCoord(DownsampledScreenCoord, LightSampleIndex)] = PackLightSample(InitLightSample());
RWLightSampleRays[GetSampleCoord(DownsampledScreenCoord, LightSampleIndex)] = PackLightSampleRay(InitLightSampleRay());
}
}
AddTextBackground(DebugContext, FontBackground);
RWDownsampledSceneDepth[DownsampledScreenCoord] = SceneDepth;
RWDownsampledSceneWorldNormal[DownsampledScreenCoord] = EncodeNormal(Material.WorldNormalForPositionBias);
}
#if DEBUG_MODE
if (bVisualizeLightLoopIterations != 0)
{
GridCellLightCount = WaveActiveMax(GridCellLightCount);
LightLoopIterationCount = WaveActiveMax(LightLoopIterationCount);
if (WaveGetLaneCount() < THREADGROUP_SIZE * THREADGROUP_SIZE)
{
uint GroupThreadIndex = GroupThreadId.y * THREADGROUP_SIZE + GroupThreadId.x;
if (GroupThreadIndex % 32u == 0)
{
SharedGridCellLightCount[GroupThreadIndex / 32u] = GridCellLightCount;
SharedLightLoopIterationCount[GroupThreadIndex / 32u] = LightLoopIterationCount;
}
GroupMemoryBarrierWithGroupSync();
GridCellLightCount = max(SharedGridCellLightCount[0], SharedGridCellLightCount[1]);
LightLoopIterationCount = max(SharedLightLoopIterationCount[0], SharedLightLoopIterationCount[1]);
}
}
if (DebugContext_VisIterations.bIsActive)
{
float HueRed = LinearRGB_2_HSV(ColorRed.xyz).x;
float HueGreen = LinearRGB_2_HSV(ColorGreen.xyz).x;
float HueBlue = LinearRGB_2_HSV(ColorBlue.xyz).x;
float QuadHue = HueBlue;
if (GridCellLightCount > 0)
{
if (2 * LightLoopIterationCount <= 3 * GridCellLightCount)
{
// blue -> green
QuadHue = lerp(HueBlue, HueGreen, saturate(float(LightLoopIterationCount - GridCellLightCount) * 2 / GridCellLightCount));
}
else
{
// green -> red
QuadHue = lerp(HueGreen, HueRed, saturate(float(2 * LightLoopIterationCount - 3 * GridCellLightCount) / GridCellLightCount));
}
}
float4 QuadColor = float4(HSV_2_LinearRGB(float3(QuadHue, 1, 1)), 0.5);
uint2 TileSize = uint2(TILE_SIZE, TILE_SIZE) / GetDownsampleFactor();
AddFilledQuadSS(
DebugContext_VisIterations,
((DownsampledTileCoord + 0) * TileSize.x) / View.ViewResolutionFraction,
((DownsampledTileCoord + 1) * TileSize.y) / View.ViewResolutionFraction,
QuadColor);
AddQuadSS(
DebugContext_VisIterations,
((DownsampledTileCoord + 0) * TileSize.x) / View.ViewResolutionFraction,
((DownsampledTileCoord + 1) * TileSize.y) / View.ViewResolutionFraction,
ColorWhite);
}
#endif // DEBUG_MODE
}
}
/**
* Clear data for empty tiles, which won't be processed by GenerateLightSamplesCS
*/
[numthreads(THREADGROUP_SIZE, THREADGROUP_SIZE, 1)]
void ClearLightSamplesCS(
uint3 GroupId : SV_GroupID,
uint3 GroupThreadId : SV_GroupThreadID,
uint3 DispatchThreadId : SV_DispatchThreadID)
{
uint DownsampledTileIndex = GroupId.x;
if (DownsampledTileIndex < DownsampledTileAllocator[TILE_MODE_EMPTY])
{
uint2 DownsampledTileCoord = UnpackTile(DownsampledTileData[DownsampledTileIndex + TILE_MODE_EMPTY * DownsampledTileDataStride]);
uint2 DownsampledScreenCoord = DownsampledTileCoord * TILE_SIZE + GroupThreadId.xy + DownsampledViewMin;
if (all(DownsampledScreenCoord < DownsampledViewMin + DownsampledViewSize))
{
for (uint LightSampleY = 0; LightSampleY < NumSamplesPerPixel.y; ++LightSampleY)
{
for (uint LightSampleX = 0; LightSampleX < NumSamplesPerPixel.x; ++LightSampleX)
{
const uint2 SampleCoord = GetSampleCoord(DownsampledScreenCoord, LightSampleX, LightSampleY);
RWLightSamples[SampleCoord] = PackLightSample(InitLightSample());
RWLightSampleRays[SampleCoord] = PackLightSampleRay(InitLightSampleRay());
}
}
}
}
}