Files
UnrealEngine/Engine/Shaders/Private/Lumen/LumenReflectionDenoiserSpatial.usf
2025-05-18 13:04:45 +08:00

195 lines
8.2 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#include "../Common.ush"
#include "../SceneTextureParameters.ush"
#include "LumenReflectionCommon.ush"
#include "LumenReflectionDenoiserCommon.ush"
#if DEBUG_MODE
#include "../ShaderPrint.ush"
#endif
Texture2DArray<float4> SpecularLightingAndSecondMomentTexture;
Texture2DArray<UNORM float> NumFramesAccumulatedTexture;
RWTexture2DArray<float3> RWSpecularIndirectAccumulated;
Texture2D<float3> BackgroundVisibilityTexture;
RWTexture2D<float4> RWTranslucencyLighting;
uint ClosureIndex;
float SpatialFilterDepthWeightScale;
float SpatialFilterKernelRadius;
uint SpatialFilterNumSamples;
float TemporalMaxFramesAccumulated;
uint bCompositeSceneColor;
float3 TonemapLighting(float3 Lighting, float DisocclusionFactor)
{
// Run heavy tonemapping for DisocclusionFactor in order to suppress fireflies in new areas
return Lighting / (1.0f + DisocclusionFactor * Luminance(Lighting));
}
float3 InverseTonemapLighting(float3 TonemappedLighting, float DisocclusionFactor)
{
return TonemappedLighting / (1.0f - DisocclusionFactor * Luminance(TonemappedLighting));
}
// Workaround for Substrate fetching invalid value at tile border
bool IsLightingValidForDenoiser(float3 In)
{
#if SUBTRATE_GBUFFER_FORMAT==1
return all(In < INVALID_LIGHTING);
#else
return IsLightingValid(In);
#endif
}
/**
* Run a spatial filter in order to filter out noise based on the temporal variance.
*/
[numthreads(REFLECTION_THREADGROUP_SIZE_2D, REFLECTION_THREADGROUP_SIZE_2D, 1)]
void LumenReflectionDenoiserSpatialCS(
uint GroupId : SV_GroupID,
uint2 GroupThreadId : SV_GroupThreadID)
{
FReflectionTileData TileData;
const uint2 ReflectionScreenCoord = GetReflectionResolveScreenCoord(GroupId, GroupThreadId, TileData);
const FLumenMaterialCoord Coord = GetLumenMaterialCoord_Reflection(ReflectionScreenCoord, TileData, ClosureIndex);
#if DEBUG_MODE
int2 DebugScreenCoord = View.CursorPosition.x >= 0 ? View.CursorPosition * View.ViewResolutionFraction : -1;
bool bDebug = all(Coord.SvPosition == DebugScreenCoord);
FShaderPrintContext Context = InitShaderPrintContext(true, float2(0.55, 0.7));
#endif
float4 CenterSpecularLightingAndSecondMoment = RGBAToDenoiserSpace(SpecularLightingAndSecondMomentTexture[Coord.SvPositionFlatten]);
if (IsLightingValidForDenoiser(CenterSpecularLightingAndSecondMoment.xyz))
{
float CenterSpecularStdDev = sqrt(max(CenterSpecularLightingAndSecondMoment.w - Pow2(Luminance(CenterSpecularLightingAndSecondMoment.xyz)), 0.0f));
const FLumenMaterialData Material = ApplySmoothBias(ReadMaterialData(Coord, MaxRoughnessToTrace, false/*bAllowStochaticMaterial*/), true /*bTopLayerRoughness*/);
float DisocclusionFactor = 0.0f;
// Run relaxed spatial filter until we accumulate enough frames to rely on temporal variance
float NumFramesAccumulated = UnpackNumFramesAccumulated(NumFramesAccumulatedTexture[Coord.SvPositionFlatten]);
const uint MaxFramesAccumulated = GetMaxFramesAccumulated(TemporalMaxFramesAccumulated, Material.TopLayerRoughness);
if (MaxFramesAccumulated > 1)
{
//DisocclusionFactor = 1.0f - saturate((NumFramesAccumulated - 1.0f) / min(4.0f, MaxFramesAccumulated - 1.0f));
DisocclusionFactor = NumFramesAccumulated > 1 ? 0.0f : 1.0f;
}
float3 SpecularLightingSum = TonemapLighting(CenterSpecularLightingAndSecondMoment.xyz, DisocclusionFactor);
float SpecularLightingWeightSum = 1.0f;
float KernelRadiusInPixels = SpatialFilterKernelRadius * saturate(Material.TopLayerRoughness * 8.0f);
uint NumSamples = SpatialFilterNumSamples;
uint2 RandomSeed = Rand3DPCG16(int3(Coord.SvPosition, ReflectionsStateFrameIndexMod8)).xy;
float2 ScreenUV = (Coord.SvPosition + 0.5f) * View.BufferSizeAndInvSize.zw;
float3 TranslatedWorldPosition = GetTranslatedWorldPositionFromScreenUV(ScreenUV, Material.SceneDepth);
float4 ScenePlane = float4(Material.WorldNormal, dot(TranslatedWorldPosition, Material.WorldNormal));
bool bFilterSpecular = CenterSpecularStdDev / max(Luminance(CenterSpecularLightingAndSecondMoment.xyz), 0.1f) > 0.5f || DisocclusionFactor > 0.0f;
#if SPATIAL_FILTER
if (KernelRadiusInPixels > 1.0f && bFilterSpecular)
{
const float LobeHalfAngle = GetSpecularLobeHalfAngle(Material.TopLayerRoughness) * lerp(1.0f, 2.0f, DisocclusionFactor);
for (uint NeighborIndex = 0; NeighborIndex < NumSamples; ++NeighborIndex)
{
float2 NeighborOffsetInRect = Hammersley16(NeighborIndex, NumSamples, RandomSeed);
float2 NeighborOffset = UniformSampleDiskConcentric(NeighborOffsetInRect) * KernelRadiusInPixels;
int2 NeighborCoord = (int2)(Coord.SvPosition + NeighborOffset);
if (all(and(NeighborCoord >= View.ViewRectMinAndSize.xy, NeighborCoord < (View.ViewRectMinAndSize.xy + View.ViewRectMinAndSize.zw))))
{
const float2 NeighborScreenUV = (NeighborCoord + 0.5f) * View.BufferSizeAndInvSize.zw;
const FLumenMaterialCoord NeighborMaterialCoord = GetLumenMaterialCoord(NeighborCoord, Coord.ClosureIndex);
const FLumenMaterialData NeighborMaterial = ApplySmoothBias(ReadMaterialData(NeighborMaterialCoord, MaxRoughnessToTrace, false/*bAllowStochaticMaterial*/), true /*bTopLayerRoughness*/);
// Depth weight
const float3 NeighborTranslatedWorldPosition = GetTranslatedWorldPositionFromScreenUV(NeighborScreenUV, NeighborMaterial.SceneDepth);
float PlaneDistance = abs(dot(float4(NeighborTranslatedWorldPosition, -1), ScenePlane));
float RelativeDepthDifference = PlaneDistance / Material.SceneDepth;
float DepthWeight = exp2(-SpatialFilterDepthWeightScale * (RelativeDepthDifference * RelativeDepthDifference));
// Normal weight
float AngleBetweenNormals = acosFast(saturate(dot(ScenePlane.xyz, NeighborMaterial.WorldNormal)));
float NormalWeight = 1.0f - saturate(AngleBetweenNormals / (LobeHalfAngle + 0.01f));
float3 NeighborSpecularLighting = RGBToDenoiserSpace(SpecularLightingAndSecondMomentTexture[uint3(NeighborCoord, Coord.SvPositionFlatten.z)].xyz);
if (IsLightingValidForDenoiser(NeighborSpecularLighting))
{
float LuminanceDelta = abs(Luminance(CenterSpecularLightingAndSecondMoment.xyz) - Luminance(NeighborSpecularLighting));
float LuminanceWeight = exp2(-LuminanceDelta / max(CenterSpecularStdDev, 0.001f));
LuminanceWeight = lerp(LuminanceWeight, 1.0f, DisocclusionFactor);
const float SpecularNeighborWeight = DepthWeight * NormalWeight * LuminanceWeight;
SpecularLightingSum += TonemapLighting(NeighborSpecularLighting, DisocclusionFactor) * SpecularNeighborWeight;
SpecularLightingWeightSum += SpecularNeighborWeight;
}
}
}
}
#endif
float3 SpecularLighting = InverseTonemapLighting(SpecularLightingSum / SpecularLightingWeightSum, DisocclusionFactor);
#if DEBUG_MODE
if (bDebug)
{
Print(Context, TEXT("Spatial"));
Newline(Context);
Print(Context, TEXT("KernelRadiusInPixels : "));
Print(Context, KernelRadiusInPixels);
Newline(Context);
Print(Context, TEXT("CenterSpecularStdDev : "));
Print(Context, CenterSpecularStdDev);
Newline(Context);
Print(Context, TEXT("DisocclusionFactor : "));
Print(Context, DisocclusionFactor);
Newline(Context);
Print(Context, SpecularLighting);
Newline(Context);
Print(Context, TEXT("Filter : "));
Print(Context, bFilterSpecular ? 1u : 0u);
}
#endif
float4 FinalLighting = float4(DenoiserSpaceToRGB(SpecularLighting), 1.0f);
#if RAY_TRACED_TRANSLUCENCY
if (bCompositeSceneColor != 0)
{
float3 BackgroundVisibility = BackgroundVisibilityTexture.Load(int3(Coord.SvPosition, 0));
float4 SceneColorSample = SceneTexturesStruct.SceneColorTexture.Load(int3(Coord.SvPosition, 0));
FinalLighting.rgb = SceneColorSample.rgb * BackgroundVisibility + FinalLighting.rgb;
FinalLighting.a = SceneColorSample.a * dot(BackgroundVisibility, 1.0f / 3.0f);
RWTranslucencyLighting[Coord.SvPosition] = FinalLighting;
}
else
{
RWTranslucencyLighting[Coord.SvPosition] = FinalLighting;
}
#else
RWSpecularIndirectAccumulated[Coord.SvPositionFlatten] = FinalLighting.xyz;
#endif
}
#if RAY_TRACED_TRANSLUCENCY
else
{
float4 ClearValue = float4(0, 0, 0, 1);
if (bCompositeSceneColor != 0)
{
ClearValue = SceneTexturesStruct.SceneColorTexture.Load(int3(Coord.SvPosition, 0));
}
RWTranslucencyLighting[Coord.SvPosition] = ClearValue;
}
#endif
}