// 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 RWDownsampledSceneDepth; RWTexture2D RWDownsampledSceneWorldNormal; RWTexture2D RWLightSamples; RWTexture2D RWLightSampleRays; Texture2D MegaLightsDepthHistory; float4 HistoryScreenPositionScaleBias; float4 HistoryUVMinMax; float4 HistoryGatherUVMinMax; float4 HistoryBufferSizeAndInvSize; StructuredBuffer DownsampledTileAllocator; StructuredBuffer DownsampledTileData; StructuredBuffer VisibleLightHashHistory; StructuredBuffer 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()); } } } } }