675 lines
27 KiB
HLSL
675 lines
27 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"
|
|
|
|
// 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
|
|
if (Material.bIsHair)
|
|
{
|
|
LightData.HairTransmittance = EvaluateDualScattering(Material.GBuffer, 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 DebugMode;
|
|
uint DebugVisualizeLight;
|
|
uint DebugLightId;
|
|
|
|
RWTexture2D<float> RWDownsampledSceneDepth;
|
|
RWTexture2D<UNORM float3> RWDownsampledSceneWorldNormal;
|
|
RWTexture2D<uint> RWLightSamples;
|
|
RWTexture2D<uint> RWLightSampleRays;
|
|
|
|
Texture2D MegaLightsDepthHistory;
|
|
float4 HistoryScreenPositionScaleBias;
|
|
float4 HistoryUVMinMax;
|
|
float4 HistoryGatherUVMinMax;
|
|
float4 HistoryBufferSizeAndInvSize;
|
|
|
|
StructuredBuffer<uint> DownsampledTileAllocator;
|
|
StructuredBuffer<uint> DownsampledTileData;
|
|
|
|
StructuredBuffer<uint> VisibleLightHashHistory;
|
|
StructuredBuffer<uint> VisibleLightMaskHashHistory;
|
|
uint2 HistoryVisibleLightHashViewMinInTiles;
|
|
uint2 HistoryVisibleLightHashViewSizeInTiles;
|
|
uint GuideByHistoryMode;
|
|
float GuideByHistoryHiddenRatio;
|
|
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 VisibleLightSampler,
|
|
inout FLightSampler HiddenLightSampler,
|
|
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 = false;
|
|
|
|
#if GUIDE_BY_HISTORY
|
|
if (LightTargetPDF.Weight > MinSampleWeight && PrevForwardLightIndex >= 0)
|
|
{
|
|
bWasVisibleInLastFrame = GetLightVisibility(VisibleLightHash, PrevForwardLightIndex);
|
|
}
|
|
#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));
|
|
|
|
if ((DebugVisualizeLight != 0 && LightSceneId == DebugLightId) || DebugVisualizeLight == 2)
|
|
{
|
|
AddSphereTWS(DebugContext, LightData.TranslatedWorldPosition, 10.0f, float4(0, 1, 0, 1));
|
|
AddLineTWS(DebugContext, TranslatedWorldPosition, LightData.TranslatedWorldPosition, float4(0, 1, 0, 1));
|
|
}
|
|
}
|
|
|
|
if (LightTargetPDF.Weight > MinSampleWeight)
|
|
{
|
|
if (bWasVisibleInLastFrame)
|
|
{
|
|
AddLightSample(VisibleLightSampler, LightTargetPDF.Weight, ForwardLightIndex, bWasVisibleInLastFrame);
|
|
}
|
|
else
|
|
{
|
|
AddLightSample(HiddenLightSampler, LightTargetPDF.Weight, ForwardLightIndex, bWasVisibleInLastFrame);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SampleLight(
|
|
uint2 ScreenCoord,
|
|
float3 TranslatedWorldPosition,
|
|
const FMegaLightsMaterial Material,
|
|
uint LightingChannelMask,
|
|
uint VisibleLightHash[VISIBLE_LIGHT_HASH_SIZE],
|
|
bool bHasValidHistory,
|
|
uint LocalLightIndex,
|
|
inout FLightSampler VisibleLightSampler,
|
|
inout FLightSampler HiddenLightSampler,
|
|
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,
|
|
VisibleLightSampler, HiddenLightSampler, DebugContext);
|
|
}
|
|
|
|
void SampleDirectionalLight(
|
|
uint DirectionalLightIndex,
|
|
uint2 ScreenCoord,
|
|
float3 TranslatedWorldPosition,
|
|
const FMegaLightsMaterial Material,
|
|
uint LightingChannelMask,
|
|
uint VisibleLightHash[VISIBLE_LIGHT_HASH_SIZE],
|
|
bool bHasValidHistory,
|
|
inout FLightSampler VisibleLightSampler,
|
|
inout FLightSampler HiddenLightSampler,
|
|
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,
|
|
VisibleLightSampler, HiddenLightSampler, DebugContext);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
const bool bForceSimpleShading = IsSimpleShadingTileType(TILE_TYPE);
|
|
|
|
if (all(DownsampledScreenCoord < DownsampledViewMin + DownsampledViewSize))
|
|
{
|
|
uint2 ScreenCoord = DownsampledScreenCoordToScreenCoord(DownsampledScreenCoord);
|
|
FShaderPrintContext DebugContext = InitDebugContext(DownsampledScreenCoord, /*bDownsampled*/ true, float2(0.05, 0.05));
|
|
|
|
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
|
|
{
|
|
bHasValidHistory = false;
|
|
|
|
float2 ScreenPosition = (ScreenUV - View.ScreenPositionScaleBias.wz) / View.ScreenPositionScaleBias.xy;
|
|
float3 HistoryScreenPosition = GetHistoryScreenPosition(ScreenPosition, ScreenUV, ConvertToDeviceZ(SceneDepth));
|
|
float2 HistoryScreenUV = HistoryScreenPosition.xy * HistoryScreenPositionScaleBias.xy + HistoryScreenPositionScaleBias.wz;
|
|
|
|
bool bHistoryWasOnScreen = all(HistoryScreenUV >= HistoryUVMinMax.xy) && all(HistoryScreenUV <= HistoryUVMinMax.zw);
|
|
|
|
HistoryScreenUV = clamp(HistoryScreenUV, HistoryGatherUVMinMax.xy, HistoryGatherUVMinMax.zw);
|
|
|
|
uint2 HistoryScreenCoord = floor(HistoryScreenUV * HistoryBufferSizeAndInvSize.xy - 0.5f);
|
|
float2 HistoryBilinearWeights = frac(HistoryScreenUV * HistoryBufferSizeAndInvSize.xy - 0.5f);
|
|
float2 HistoryGatherUV = (HistoryScreenCoord + 1.0f) * HistoryBufferSizeAndInvSize.zw;
|
|
|
|
float4 HistorySampleSceneDepth4 = MegaLightsDepthHistory.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);
|
|
float DisocclusionDistanceThreshold = 0.03f;
|
|
const float4 DistanceToHistoryValue = abs(HistorySampleSceneDepth4 - ReprojectedSceneDepth);
|
|
float4 DepthWeights = select(DistanceToHistoryValue >= ReprojectedSceneDepth * DisocclusionDistanceThreshold, 0.0f, 1.0f);
|
|
|
|
if (any(DepthWeights > 0.01f) && bHistoryWasOnScreen)
|
|
{
|
|
bHasValidHistory = true;
|
|
}
|
|
|
|
PrevScreenCoord = HistoryScreenCoord;
|
|
}
|
|
#endif
|
|
|
|
if (DebugContext.bIsActive)
|
|
{
|
|
#if SUBSTRATE_ENABLED
|
|
const uint MaxTileType = TILE_MODE_MAX;
|
|
#else
|
|
const uint MaxTileType = TILE_MODE_MAX_LEGACY;
|
|
#endif
|
|
|
|
Print(DebugContext, TEXT("MegaLights"), FontTitle);
|
|
Newline(DebugContext);
|
|
Print(DebugContext, TEXT("TileType | Num "), FontSilver);
|
|
for (uint TileTypeIndex = 0; TileTypeIndex < MaxTileType; ++TileTypeIndex)
|
|
{
|
|
Newline(DebugContext);
|
|
PrintTileTypeString(DebugContext, TileTypeIndex, FontValue);
|
|
Print(DebugContext, TEXT(": "));
|
|
Print(DebugContext, DownsampledTileAllocator[TileTypeIndex], FontValue);
|
|
}
|
|
Newline(DebugContext);
|
|
Newline(DebugContext);
|
|
|
|
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);
|
|
}
|
|
|
|
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
|
|
{
|
|
const uint2 PrevScreenTileCoord = clamp(PrevScreenCoord / TILE_SIZE, HistoryVisibleLightHashViewMinInTiles, HistoryVisibleLightHashViewMinInTiles + HistoryVisibleLightHashViewSizeInTiles - 1);
|
|
const uint HistoryBufferBase = VISIBLE_LIGHT_HASH_SIZE * (PrevScreenTileCoord.y * HistoryVisibleLightHashViewSizeInTiles.x + PrevScreenTileCoord.x);
|
|
VisibleLightHash[IndexInHash] = VisibleLightHashHistory[HistoryBufferBase + IndexInHash];
|
|
VisibleLightMaskHash[IndexInHash] = VisibleLightMaskHashHistory[HistoryBufferBase + IndexInHash];
|
|
}
|
|
#endif
|
|
}
|
|
|
|
const float RandomScalar = BlueNoiseScalar(DownsampledScreenCoord, MegaLightsStateFrameIndex);
|
|
FLightSampler LightSampler = InitLightSampler(RandomScalar);
|
|
FLightSampler HiddenLightSampler = InitLightSampler(1.0f - RandomScalar);
|
|
|
|
const uint ScalarGridIndex = WaveReadLaneFirst(GridIndex);
|
|
const bool bScalarGridCell = WaveActiveAllTrue(ScalarGridIndex == GridIndex);
|
|
|
|
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;
|
|
SampleLight(ScreenCoord, TranslatedWorldPosition, Material, LightingChannelMask, VisibleLightHash, bHasValidHistory, LocalLightIndex, LightSampler, HiddenLightSampler, DebugContext);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uint GridLightIndex = 0;
|
|
while(GridLightIndex < NumLightsInGridCell)
|
|
{
|
|
const uint VectorLocalLightIndex = GetCulledLightDataGrid(CulledLightsGridHeader.MegaLightsDataStartIndex + GridLightIndex);
|
|
if (VectorLocalLightIndex >= MAX_LOCAL_LIGHT_INDEX)
|
|
{
|
|
break;
|
|
}
|
|
|
|
uint LocalLightIndex = WaveActiveMin(VectorLocalLightIndex);
|
|
if (LocalLightIndex == VectorLocalLightIndex)
|
|
{
|
|
++GridLightIndex;
|
|
SampleLight(ScreenCoord, TranslatedWorldPosition, Material, LightingChannelMask, VisibleLightHash, bHasValidHistory, LocalLightIndex, LightSampler, HiddenLightSampler, DebugContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
// sample directional lights
|
|
for (uint Index = ForwardLightStruct.DirectionalMegaLightsSupportedStartIndex; Index < ForwardLightStruct.NumDirectionalLights; ++Index)
|
|
{
|
|
SampleDirectionalLight(Index, ScreenCoord, TranslatedWorldPosition, Material, LightingChannelMask, VisibleLightHash, bHasValidHistory, LightSampler, HiddenLightSampler, DebugContext);
|
|
}
|
|
|
|
if (DebugContext.bIsActive)
|
|
{
|
|
Newline(DebugContext);
|
|
Print(DebugContext, TEXT("Visible weight sum : "));
|
|
Print(DebugContext, LightSampler.WeightSum, FontValue);
|
|
Newline(DebugContext);
|
|
Print(DebugContext, TEXT("Hidden weight sum : "));
|
|
Print(DebugContext, HiddenLightSampler.WeightSum, FontValue);
|
|
}
|
|
|
|
CombineLightSamplers(LightSampler, HiddenLightSampler, bHasValidHistory, GuideByHistoryHiddenRatio, DebugContext);
|
|
|
|
if (DebugContext.bIsActive)
|
|
{
|
|
Newline(DebugContext);
|
|
Newline(DebugContext);
|
|
Print(DebugContext, TEXT("LightId | Weight | History | AsVis | 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);
|
|
LightSampleRay.UV = BlueNoiseVec2(SampleCoord, MegaLightsStateFrameIndex);
|
|
|
|
// Disable screen tracing on hair material as it is very noisy and can cause light leak when light and view are opposed.
|
|
LightSampleRay.bSupportScreenTrace = !Material.bIsHair;
|
|
|
|
// Subsurface and two-sided need special treatment as they need lighting from behind the object too
|
|
if (Material.bHasBackfaceDiffuse)
|
|
{
|
|
LightSampleRay.bBackfaceDiffuse = true;
|
|
}
|
|
|
|
#if GUIDE_BY_HISTORY
|
|
if (bHasValidHistory && ForwardLightData.PrevLocalLightIndex >= 0)
|
|
{
|
|
uint VisibleInLastFrameMask = GetLightVisibilityMask(VisibleLightMaskHash, ForwardLightData.PrevLocalLightIndex);
|
|
LightSample.bGuidedAsVisible = CandidateLightSample.bLightWasVisible;
|
|
|
|
if (GuideByHistoryMode == 2)
|
|
{
|
|
// If entire light is fully visible or invisible then no need to guide samples towards the visible parts
|
|
const bool bAreaLight = LightData.SourceRadius > 0.0f || LightData.SourceLength > 0.0f;
|
|
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 (DebugContext.bIsActive)
|
|
{
|
|
Newline(DebugContext);
|
|
Print(DebugContext, ForwardLightData.LightSceneId, Select(ForwardLightData.LightSceneId == DebugLightId, FontSelected, FontValue));
|
|
Print(DebugContext, LightSample.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);
|
|
Print(DebugContext, LightSample.bGuidedAsVisible ? 1u : 0u, FontValue);
|
|
Print(DebugContext, LightSampleRay.UV, FontValue);
|
|
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);
|
|
}
|
|
}
|
|
|
|
if (LightSample.LocalLightIndex != MAX_LOCAL_LIGHT_INDEX)
|
|
{
|
|
const bool bCastShadows = UnpackCastShadow(ForwardLightData);
|
|
LightSampleRay.bCompleted = bCastShadows ? false : true;
|
|
LightSample.Weight = LightSampler.WeightSum / (NUM_SAMPLES_PER_PIXEL_1D * LightSample.Weight);
|
|
}
|
|
|
|
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());
|
|
}
|
|
}
|
|
|
|
RWDownsampledSceneDepth[DownsampledScreenCoord] = SceneDepth;
|
|
RWDownsampledSceneWorldNormal[DownsampledScreenCoord] = EncodeNormal(Material.WorldNormalForPositionBias);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
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());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |