// Copyright Epic Games, Inc. All Rights Reserved. #include "LumenScreenProbeGather.h" #include "RendererPrivate.h" #include "ScenePrivate.h" #include "SceneUtils.h" #include "PipelineStateCache.h" #include "ShaderParameterStruct.h" #include "PixelShaderUtils.h" #include "LumenShortRangeAO.h" static TAutoConsoleVariable CVarLumenShortRangeAODownsampleFactor( TEXT("r.Lumen.ScreenProbeGather.ShortRangeAO.DownsampleFactor"), 2, TEXT("Downsampling factor for ShortRangeAO."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarLumenShortRangeAOTemporal( TEXT("r.Lumen.ScreenProbeGather.ShortRangeAO.Temporal"), 1, TEXT("Whether to run temporal accumulation on Short Range AO"), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarLumenShortRangeAOBentNormal( TEXT("r.Lumen.ScreenProbeGather.ShortRangeAO.BentNormal"), 1, TEXT("Whether to use bent normal or just scalar AO. Scalar AO is slightly faster, but bent normal improves specular occlusion."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarLumenShortRangeAOTemporalNeighborhoodClampScale( TEXT("r.Lumen.ScreenProbeGather.ShortRangeAO.Temporal.NeighborhoodClampScale"), 1.0f, TEXT("Scales how permissive is neighborhood clamp. Higher values increase ghosting, but reduce noise and instability. Values <= 0 will disable neighborhood clamp."), ECVF_Scalability | ECVF_RenderThreadSafe ); float GLumenShortRangeAOSlopeCompareToleranceScale = .5f; FAutoConsoleVariableRef CVarLumenShortRangeAOSlopeCompareToleranceScale( TEXT("r.Lumen.ScreenProbeGather.ShortRangeAO.ScreenSpace.SlopeCompareToleranceScale"), GLumenShortRangeAOSlopeCompareToleranceScale, TEXT("Scales the slope threshold that screen space traces use to determine whether there was a hit."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarLumenShortRangeAOFoliageOcclusionStrength( TEXT("r.Lumen.ScreenProbeGather.ShortRangeAO.ScreenSpace.FoliageOcclusionStrength"), 0.7f, TEXT("Maximum strength of ScreenSpaceBentNormal occlusion on foliage and subsurface pixels. Useful for reducing max occlusion to simulate subsurface scattering."), ECVF_Scalability | ECVF_RenderThreadSafe ); float GLumenMaxShortRangeAOMultibounceAlbedo = .5f; FAutoConsoleVariableRef CVarLumenMaxShortRangeAOMultibounceAlbedo( TEXT("r.Lumen.ScreenProbeGather.ShortRangeAO.MaxMultibounceAlbedo"), GLumenMaxShortRangeAOMultibounceAlbedo, TEXT("Maximum albedo used for the AO multi-bounce approximation. Useful for forcing near-white albedo to have some occlusion."), ECVF_Scalability | ECVF_RenderThreadSafe ); int32 GLumenShortRangeAOHairStrandsVoxelTrace = 1; FAutoConsoleVariableRef GVarLumenShortRangeAOHairStrandsVoxelTrace( TEXT("r.Lumen.ScreenProbeGather.ShortRangeAO.HairVoxelTrace"), GLumenShortRangeAOHairStrandsVoxelTrace, TEXT("Whether to trace against hair voxel structure for hair casting shadow onto opaques."), ECVF_Scalability | ECVF_RenderThreadSafe ); int32 GLumenShortRangeAOHairStrandsScreenTrace = 0; FAutoConsoleVariableRef GVarShortRangeAOHairStrandsScreenTrace( TEXT("r.Lumen.ScreenProbeGather.ShortRangeAO.HairScreenTrace"), GLumenShortRangeAOHairStrandsScreenTrace, TEXT("Whether to trace against hair depth for hair casting shadow onto opaques."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarLumenShortRangeAOApplyDuringIntegration( TEXT("r.Lumen.ScreenProbeGather.ShortRangeAO.ApplyDuringIntegration"), 0, TEXT("Whether Screen Space Bent Normal should be applied during BRDF integration, which has higher quality but is before the temporal filter so causes streaking on moving objects."), ECVF_Scalability | ECVF_RenderThreadSafe ); namespace LumenShortRangeAO { bool ShouldApplyDuringIntegration() { return CVarLumenShortRangeAOApplyDuringIntegration.GetValueOnAnyThread() != 0; } bool UseBentNormal() { return ShouldApplyDuringIntegration() || CVarLumenShortRangeAOBentNormal.GetValueOnRenderThread() != 0; } EPixelFormat GetTextureFormat() { return LumenShortRangeAO::UseBentNormal() ? PF_R32_UINT : PF_R8; } uint32 GetRequestedDownsampleFactor() { return FMath::Clamp(CVarLumenShortRangeAODownsampleFactor.GetValueOnAnyThread(), 1, 2); } uint32 GetDownsampleFactor() { uint32 DownsampleFactor = GetRequestedDownsampleFactor(); if (ShouldApplyDuringIntegration() && LumenScreenProbeGather::GetRequestedIntegrateDownsampleFactor() != DownsampleFactor) { return 1; } if (!ShouldApplyDuringIntegration() && !UseTemporal()) { return 1; } return DownsampleFactor; } bool UseTemporal() { return CVarLumenShortRangeAOTemporal.GetValueOnRenderThread() != 0; } float GetTemporalNeighborhoodClampScale() { return CVarLumenShortRangeAOTemporalNeighborhoodClampScale.GetValueOnRenderThread(); } float GetFoliageOcclusionStrength() { return FMath::Clamp(CVarLumenShortRangeAOFoliageOcclusionStrength.GetValueOnRenderThread(), 0.0f, 1.0f); } } class FScreenSpaceShortRangeAOCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FScreenSpaceShortRangeAOCS) SHADER_USE_PARAMETER_STRUCT(FScreenSpaceShortRangeAOCS, FGlobalShader) BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2DArray, RWShortRangeAO) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWDownsampledSceneDepth) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWDownsampledSceneWorldNormal) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTexturesStruct) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSubstrateGlobalUniformParameters, Substrate) SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureParameters, SceneTextures) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, LightingChannelsTexture) SHADER_PARAMETER_STRUCT_REF(FBlueNoise, BlueNoise) SHADER_PARAMETER(uint32, ScreenProbeGatherStateFrameIndex) SHADER_PARAMETER(FIntPoint, ShortRangeAOViewMin) SHADER_PARAMETER(FIntPoint, ShortRangeAOViewSize) SHADER_PARAMETER_STRUCT_INCLUDE(FHZBParameters, HZBParameters) SHADER_PARAMETER(float, SlopeCompareToleranceScale) SHADER_PARAMETER(float, MaxScreenTraceFraction) SHADER_PARAMETER(float, ScreenTraceNoFallbackThicknessScale) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FHairStrandsViewUniformParameters, HairStrands) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualVoxelParameters, HairStrandsVoxel) RDG_BUFFER_ACCESS(TileIndirectBuffer, ERHIAccess::IndirectArgs) END_SHADER_PARAMETER_STRUCT() class FNumPixelRays : SHADER_PERMUTATION_SPARSE_INT("NUM_PIXEL_RAYS", 4, 8, 16); class FOverflow : SHADER_PERMUTATION_BOOL("PERMUTATION_OVERFLOW_TILE"); class FHairStrandsScreen : SHADER_PERMUTATION_BOOL("USE_HAIRSTRANDS_SCREEN"); class FHairStrandsVoxel : SHADER_PERMUTATION_BOOL("USE_HAIRSTRANDS_VOXEL"); class FOutputBentNormal : SHADER_PERMUTATION_BOOL("OUTPUT_BENT_NORMAL"); class FDownsampleFactor : SHADER_PERMUTATION_RANGE_INT("DOWNSAMPLE_FACTOR", 1, 2); class FUseDistanceFieldRepresentationBit : SHADER_PERMUTATION_BOOL("USE_DISTANCE_FIELD_REPRESENTATION_BIT"); using FPermutationDomain = TShaderPermutationDomain; static FPermutationDomain RemapPermutation(FPermutationDomain PermutationVector) { if (!Substrate::IsSubstrateEnabled()) { PermutationVector.Set(false); } return PermutationVector; } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { FPermutationDomain PermutationVector(Parameters.PermutationId); if (RemapPermutation(PermutationVector) != PermutationVector) { return false; } return DoesPlatformSupportLumenGI(Parameters.Platform); } static int32 GetGroupSize() { // Sanity check static_assert(8 == SUBSTRATE_TILE_SIZE); return 8; } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize()); OutEnvironment.CompilerFlags.Add(CFLAG_Wave32); } }; IMPLEMENT_GLOBAL_SHADER(FScreenSpaceShortRangeAOCS, "/Engine/Private/Lumen/LumenScreenSpaceBentNormal.usf", "ScreenSpaceShortRangeAOCS", SF_Compute); FLumenScreenSpaceBentNormalParameters ComputeScreenSpaceShortRangeAO( FRDGBuilder& GraphBuilder, const FScene* Scene, const FViewInfo& View, const FSceneTextures& SceneTextures, FRDGTextureRef LightingChannelsTexture, const FBlueNoise& BlueNoise, float MaxScreenTraceFraction, float ScreenTraceNoFallbackThicknessScale, ERDGPassFlags ComputePassFlags) { const FSceneTextureParameters& SceneTextureParameters = GetSceneTextureParameters(GraphBuilder, SceneTextures); // When Substrate is enabled, increase the resolution for multi-layer tile overflowing (tile containing multi-BSDF data) const int32 DownsampleFactor = LumenShortRangeAO::GetDownsampleFactor(); FIntPoint ShortRangeAOBufferSize = Substrate::GetSubstrateTextureResolution(View, FIntPoint::DivideAndRoundUp(View.GetSceneTexturesConfig().Extent, DownsampleFactor)); FIntPoint ShortRangeAOViewMin = FIntPoint::DivideAndRoundUp(View.ViewRect.Min, DownsampleFactor); FIntPoint ShortRangeAOViewSize = FIntPoint::DivideAndRoundUp(View.ViewRect.Size(), DownsampleFactor); const uint32 ClosureCount = Substrate::GetSubstrateMaxClosureCount(View); FLumenScreenSpaceBentNormalParameters OutParameters; OutParameters.ShortRangeAOViewMin = ShortRangeAOViewMin; OutParameters.ShortRangeAOViewSize = ShortRangeAOViewSize; FRDGTextureRef ShortRangeAO = GraphBuilder.CreateTexture( FRDGTextureDesc::Create2DArray(ShortRangeAOBufferSize, LumenShortRangeAO::GetTextureFormat(), FClearValueBinding::Black, TexCreate_ShaderResource | TexCreate_UAV, ClosureCount), TEXT("Lumen.ScreenProbeGather.ShortRangeAO")); if (DownsampleFactor != 1) { OutParameters.DownsampledSceneDepth = GraphBuilder.CreateTexture( FRDGTextureDesc::Create2D(ShortRangeAOBufferSize, PF_R32_FLOAT, FClearValueBinding::Black, TexCreate_ShaderResource | TexCreate_UAV), TEXT("Lumen.ScreenProbeGather.DownsampledSceneDepth")); OutParameters.DownsampledSceneWorldNormal = GraphBuilder.CreateTexture( FRDGTextureDesc::Create2D(ShortRangeAOBufferSize, PF_A2B10G10R10, FClearValueBinding::Black, TexCreate_ShaderResource | TexCreate_UAV), TEXT("Lumen.ScreenProbeGather.DownsampledSceneWorldNormal")); } int32 NumPixelRays = 4; if (View.FinalPostProcessSettings.LumenFinalGatherQuality >= 6.0f) { NumPixelRays = 16; } else if (View.FinalPostProcessSettings.LumenFinalGatherQuality >= 2.0f) { NumPixelRays = 8; } if (Lumen::UseHardwareRayTracedShortRangeAO(*View.Family)) { RenderHardwareRayTracingShortRangeAO( GraphBuilder, Scene, SceneTextures, SceneTextureParameters, OutParameters, BlueNoise, MaxScreenTraceFraction, View, ShortRangeAO, NumPixelRays); } else { const bool bNeedTraceHairVoxel = HairStrands::HasViewHairStrandsVoxelData(View) && GLumenShortRangeAOHairStrandsVoxelTrace > 0; const bool bNeedTraceHairScreen = HairStrands::HasViewHairStrandsData(View) && GLumenShortRangeAOHairStrandsScreenTrace > 0; const bool bUseHardwareRayTracing = Lumen::UseHardwareRayTracedScreenProbeGather(*View.Family); auto ScreenSpaceShortRangeAO = [&](bool bOverflow) { FScreenSpaceShortRangeAOCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RWShortRangeAO = GraphBuilder.CreateUAV(ShortRangeAO); PassParameters->RWDownsampledSceneDepth = OutParameters.DownsampledSceneDepth ? GraphBuilder.CreateUAV(OutParameters.DownsampledSceneDepth) : nullptr; PassParameters->RWDownsampledSceneWorldNormal = OutParameters.DownsampledSceneWorldNormal ? GraphBuilder.CreateUAV(OutParameters.DownsampledSceneWorldNormal) : nullptr; PassParameters->SceneTexturesStruct = SceneTextures.UniformBuffer; PassParameters->Substrate = Substrate::BindSubstrateGlobalUniformParameters(View); PassParameters->SceneTextures = SceneTextureParameters; if (!PassParameters->SceneTextures.GBufferVelocityTexture) { PassParameters->SceneTextures.GBufferVelocityTexture = GSystemTextures.GetBlackDummy(GraphBuilder); } PassParameters->MaxScreenTraceFraction = MaxScreenTraceFraction; PassParameters->ScreenTraceNoFallbackThicknessScale = ScreenTraceNoFallbackThicknessScale; PassParameters->ViewUniformBuffer = View.ViewUniformBuffer; PassParameters->LightingChannelsTexture = LightingChannelsTexture; PassParameters->BlueNoise = CreateUniformBufferImmediate(BlueNoise, EUniformBufferUsage::UniformBuffer_SingleDraw); PassParameters->ScreenProbeGatherStateFrameIndex = LumenScreenProbeGather::GetStateFrameIndex(View.ViewState); PassParameters->ShortRangeAOViewMin = ShortRangeAOViewMin; PassParameters->ShortRangeAOViewSize = ShortRangeAOViewSize; PassParameters->HZBParameters = GetHZBParameters(GraphBuilder, View, EHZBType::FurthestHZB); PassParameters->SlopeCompareToleranceScale = GLumenShortRangeAOSlopeCompareToleranceScale; if (bNeedTraceHairScreen) { PassParameters->HairStrands = HairStrands::BindHairStrandsViewUniformParameters(View); } if (bNeedTraceHairVoxel) { PassParameters->HairStrandsVoxel = HairStrands::BindHairStrandsVoxelUniformParameters(View); } FScreenSpaceShortRangeAOCS::FPermutationDomain PermutationVector; PermutationVector.Set(NumPixelRays); PermutationVector.Set(bOverflow); PermutationVector.Set(bNeedTraceHairScreen); PermutationVector.Set(bNeedTraceHairVoxel); PermutationVector.Set(LumenShortRangeAO::UseBentNormal() ? 1 : 0); PermutationVector.Set(DownsampleFactor); PermutationVector.Set(Lumen::IsUsingDistanceFieldRepresentationBit(View)); PermutationVector = FScreenSpaceShortRangeAOCS::RemapPermutation(PermutationVector); auto ComputeShader = View.ShaderMap->GetShader(PermutationVector); if (bOverflow) { PassParameters->TileIndirectBuffer = View.SubstrateViewData.ClosureTileDispatchIndirectBuffer; FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("ShortRangeAO_ScreenSpace(Rays=%u, DownsampleFactor:%d, BentNormal:%d, Overflow)", NumPixelRays, DownsampleFactor, LumenShortRangeAO::UseBentNormal()), ComputePassFlags, ComputeShader, PassParameters, View.SubstrateViewData.ClosureTileDispatchIndirectBuffer, Substrate::GetClosureTileIndirectArgsOffset(DownsampleFactor)); } else { FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("ShortRangeAO_ScreenSpace(Rays=%u, DownsampleFactor:%d, BentNormal:%d)", NumPixelRays, DownsampleFactor, LumenShortRangeAO::UseBentNormal()), ComputePassFlags, ComputeShader, PassParameters, FComputeShaderUtils::GetGroupCount(ShortRangeAOViewSize, FScreenSpaceShortRangeAOCS::GetGroupSize())); } }; ScreenSpaceShortRangeAO(false); if (Lumen::SupportsMultipleClosureEvaluation(View)) { ScreenSpaceShortRangeAO(true); } } OutParameters.ShortRangeAOTexture = ShortRangeAO; OutParameters.ShortRangeAOMode = LumenShortRangeAO::UseBentNormal() ? 2 : 1; return OutParameters; }