// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= PostProcessAmbientOcclusion.cpp: Post processing ambient occlusion implementation. =============================================================================*/ #include "CompositionLighting/PostProcessAmbientOcclusion.h" #include "CompositionLighting/CompositionLighting.h" #include "DataDrivenShaderPlatformInfo.h" #include "PostProcess/SceneFilterRendering.h" #include "SceneTextureParameters.h" #include "ScenePrivate.h" #include "Substrate/Substrate.h" #include "SystemTextures.h" #include "ClearQuad.h" #include "VariableRateShadingImageManager.h" DECLARE_GPU_STAT_NAMED(SSAOSetup, TEXT("ScreenSpace AO Setup") ); DECLARE_GPU_STAT_NAMED(SSAO, TEXT("ScreenSpace AO") ); DECLARE_GPU_STAT_NAMED(BasePassAO, TEXT("BasePass AO") ); DECLARE_GPU_STAT_NAMED(SSAOSmooth, TEXT("SSAO smooth")); DECLARE_GPU_STAT_NAMED(GTAO_HorizonSearch, TEXT("GTAO HorizonSearch")); DECLARE_GPU_STAT_NAMED(GTAO_HorizonSearchIntegrate, TEXT("GTAO HorizonSearch And Integrate")); DECLARE_GPU_STAT_NAMED(GTAO_InnerIntegrate, TEXT("GTAO InnerIntegrate")); DECLARE_GPU_STAT_NAMED(GTAO_TemporalFilter, TEXT("GTAO Temportal Filter")); DECLARE_GPU_STAT_NAMED(GTAO_SpatialFilter, TEXT("GTAO Spatial Filter")); DECLARE_GPU_STAT_NAMED(GTAO_Upsample, TEXT("GTAO Upsample")); static TAutoConsoleVariable CVarAmbientOcclusionCompute( TEXT("r.AmbientOcclusion.Compute"), 0, TEXT("If SSAO should use ComputeShader (not available on all platforms) or PixelShader.\n") TEXT("The [Async] Compute Shader version is WIP, not optimized, requires hardware support (not mobile/DX10/OpenGL3),\n") TEXT("does not use normals which allows it to run right after EarlyZPass (better performance when used with AyncCompute)\n") TEXT(" 0: PixelShader (default)\n") TEXT(" 1: (WIP) Use ComputeShader if possible, otherwise fall back to '0'\n") TEXT(" 2: (WIP) Use AsyncCompute if efficient, otherwise fall back to '1'\n") TEXT(" 3: (WIP) Use AsyncCompute if possible, otherwise fall back to '1'"), ECVF_Scalability | ECVF_RenderThreadSafe); static TAutoConsoleVariable CVarAmbientOcclusionMaxQuality( TEXT("r.AmbientOcclusionMaxQuality"), 100.0f, TEXT("Defines the max clamping value from the post process volume's quality level for ScreenSpace Ambient Occlusion\n") TEXT(" 100: don't override quality level from the post process volume (default)\n") TEXT(" 0..99: clamp down quality level from the post process volume to the maximum set by this cvar\n") TEXT(" -100..0: Enforces a different quality (the absolute value) even if the postprocessvolume asks for a lower quality."), ECVF_Scalability | ECVF_RenderThreadSafe); static TAutoConsoleVariable CVarAmbientOcclusionStepMipLevelFactor( TEXT("r.AmbientOcclusionMipLevelFactor"), 0.5f, TEXT("Controls mipmap level according to the SSAO step id\n") TEXT(" 0: always look into the HZB mipmap level 0 (memory cache trashing)\n") TEXT(" 0.5: sample count depends on post process settings (default)\n") TEXT(" 1: Go into higher mipmap level (quality loss)"), ECVF_Scalability | ECVF_RenderThreadSafe); static TAutoConsoleVariable CVarAmbientOcclusionLevels( TEXT("r.AmbientOcclusionLevels"), -1, TEXT("Defines how many mip levels are using during the ambient occlusion calculation. This is useful when tweaking the algorithm.\n") TEXT("<0: decide based on the quality setting in the postprocess settings/volume and r.AmbientOcclusionMaxQuality (default)\n") TEXT(" 0: none (disable AmbientOcclusion)\n") TEXT(" 1: one\n") TEXT(" 2: two (costs extra performance, soft addition)\n") TEXT(" 3: three (larger radius cost less but can flicker)"), ECVF_Scalability | ECVF_RenderThreadSafe); static TAutoConsoleVariable CVarAmbientOcclusionAsyncComputeBudget( TEXT("r.AmbientOcclusion.AsyncComputeBudget"), 1, TEXT("Defines which level of EAsyncComputeBudget to use for balancing AsyncCompute work against Gfx work.\n") TEXT("Only matters if the compute version of SSAO is active (requires CS support, enabled by cvar, single pass, no normals)\n") TEXT("This is a low level developer tweak to get best performance on hardware that supports AsyncCompute.\n") TEXT(" 0: least AsyncCompute\n") TEXT(" 1: .. (default)\n") TEXT(" 2: .. \n") TEXT(" 3: .. \n") TEXT(" 4: most AsyncCompute"), ECVF_RenderThreadSafe); static TAutoConsoleVariable CVarAmbientOcclusionDepthBoundsTest( TEXT("r.AmbientOcclusion.DepthBoundsTest"), 1, TEXT("Whether to use depth bounds test to cull distant pixels during AO pass. This option is only valid when pixel shader path is used (r.AmbientOcclusion.Compute=0), without upsampling."), ECVF_RenderThreadSafe); static TAutoConsoleVariable CVarAmbientOcclusionMethod( TEXT("r.AmbientOcclusion.Method"), 0, TEXT("Select between SSAO methods \n ") TEXT("0: SSAO (default)\n ") TEXT("1: GTAO\n "), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarGTAOUseNormals( TEXT("r.GTAO.UseNormals"), 0, TEXT("Whether to use GBuffer Normals or Depth Derived normals \n ") TEXT("0: Off (default) \n ") TEXT("1: On\n "), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarGTAOFilterWidth( TEXT("r.GTAO.FilterWidth"), 5, TEXT("Size of the noise pattern and filter width\n ") TEXT("5: 5x5 Pattern (default) \n ") TEXT("4: 4x4 Pattern \n "), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarGTAOThicknessBlend( TEXT("r.GTAO.ThicknessBlend"), 0.5f, TEXT("A heuristic to bias occlusion for thin or thick objects. \n ") TEXT("0 : Off \n ") TEXT(">0 : On - Bigger values lead to reduced occlusion \n ") TEXT("0.5: On (default)\n "), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarGTAOFalloffEnd( TEXT("r.GTAO.FalloffEnd"), 200.0f, TEXT("Distance at when the occlusion completes the fall off. \n "), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarGTAOFalloffStartRatio( TEXT("r.GTAO.FalloffStartRatio"), 0.5f, TEXT("Ratio of the r.GTAO.FalloffEnd value at which it starts to fall off. \n ") TEXT("Must be Between 0 and 1. \n "), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarGTAONumAngles( TEXT("r.GTAO.NumAngles"), 2, TEXT("How Many Angles we choose per pixel \n ") TEXT("Must be Between 1 and 16. \n "), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarGTAOPauseJitter( TEXT("r.GTAO.PauseJitter"), 0, TEXT("Whether to pause Jitter when Temporal filter is off \n "), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarGTAOUpsample( TEXT("r.GTAO.Upsample"), 1, TEXT("Enable Simple or Depth aware upsample filter for GTAO \n ") TEXT("0: Simple \n ") TEXT("1: DepthAware (default)\n "), ECVF_RenderThreadSafe | ECVF_Scalability); float FSSAOHelper::GetAmbientOcclusionQualityRT(const FSceneView& View) { float CVarValue = CVarAmbientOcclusionMaxQuality.GetValueOnRenderThread(); if (CVarValue < 0) { return FMath::Clamp(-CVarValue, 0.0f, 100.0f); } else { return FMath::Min(CVarValue, View.FinalPostProcessSettings.AmbientOcclusionQuality); } } int32 FSSAOHelper::GetAmbientOcclusionShaderLevel(const FSceneView& View) { float QualityPercent = GetAmbientOcclusionQualityRT(View); return (QualityPercent > 75.0f) + (QualityPercent > 55.0f) + (QualityPercent > 25.0f) + (QualityPercent > 5.0f); } bool FSSAOHelper::IsAmbientOcclusionCompute(const ERHIFeatureLevel::Type FeatureLevel) { return FeatureLevel >= ERHIFeatureLevel::SM5 && CVarAmbientOcclusionCompute.GetValueOnAnyThread() >= 1; } int32 FSSAOHelper::GetNumAmbientOcclusionLevels() { return CVarAmbientOcclusionLevels.GetValueOnRenderThread(); } float FSSAOHelper::GetAmbientOcclusionStepMipLevelFactor() { return CVarAmbientOcclusionStepMipLevelFactor.GetValueOnRenderThread(); } EAsyncComputeBudget FSSAOHelper::GetAmbientOcclusionAsyncComputeBudget() { int32 RawBudget = CVarAmbientOcclusionAsyncComputeBudget.GetValueOnRenderThread(); return (EAsyncComputeBudget)FMath::Clamp(RawBudget, (int32)EAsyncComputeBudget::ELeast_0, (int32)EAsyncComputeBudget::EAll_4); } bool FSSAOHelper::IsBasePassAmbientOcclusionRequired(const FViewInfo& View) { // the BaseAO pass is only worth with some AO return (View.FinalPostProcessSettings.AmbientOcclusionStaticFraction >= 1 / 100.0f) && IsUsingGBuffers(View.GetShaderPlatform()); } bool FSSAOHelper::IsAmbientOcclusionAsyncCompute(const FViewInfo& View, uint32 AOPassCount) { // if AsyncCompute is feasible // only single level is allowed. more levels end up reading from gbuffer normals atm which is not allowed. if(IsAmbientOcclusionCompute(View.GetFeatureLevel()) && (AOPassCount == 1)) { int32 ComputeCVar = CVarAmbientOcclusionCompute.GetValueOnRenderThread(); if(ComputeCVar >= 2) { // we might want AsyncCompute if(ComputeCVar == 3) { // enforced, no matter if efficient hardware support return true; } // depends on efficient hardware support return GSupportsEfficientAsyncCompute; } } return false; } // @return 0:off, 0..3 uint32 FSSAOHelper::ComputeAmbientOcclusionPassCount(const FViewInfo& View) { // 0:off / 1 / 2 / 3 uint32 Ret = 0; { int32 CVarLevel = GetNumAmbientOcclusionLevels(); if (IsAmbientOcclusionCompute(View.GetFeatureLevel()) || IsForwardShadingEnabled(View.GetShaderPlatform())) { if (CVarLevel<0) { CVarLevel = 1; } // Compute and forward only support one pass currently. return FMath::Min(CVarLevel, 1); } // usually in the range 0..100 float QualityPercent = GetAmbientOcclusionQualityRT(View); // don't expose 0 as the lowest quality should still render Ret = 1 + (QualityPercent > 70.0f) + (QualityPercent > 35.0f); if (CVarLevel >= 0) { // cvar can override (for scalability or to profile/test) Ret = CVarLevel; } // bring into valid range Ret = FMath::Min(Ret, 3); } return Ret; } // Helper function to get what type of method we are using // EGTAOType::EOff : This is when r.AmbientOcclusion.Method == 0 // EGTAOType::EAsyncHorizonSearch : This is when We need GBuffer normals and the hardware Supports Async Compute. The trace pass is on the // Async Pipe and the Intergrate, Spatial and temporal Filters are on the Gfx pipe after the base pass // EGTAOType::EAsyncCombinedSpatial : This is when we use Derived normals from the depth buffer and the hardware Supports Async Compute. // All passes will be on the async compute pipe // EGTAOType::ENonAsync : All passes are are on the graphics pipe. Can use either gbuffer normals or derived depth normals. EGTAOType FSSAOHelper::GetGTAOPassType(const FViewInfo& View, uint32 Levels) { int32 Method = CVarAmbientOcclusionMethod.GetValueOnRenderThread(); int32 UseNormals = CVarGTAOUseNormals.GetValueOnRenderThread(); if (Method == 1) { if (IsAmbientOcclusionAsyncCompute(View, Levels)) { if (UseNormals) { return EGTAOType::EAsyncHorizonSearch; } else { return EGTAOType::EAsyncCombinedSpatial; } } else { return EGTAOType::ENonAsync; } } return EGTAOType::EOff; } FRDGTextureDesc GetScreenSpaceAOTextureDesc(ERHIFeatureLevel::Type FeatureLevel, FIntPoint Extent) { ETextureCreateFlags TextureCreateFlags = TexCreate_UAV | TexCreate_RenderTargetable | TexCreate_ShaderResource | GFastVRamConfig.ScreenSpaceAO; if (FSSAOHelper::IsAmbientOcclusionCompute(FeatureLevel)) { TextureCreateFlags |= TexCreate_NoFastClear; } return FRDGTextureDesc(FRDGTextureDesc::Create2D(Extent, PF_G8, FClearValueBinding::White, TextureCreateFlags)); } FRDGTextureRef CreateScreenSpaceAOTexture(FRDGBuilder& GraphBuilder, ERHIFeatureLevel::Type FeatureLevel, FIntPoint Extent) { return GraphBuilder.CreateTexture(GetScreenSpaceAOTextureDesc(FeatureLevel, Extent), TEXT("ScreenSpaceAO")); } FRDGTextureRef GetScreenSpaceAOFallback(const FRDGSystemTextures& SystemTextures) { return SystemTextures.White; } //---------------------------------------------------------------------------------------------------------------------- static const uint32 kSSAOParametersArraySize = 5; BEGIN_SHADER_PARAMETER_STRUCT(FSSAOShaderParameters, ) SHADER_PARAMETER_ARRAY(FVector4f, ScreenSpaceAOParams, [kSSAOParametersArraySize]) SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, AOViewport) SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, AOSceneViewport) END_SHADER_PARAMETER_STRUCT(); static FSSAOShaderParameters GetSSAOShaderParameters( const FViewInfo& View, const FScreenPassTextureViewport& InputViewport, const FScreenPassTextureViewport& OutputViewport, const FScreenPassTextureViewport& SceneViewport, EAOTechnique AOTechnique) { const FFinalPostProcessSettings& Settings = View.FinalPostProcessSettings; const FIntPoint RandomizationSize = GSystemTextures.SSAORandomization->GetDesc().Extent; FVector2D ViewportUVToRandomUV(InputViewport.Extent.X / (float)RandomizationSize.X, InputViewport.Extent.Y / (float)RandomizationSize.Y); // e.g. 4 means the input texture is 4x smaller than the buffer size uint32 ScaleToFullRes = SceneViewport.Extent.X / InputViewport.Extent.X; FIntRect ViewRect = FIntRect::DivideAndRoundUp(View.ViewRect, ScaleToFullRes); float AORadiusInShader = Settings.AmbientOcclusionRadius; float ScaleRadiusInWorldSpace = 1.0f; if (!Settings.AmbientOcclusionRadiusInWS) { // radius is defined in view space in 400 units AORadiusInShader /= 400.0f; ScaleRadiusInWorldSpace = 0.0f; } // /4 is an adjustment for usage with multiple mips float f = FMath::Log2(static_cast(ScaleToFullRes)); float g = pow(Settings.AmbientOcclusionMipScale, f); AORadiusInShader *= pow(Settings.AmbientOcclusionMipScale, f) / 4.0f; float Ratio = View.UnscaledViewRect.Width() / (float)View.UnscaledViewRect.Height(); // Grab this and pass into shader so we can negate the fov influence of projection on the screen pos. float InvTanHalfFov = View.ViewMatrices.GetInvTanHalfFov().X; float StaticFraction = FMath::Clamp(Settings.AmbientOcclusionStaticFraction, 0.0f, 1.0f); // clamp to prevent user error float FadeRadius = FMath::Max(1.0f, Settings.AmbientOcclusionFadeRadius); float InvFadeRadius = 1.0f / FadeRadius; FVector2D TemporalOffset(0.0f, 0.0f); if (View.State) { TemporalOffset = (View.State->GetCurrentTemporalAASampleIndex() % 8) * FVector2D(2.48f, 7.52f) / (float)RandomizationSize.X; } const float HzbStepMipLevelFactorValue = FMath::Clamp(FSSAOHelper::GetAmbientOcclusionStepMipLevelFactor(), 0.0f, 100.0f); const float InvAmbientOcclusionDistance = 1.0f / FMath::Max(Settings.AmbientOcclusionDistance_DEPRECATED, KINDA_SMALL_NUMBER); FSSAOShaderParameters Result{}; // /1000 to be able to define the value in that distance Result.ScreenSpaceAOParams[0] = FVector4f(Settings.AmbientOcclusionPower, Settings.AmbientOcclusionBias / 1000.0f, InvAmbientOcclusionDistance, Settings.AmbientOcclusionIntensity); Result.ScreenSpaceAOParams[1] = FVector4f(ViewportUVToRandomUV.X, ViewportUVToRandomUV.Y, AORadiusInShader, Ratio); Result.ScreenSpaceAOParams[2] = FVector4f(ScaleToFullRes, Settings.AmbientOcclusionMipThreshold / ScaleToFullRes, ScaleRadiusInWorldSpace, Settings.AmbientOcclusionMipBlend); Result.ScreenSpaceAOParams[3] = FVector4f(TemporalOffset.X, TemporalOffset.Y, StaticFraction, InvTanHalfFov); Result.ScreenSpaceAOParams[4] = FVector4f(InvFadeRadius, -(Settings.AmbientOcclusionFadeDistance - FadeRadius) * InvFadeRadius, HzbStepMipLevelFactorValue, Settings.AmbientOcclusionFadeDistance); Result.AOViewport = GetScreenPassTextureViewportParameters(OutputViewport); Result.AOSceneViewport = GetScreenPassTextureViewportParameters(SceneViewport); return Result; } //---------------------------------------------------------------------------------------------------------------------- static const uint32 kGTAOParametersArraySize = 5; BEGIN_SHADER_PARAMETER_STRUCT(FGTAOShaderParameters, ) SHADER_PARAMETER_ARRAY(FVector4f, GTAOParams, [kGTAOParametersArraySize]) END_SHADER_PARAMETER_STRUCT(); static FGTAOShaderParameters GetGTAOShaderParameters(const FViewInfo& View, FIntPoint DestSize) { const FFinalPostProcessSettings& Settings = View.FinalPostProcessSettings; const FSceneViewState* ViewState = static_cast(View.State); uint32 Frame = 0; if (ViewState && (CVarGTAOPauseJitter.GetValueOnRenderThread() != 1)) { Frame = ViewState->GetFrameIndex(); } FGTAOShaderParameters Result{}; const float Rots[6] = { 60.0f, 300.0f, 180.0f, 240.0f, 120.0f, 0.0f }; const float Offsets[4] = { 0.1f, 0.6f, 0.35f, 0.85f }; float TemporalAngle = Rots[Frame % 6] * (PI / 360.0f); // Angles of rotation that are set per frame float SinAngle, CosAngle; FMath::SinCos(&SinAngle, &CosAngle, TemporalAngle); Result.GTAOParams[0] = FVector4f(CosAngle, SinAngle, Offsets[(Frame / 6) % 4] * 0.25, Offsets[Frame % 4]); // Frame X = number , Y = Thickness param, float ThicknessBlend = CVarGTAOThicknessBlend.GetValueOnRenderThread(); ThicknessBlend = FMath::Clamp(1.0f - (ThicknessBlend * ThicknessBlend), 0.0f, 0.99f); Result.GTAOParams[1] = FVector4f(Frame, ThicknessBlend, 0.0f, 0.0f); // Destination buffer Size and InvSize float Fx = float(DestSize.X); float Fy = float(DestSize.Y); Result.GTAOParams[2] = FVector4f(Fx, Fy, 1.0f / Fx, 1.0f / Fy); // Fall Off Params float FallOffEnd = CVarGTAOFalloffEnd.GetValueOnRenderThread(); float FallOffStartRatio = FMath::Clamp(CVarGTAOFalloffStartRatio.GetValueOnRenderThread(), 0.0f, 0.999f); float FallOffStart = FallOffEnd * FallOffStartRatio; float FallOffStartSq = FallOffStart * FallOffStart; float FallOffEndSq = FallOffEnd * FallOffEnd; float FallOffScale = 1.0f / (FallOffEndSq - FallOffStartSq); float FallOffBias = -FallOffStartSq * FallOffScale; Result.GTAOParams[3] = FVector4f(FallOffStart, FallOffEnd, FallOffScale, FallOffBias); float TemporalBlendWeight = FMath::Clamp(Settings.AmbientOcclusionTemporalBlendWeight, 0.01f, 1.0f); float NumAngles = FMath::Clamp(CVarGTAONumAngles.GetValueOnRenderThread(), 1.0f, 16.0f); float SinDeltaAngle, CosDeltaAngle; FMath::SinCos(&SinDeltaAngle, &CosDeltaAngle, PI / NumAngles); Result.GTAOParams[4] = FVector4f(Settings.AmbientOcclusionTemporalBlendWeight, NumAngles, SinDeltaAngle, CosDeltaAngle); return Result; } //---------------------------------------------------------------------------------------------------------------------- FHZBParameters GetHZBParametersForAO(FRDGBuilder& GraphBuilder, const FViewInfo& View, FIntPoint InputTextureSize, EAOTechnique AOTechnique) { FHZBParameters OutParameters = GetHZBParameters(GraphBuilder, View, EHZBType::FurthestHZB); FScreenTransform HZBRemapping; if (AOTechnique == EAOTechnique::SSAO) { const FVector2f HZBScaleFactor( float(View.ViewRect.Width()) / float(2 * View.HZBMipmap0Size.X), float(View.ViewRect.Height()) / float(2 * View.HZBMipmap0Size.Y)); // from -1..1 to UV 0..1*HZBScaleFactor HZBRemapping.Scale = FVector2f(0.5f * HZBScaleFactor.X, -0.5f * HZBScaleFactor.Y); HZBRemapping.Bias = FVector2f(0.5f * HZBScaleFactor.X, 0.5f * HZBScaleFactor.Y); } else { const FVector2f HZBScaleFactor( float(InputTextureSize.X) / float(2 * View.HZBMipmap0Size.X), float(InputTextureSize.Y) / float(2 * View.HZBMipmap0Size.Y) ); HZBRemapping.Scale = HZBScaleFactor; HZBRemapping.Bias = FVector2f(0.0f, 0.0f); } OutParameters.ScreenPosToHZBUVScaleBias = FVector4f( HZBRemapping.Scale.X, HZBRemapping.Scale.Y, HZBRemapping.Bias.X, HZBRemapping.Bias.Y); return OutParameters; } //---------------------------------------------------------------------------------------------------------------------- class FAmbientOcclusionSetupPS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FAmbientOcclusionSetupPS); SHADER_USE_PARAMETER_STRUCT(FAmbientOcclusionSetupPS, FGlobalShader); static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5); } BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FViewShaderParameters, View) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures) SHADER_PARAMETER_STRUCT_INCLUDE(FSSAOShaderParameters, SSAOParameters) SHADER_PARAMETER(float, ThresholdInverse) SHADER_PARAMETER(FVector2f, InputExtentInverse) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSubstrateGlobalUniformParameters, Substrate) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT(); }; IMPLEMENT_GLOBAL_SHADER(FAmbientOcclusionSetupPS, "/Engine/Private/PostProcessAmbientOcclusion.usf", "MainSetupPS", SF_Pixel); FScreenPassTexture AddAmbientOcclusionSetupPass( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FSSAOCommonParameters& CommonParameters, FScreenPassTexture Input) { RDG_EVENT_SCOPE_STAT(GraphBuilder, SSAOSetup, "ScreenSpace AO Setup"); RDG_GPU_STAT_SCOPE(GraphBuilder, SSAOSetup); const FScreenPassTextureViewport InputViewport(Input); const FScreenPassTextureViewport OutputViewport(GetDownscaledViewport(InputViewport, 2)); FScreenPassRenderTarget Output; { FRDGTextureDesc OutputDesc = Input.Texture->Desc; OutputDesc.Reset(); OutputDesc.Format = PF_FloatRGBA; OutputDesc.ClearValue = FClearValueBinding::None; OutputDesc.Flags &= ~TexCreate_DepthStencilTargetable; OutputDesc.Flags |= TexCreate_RenderTargetable; OutputDesc.Extent = OutputViewport.Extent; Output.Texture = GraphBuilder.CreateTexture(OutputDesc, TEXT("AmbientOcclusionSetup")); Output.ViewRect = OutputViewport.Rect; Output.LoadAction = ERenderTargetLoadAction::ENoAction; Output.UpdateVisualizeTextureExtent(); } const FFinalPostProcessSettings& Settings = View.FinalPostProcessSettings; const float ThresholdInverseValue = Settings.AmbientOcclusionMipThreshold * ((float)OutputViewport.Extent.X / (float)CommonParameters.SceneTexturesViewport.Extent.X); FAmbientOcclusionSetupPS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->View = View.GetShaderParameters(); PassParameters->SceneTextures = CommonParameters.SceneTexturesUniformBuffer; PassParameters->SSAOParameters = GetSSAOShaderParameters(View, InputViewport, OutputViewport, CommonParameters.SceneTexturesViewport, EAOTechnique::SSAO); PassParameters->ThresholdInverse = ThresholdInverseValue; PassParameters->InputExtentInverse = FVector2f(1.0f) / FVector2f(InputViewport.Extent); PassParameters->Substrate = Substrate::BindSubstrateGlobalUniformParameters(View); PassParameters->RenderTargets[0] = Output.GetRenderTargetBinding(); TShaderMapRef PixelShader(View.ShaderMap); AddDrawScreenPass( GraphBuilder, RDG_EVENT_NAME("AmbientOcclusionSetup %dx%d", OutputViewport.Rect.Width(), OutputViewport.Rect.Height()), View, OutputViewport, InputViewport, PixelShader, PassParameters); return MoveTemp(Output); } //---------------------------------------------------------------------------------------------------------------------- class FAmbientOcclusionSmoothCS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FAmbientOcclusionSmoothCS); SHADER_USE_PARAMETER_STRUCT(FAmbientOcclusionSmoothCS, FGlobalShader); static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("COMPUTE_SHADER"), 1); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEX"), 8); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEY"), 8); } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5); } BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, SSAOSmoothOutputViewport) SHADER_PARAMETER(FScreenTransform, SSAOSmoothOutputToInput) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SSAOSmoothInputTexture) SHADER_PARAMETER_SAMPLER(SamplerState, SSAOSmoothInputSampler) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, SSAOSmoothOutputTexture) END_SHADER_PARAMETER_STRUCT(); }; IMPLEMENT_GLOBAL_SHADER(FAmbientOcclusionSmoothCS, "/Engine/Private/PostProcessAmbientOcclusion.usf", "MainSSAOSmoothCS", SF_Compute); void AddAmbientOcclusionSmoothPass( FRDGBuilder& GraphBuilder, const FViewInfo& View, ESSAOType SSAOType, FScreenPassTexture Input, FScreenPassRenderTarget Output) { RDG_EVENT_SCOPE_STAT(GraphBuilder, SSAOSmooth, "SSAO smooth"); RDG_GPU_STAT_SCOPE(GraphBuilder, SSAOSmooth); const FScreenPassTextureViewport InputViewport(Input); const FScreenPassTextureViewport OutputViewport(Output); const FScreenPassTextureViewportParameters InputViewportParameters = GetScreenPassTextureViewportParameters(InputViewport); const FScreenPassTextureViewportParameters OutputViewportParameters = GetScreenPassTextureViewportParameters(OutputViewport); FAmbientOcclusionSmoothCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->SSAOSmoothOutputViewport = OutputViewportParameters; PassParameters->SSAOSmoothOutputToInput = FScreenTransform::ChangeTextureUVCoordinateFromTo(OutputViewport, InputViewport); PassParameters->SSAOSmoothInputTexture = Input.Texture; PassParameters->SSAOSmoothInputSampler = TStaticSamplerState::GetRHI(); PassParameters->SSAOSmoothOutputTexture = GraphBuilder.CreateUAV(Output.Texture); TShaderMapRef ComputeShader(View.ShaderMap); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("AmbientOcclusionSmooth %dx%d", OutputViewport.Rect.Width(), OutputViewport.Rect.Height()), SSAOType == ESSAOType::EAsyncCS ? ERDGPassFlags::AsyncCompute : ERDGPassFlags::Compute, ComputeShader, PassParameters, FComputeShaderUtils::GetGroupCount(OutputViewport.Rect.Size(), 8)); } //---------------------------------------------------------------------------------------------------------------------- /** * Encapsulates the post processing ambient occlusion pixel shader. * @param bAOSetupAsInput true:use AO setup instead of full resolution depth and normal * @param bDoUpsample true:we have lower resolution pass data we need to upsample, false otherwise * @param ShaderQuality 0..4, 0:low 4:high */ BEGIN_SHADER_PARAMETER_STRUCT(FAmbientOcclusionParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FViewShaderParameters, View) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures) SHADER_PARAMETER_STRUCT_INCLUDE(FHZBParameters, HZBParameters) SHADER_PARAMETER_STRUCT_INCLUDE(FSSAOShaderParameters, SSAOParameters) SHADER_PARAMETER(FVector2f, SSAO_DownsampledAOInverseSize) SHADER_PARAMETER(FVector2f, SSAO_DownsampledAOUVViewportMin) SHADER_PARAMETER(FVector2f, SSAO_DownsampledAOUVViewportMax) SHADER_PARAMETER(FVector2f, SSAO_SvPositionScaleBias) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SSAO_SetupTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SSAO_NormalsTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SSAO_DownsampledAO) SHADER_PARAMETER_SAMPLER(SamplerState, SSAO_Sampler) SHADER_PARAMETER_TEXTURE(Texture2D, RandomNormalTexture) SHADER_PARAMETER_SAMPLER(SamplerState, RandomNormalTextureSampler) END_SHADER_PARAMETER_STRUCT(); class FAmbientOcclusionPS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FAmbientOcclusionPS); SHADER_USE_PARAMETER_STRUCT(FAmbientOcclusionPS, FGlobalShader); class FUseUpsampleDim : SHADER_PERMUTATION_BOOL("USE_UPSAMPLE"); class FUseAoSetupAsInputDim : SHADER_PERMUTATION_BOOL("USE_AO_SETUP_AS_INPUT"); class FShaderQualityDim : SHADER_PERMUTATION_INT("SHADER_QUALITY", 5); using FPermutationDomain = TShaderPermutationDomain; static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("COMPUTE_SHADER"), 0); } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5); } BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FAmbientOcclusionParameters, SharedParameters) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSubstrateGlobalUniformParameters, Substrate) RENDER_TARGET_BINDING_SLOTS() END_GLOBAL_SHADER_PARAMETER_STRUCT(); }; IMPLEMENT_GLOBAL_SHADER(FAmbientOcclusionPS, "/Engine/Private/PostProcessAmbientOcclusion.usf", "MainPS", SF_Pixel); class FAmbientOcclusionCS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FAmbientOcclusionCS); SHADER_USE_PARAMETER_STRUCT(FAmbientOcclusionCS, FGlobalShader); class FUseUpsampleDim : SHADER_PERMUTATION_BOOL("USE_UPSAMPLE"); class FUseAoSetupAsInputDim : SHADER_PERMUTATION_BOOL("USE_AO_SETUP_AS_INPUT"); class FShaderQualityDim : SHADER_PERMUTATION_INT("SHADER_QUALITY", 5); using FPermutationDomain = TShaderPermutationDomain; static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("COMPUTE_SHADER"), 1); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEX"), 16); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEY"), 16); } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5); } BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FAmbientOcclusionParameters, SharedParameters) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSubstrateGlobalUniformParameters, Substrate) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutTexture) END_GLOBAL_SHADER_PARAMETER_STRUCT(); }; IMPLEMENT_GLOBAL_SHADER(FAmbientOcclusionCS, "/Engine/Private/PostProcessAmbientOcclusion.usf", "MainCS", SF_Compute); FScreenPassRenderTarget CreateAmbientOcclusionOutputTarget( FRDGBuilder& GraphBuilder, FRDGTextureDesc OutputDesc, FIntRect ViewRect, ESSAOType AOType, EPixelFormat IntermediateFormatOverride) { const bool bUsingUAVOutput = (AOType == ESSAOType::ECS || AOType == ESSAOType::EAsyncCS); OutputDesc.Reset(); OutputDesc.ClearValue = FClearValueBinding::None; OutputDesc.Flags &= ~TexCreate_DepthStencilTargetable; if (bUsingUAVOutput) { // UAV allowed format OutputDesc.Format = PF_FloatRGBA; OutputDesc.Flags |= TexCreate_UAV; } else { // R:AmbientOcclusion, GBA:used for normal OutputDesc.Format = PF_B8G8R8A8; OutputDesc.Flags |= TexCreate_RenderTargetable; } if (IntermediateFormatOverride != PF_Unknown) { OutputDesc.Format = IntermediateFormatOverride; } return FScreenPassRenderTarget(GraphBuilder.CreateTexture(OutputDesc, TEXT("AmbientOcclusion")), ViewRect, ERenderTargetLoadAction::ENoAction); } void AddAmbientOcclusionPass( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FSSAOCommonParameters& CommonParameters, const FScreenPassTexture& SetupTexture, const FScreenPassTexture& NormalsTexture, const FScreenPassTexture& DownsampledAO, FScreenPassRenderTarget Output, ESSAOType AOType, bool bAOSetupAsInput) { RDG_EVENT_SCOPE_STAT(GraphBuilder, SSAO, "ScreenSpace AO"); RDG_GPU_STAT_SCOPE(GraphBuilder, SSAO); // No setup texture falls back to a depth scene texture fetch. const FScreenPassTextureViewport InputViewport = SetupTexture.IsValid() ? FScreenPassTextureViewport(SetupTexture) : CommonParameters.SceneTexturesViewport; const FScreenPassTextureViewport OutputViewport(Output); const bool bDoUpsample = DownsampledAO.IsValid(); FAmbientOcclusionParameters SharedParameters; SharedParameters.View = View.GetShaderParameters(); SharedParameters.SceneTextures = CommonParameters.SceneTexturesUniformBuffer; SharedParameters.HZBParameters = GetHZBParametersForAO(GraphBuilder, View, CommonParameters.SceneTexturesViewport.Extent, EAOTechnique::SSAO); SharedParameters.SSAOParameters = GetSSAOShaderParameters(View, InputViewport, OutputViewport, CommonParameters.SceneTexturesViewport, EAOTechnique::SSAO); SharedParameters.SSAO_SetupTexture = SetupTexture.Texture; const FRDGSystemTextures& SystemTextures = FRDGSystemTextures::Get(GraphBuilder); if (NormalsTexture.IsValid()) { SharedParameters.SSAO_NormalsTexture = NormalsTexture.Texture; } else { SharedParameters.SSAO_NormalsTexture = SystemTextures.Black; } if (DownsampledAO.IsValid()) { SharedParameters.SSAO_DownsampledAO = DownsampledAO.Texture; SharedParameters.SSAO_DownsampledAOInverseSize = FVector2f(1.0f) / FVector2f(DownsampledAO.Texture->Desc.Extent); const FVector2f ViewportMin(DownsampledAO.ViewRect.Min.X, DownsampledAO.ViewRect.Min.Y); const FVector2f ViewportMax(DownsampledAO.ViewRect.Max.X, DownsampledAO.ViewRect.Max.Y); SharedParameters.SSAO_DownsampledAOUVViewportMin = ViewportMin * SharedParameters.SSAO_DownsampledAOInverseSize; SharedParameters.SSAO_DownsampledAOUVViewportMax = ViewportMax * SharedParameters.SSAO_DownsampledAOInverseSize; } else { SharedParameters.SSAO_DownsampledAO = SystemTextures.Black; SharedParameters.SSAO_DownsampledAOInverseSize = FVector2f(1.0f, 1.0f); SharedParameters.SSAO_DownsampledAOUVViewportMin = FVector2f(0.0f, 0.0f); SharedParameters.SSAO_DownsampledAOUVViewportMax = FVector2f(1.0f, 1.0f); } SharedParameters.SSAO_SvPositionScaleBias = FVector2f(1, 0); SharedParameters.SSAO_Sampler = TStaticSamplerState::GetRHI(); SharedParameters.RandomNormalTexture = GSystemTextures.SSAORandomization->GetRHI(); SharedParameters.RandomNormalTextureSampler = TStaticSamplerState::GetRHI(); FRDGEventName EventName(TEXT("AmbientOcclusion%s %dx%d SetupAsInput=%d Upsample=%d ShaderQuality=%d"), (AOType == ESSAOType::EPS) ? TEXT("PS") : TEXT("CS"), OutputViewport.Rect.Width(), OutputViewport.Rect.Height(), bAOSetupAsInput, bDoUpsample, CommonParameters.ShaderQuality); if (AOType == ESSAOType::ECS || AOType == ESSAOType::EAsyncCS) { // Compute Shader Path FAmbientOcclusionCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->SharedParameters = MoveTemp(SharedParameters); PassParameters->Substrate = Substrate::BindSubstrateGlobalUniformParameters(View); PassParameters->OutTexture = GraphBuilder.CreateUAV(Output.Texture); FAmbientOcclusionCS::FPermutationDomain PermutationVector; PermutationVector.Set(bDoUpsample); PermutationVector.Set(bAOSetupAsInput); PermutationVector.Set(CommonParameters.ShaderQuality); TShaderMapRef ComputeShader(View.ShaderMap, PermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, MoveTemp(EventName), AOType == ESSAOType::EAsyncCS ? ERDGPassFlags::AsyncCompute : ERDGPassFlags::Compute, ComputeShader, PassParameters, FComputeShaderUtils::GetGroupCount(OutputViewport.Rect.Size(), 16)); } else { const uint32 ScaleToFullRes = CommonParameters.SceneTexturesViewport.Extent.X / InputViewport.Extent.X; const bool bDepthBoundsTestEnabled = !bDoUpsample && CVarAmbientOcclusionDepthBoundsTest.GetValueOnRenderThread() && ScaleToFullRes == 1 && GSupportsDepthBoundsTest && CommonParameters.SceneDepth.IsValid() && CommonParameters.SceneDepth.Texture->Desc.NumSamples == 1; FDepthStencilBinding DepthStencilBinding(CommonParameters.SceneDepth.Texture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthRead_StencilWrite); float DepthFar = 0.0f; if (bDepthBoundsTestEnabled) { const FFinalPostProcessSettings& Settings = View.FinalPostProcessSettings; const FMatrix& ProjectionMatrix = View.ViewMatrices.GetProjectionMatrix(); const FVector4f Far = (FVector4f)ProjectionMatrix.TransformFVector4(FVector4(0, 0, Settings.AmbientOcclusionFadeDistance)); DepthFar = FMath::Min(1.0f, Far.Z / Far.W); static_assert(bool(ERHIZBuffer::IsInverted), "Inverted depth buffer is assumed when setting depth bounds test for AO."); FRenderTargetParameters* ClearParameters = GraphBuilder.AllocParameters(); ClearParameters->RenderTargets[0] = Output.GetRenderTargetBinding(); ClearParameters->RenderTargets.DepthStencil = DepthStencilBinding; GraphBuilder.AddPass( RDG_EVENT_NAME("DepthBounds ClearQuad(%s)", Output.Texture->Name), ClearParameters, ERDGPassFlags::Raster, [OutputViewport, DepthFar](FRDGAsyncTask, FRHICommandList& RHICmdList) { // We must clear all pixels that won't be touched by AO shader. FClearQuadCallbacks Callbacks; Callbacks.PSOModifier = [](FGraphicsPipelineStateInitializer& PSOInitializer) { PSOInitializer.bDepthBounds = true; }; Callbacks.PreClear = [DepthFar](FRHICommandList& InRHICmdList) { // This is done by rendering a clear quad over a depth range from AmbientOcclusionFadeDistance to far plane. InRHICmdList.SetDepthBounds(0, DepthFar); // NOTE: Inverted depth }; Callbacks.PostClear = [DepthFar](FRHICommandList& InRHICmdList) { // Set depth bounds test to cover everything from near plane to AmbientOcclusionFadeDistance and run AO pixel shader. InRHICmdList.SetDepthBounds(DepthFar, 1.0f); }; RHICmdList.SetViewport(OutputViewport.Rect.Min.X, OutputViewport.Rect.Min.Y, 0.0f, OutputViewport.Rect.Max.X, OutputViewport.Rect.Max.Y, 1.0f); DrawClearQuad(RHICmdList, FLinearColor::White, Callbacks); }); // Make sure the following pass doesn't clear or ignore the data Output.LoadAction = ERenderTargetLoadAction::ELoad; } // Pixel Shader Path FAmbientOcclusionPS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->SharedParameters = MoveTemp(SharedParameters); PassParameters->Substrate = Substrate::BindSubstrateGlobalUniformParameters(View); PassParameters->RenderTargets[0] = Output.GetRenderTargetBinding(); PassParameters->RenderTargets.ShadingRateTexture = GVRSImageManager.GetVariableRateShadingImage(GraphBuilder, View, FVariableRateShadingImageManager::EVRSPassType::SSAO); if (bDepthBoundsTestEnabled) { PassParameters->RenderTargets.DepthStencil = DepthStencilBinding; } FAmbientOcclusionPS::FPermutationDomain PermutationVector; PermutationVector.Set(bDoUpsample); PermutationVector.Set(bAOSetupAsInput); PermutationVector.Set(CommonParameters.ShaderQuality); TShaderMapRef PixelShader(View.ShaderMap, PermutationVector); TShaderMapRef VertexShader(View.ShaderMap); check(PassParameters); ClearUnusedGraphResources(PixelShader, PassParameters); GraphBuilder.AddPass( MoveTemp(EventName), PassParameters, ERDGPassFlags::Raster, [&View, OutputViewport, InputViewport, VertexShader, PixelShader, PassParameters, bDepthBoundsTestEnabled, DepthFar] (FRDGAsyncTask, FRHICommandList& RHICmdList) { const FIntRect InputRect = InputViewport.Rect; const FIntPoint InputSize = InputViewport.Extent; const FIntRect OutputRect = OutputViewport.Rect; const FIntPoint OutputSize = OutputRect.Size(); RHICmdList.SetViewport(OutputRect.Min.X, OutputRect.Min.Y, 0.0f, OutputRect.Max.X, OutputRect.Max.Y, 1.0f); FGraphicsPipelineStateInitializer GraphicsPSOInit; RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI(); GraphicsPSOInit.RasterizerState = TStaticRasterizerState::GetRHI(); GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState::GetRHI(); GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI; GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader(); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader(); GraphicsPSOInit.PrimitiveType = PT_TriangleList; GraphicsPSOInit.bDepthBounds = bDepthBoundsTestEnabled; SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0); SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *PassParameters); if (bDepthBoundsTestEnabled) { RHICmdList.SetDepthBounds(DepthFar, 1.0f); } DrawPostProcessPass( RHICmdList, 0, 0, OutputSize.X, OutputSize.Y, InputRect.Min.X, InputRect.Min.Y, InputRect.Width(), InputRect.Height(), OutputSize, InputSize, VertexShader, View.StereoViewIndex, false, EDRF_UseTriangleOptimization); if (bDepthBoundsTestEnabled) { RHICmdList.SetDepthBounds(0, 1.0f); } }); } } FScreenPassTexture AddAmbientOcclusionStepPass( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FSSAOCommonParameters& CommonParameters, const FScreenPassTexture& SetupTexture, const FScreenPassTexture& NormalsTexture, const FScreenPassTexture& DownsampledAO) { FScreenPassRenderTarget Output = CreateAmbientOcclusionOutputTarget(GraphBuilder, SetupTexture.Texture->Desc, SetupTexture.ViewRect, CommonParameters.DownscaleType, PF_Unknown); AddAmbientOcclusionPass( GraphBuilder, View, CommonParameters, SetupTexture, NormalsTexture, DownsampledAO, Output, CommonParameters.DownscaleType, true); return MoveTemp(Output); } FScreenPassTexture AddAmbientOcclusionFinalPass( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FSSAOCommonParameters& CommonParameters, const FScreenPassTexture& SetupTexture, const FScreenPassTexture& NormalsTexture, const FScreenPassTexture& DownsampledAO, FScreenPassRenderTarget FinalOutput) { FScreenPassRenderTarget CurrentOutput = CommonParameters.bNeedSmoothingPass ? CreateAmbientOcclusionOutputTarget(GraphBuilder, FinalOutput.Texture->Desc, FinalOutput.ViewRect, CommonParameters.FullscreenType, PF_G8): FinalOutput; AddAmbientOcclusionPass( GraphBuilder, View, CommonParameters, SetupTexture, NormalsTexture, DownsampledAO, CurrentOutput, CommonParameters.FullscreenType, false); if (CommonParameters.bNeedSmoothingPass) { AddAmbientOcclusionSmoothPass( GraphBuilder, View, CommonParameters.FullscreenType, CurrentOutput, FinalOutput); CurrentOutput = FinalOutput; } return MoveTemp(CurrentOutput); } //---------------------------------------------------------------------------------------------------------------------- class FGTAOHorizonSearchAndIntegrateCS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FGTAOHorizonSearchAndIntegrateCS); SHADER_USE_PARAMETER_STRUCT(FGTAOHorizonSearchAndIntegrateCS, FGlobalShader); class FShaderQualityDim : SHADER_PERMUTATION_INT("SHADER_QUALITY", 5); class FUseNormalBufferDim : SHADER_PERMUTATION_BOOL("USE_NORMALBUFFER"); using FPermutationDomain = TShaderPermutationDomain; static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("COMPUTE_SHADER"), 1); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEX"), 8); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEY"), 8); } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5); } BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures) SHADER_PARAMETER_STRUCT_INCLUDE(FHZBParameters, HZBParameters) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSubstrateGlobalUniformParameters, Substrate) SHADER_PARAMETER_STRUCT_INCLUDE(FSSAOShaderParameters, SSAOParameters) SHADER_PARAMETER_STRUCT_INCLUDE(FGTAOShaderParameters, GTAOParameters) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutTexture) END_GLOBAL_SHADER_PARAMETER_STRUCT(); }; IMPLEMENT_GLOBAL_SHADER(FGTAOHorizonSearchAndIntegrateCS, "/Engine/Private/PostProcessAmbientOcclusion.usf", "GTAOCombinedCS", SF_Compute); FGTAOHorizonSearchOutputs AddGTAOHorizonSearchIntegratePass( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FGTAOCommonParameters& CommonParameters, FScreenPassTexture SceneDepth) { RDG_EVENT_SCOPE_STAT(GraphBuilder, GTAO_HorizonSearchIntegrate, "GTAO HorizonSearch And Integrate"); RDG_GPU_STAT_SCOPE(GraphBuilder, GTAO_HorizonSearchIntegrate); const FScreenPassTextureViewport SceneViewport(SceneDepth); const FScreenPassTextureViewport OutputViewport(GetDownscaledViewport(SceneViewport, CommonParameters.DownscaleFactor)); FScreenPassRenderTarget Output; { FRDGTextureDesc OutputDesc = SceneDepth.Texture->Desc; OutputDesc.Reset(); OutputDesc.Format = PF_G8; OutputDesc.ClearValue = FClearValueBinding::None; OutputDesc.Flags &= ~TexCreate_DepthStencilTargetable; OutputDesc.Flags |= TexCreate_RenderTargetable | TexCreate_ShaderResource | TexCreate_UAV; OutputDesc.Extent = OutputViewport.Extent; Output.Texture = GraphBuilder.CreateTexture(OutputDesc, TEXT("GTAOCombined")); Output.ViewRect = OutputViewport.Rect; Output.LoadAction = ERenderTargetLoadAction::ENoAction; Output.UpdateVisualizeTextureExtent(); } const bool bUseNormals = CVarGTAOUseNormals.GetValueOnRenderThread() >= 1; FGTAOHorizonSearchAndIntegrateCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->View = View.ViewUniformBuffer; PassParameters->SceneTextures = CommonParameters.SceneTexturesUniformBuffer; PassParameters->HZBParameters = GetHZBParametersForAO(GraphBuilder, View, SceneViewport.Extent, EAOTechnique::GTAO); PassParameters->Substrate = Substrate::BindSubstrateGlobalUniformParameters(View); PassParameters->SSAOParameters = GetSSAOShaderParameters(View, SceneViewport, OutputViewport, CommonParameters.SceneTexturesViewport, EAOTechnique::GTAO); PassParameters->GTAOParameters = GetGTAOShaderParameters(View, OutputViewport.Extent); PassParameters->OutTexture = GraphBuilder.CreateUAV(Output.Texture); FGTAOHorizonSearchAndIntegrateCS::FPermutationDomain PermutationVector; PermutationVector.Set(CommonParameters.ShaderQuality); PermutationVector.Set(bUseNormals); TShaderMapRef ComputeShader(View.ShaderMap, PermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("GTAOCombinedCS %dx%d ShaderQuality=%d UseNormals=%d", OutputViewport.Rect.Width(), OutputViewport.Rect.Height(), CommonParameters.ShaderQuality, bUseNormals ? 1 : 0), CommonParameters.GTAOType == EGTAOType::EAsyncCombinedSpatial ? ERDGPassFlags::AsyncCompute : ERDGPassFlags::Compute, ComputeShader, PassParameters, FComputeShaderUtils::GetGroupCount(OutputViewport.Rect.Size(), 8)); FGTAOHorizonSearchOutputs Outputs; Outputs.Color = Output; return MoveTemp(Outputs); } //---------------------------------------------------------------------------------------------------------------------- class FGTAOInnerIntegratePS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FGTAOInnerIntegratePS); SHADER_USE_PARAMETER_STRUCT(FGTAOInnerIntegratePS, FGlobalShader); static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("COMPUTE_SHADER"), 0); } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5); } BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSubstrateGlobalUniformParameters, Substrate) SHADER_PARAMETER_STRUCT_INCLUDE(FSSAOShaderParameters, SSAOParameters) SHADER_PARAMETER_STRUCT_INCLUDE(FGTAOShaderParameters, GTAOParameters) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, HorizonsTexture) SHADER_PARAMETER_SAMPLER(SamplerState, HorizonsTextureSampler) RENDER_TARGET_BINDING_SLOTS() END_GLOBAL_SHADER_PARAMETER_STRUCT(); }; IMPLEMENT_GLOBAL_SHADER(FGTAOInnerIntegratePS, "/Engine/Private/PostProcessAmbientOcclusion.usf", "GTAOInnerIntegratePS", SF_Pixel); FScreenPassTexture AddGTAOInnerIntegratePass( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FGTAOCommonParameters& CommonParameters, FScreenPassTexture SceneDepth, FScreenPassTexture HorizonsTexture) { RDG_EVENT_SCOPE_STAT(GraphBuilder, GTAO_InnerIntegrate, "GTAO InnerIntegrate"); RDG_GPU_STAT_SCOPE(GraphBuilder, GTAO_InnerIntegrate); const FScreenPassTextureViewport InputViewport(SceneDepth); const FScreenPassTextureViewport OutputViewport(GetDownscaledViewport(InputViewport, CommonParameters.DownscaleFactor)); FScreenPassRenderTarget Output; { FRDGTextureDesc OutputDesc = SceneDepth.Texture->Desc; OutputDesc.Reset(); OutputDesc.Format = PF_G8; OutputDesc.ClearValue = FClearValueBinding::None; OutputDesc.Flags &= ~TexCreate_DepthStencilTargetable; OutputDesc.Flags |= TexCreate_RenderTargetable | TexCreate_ShaderResource | TexCreate_UAV; OutputDesc.Extent = OutputViewport.Extent; Output.Texture = GraphBuilder.CreateTexture(OutputDesc, TEXT("GTAOInnerIntegrate")); Output.ViewRect = OutputViewport.Rect; Output.LoadAction = ERenderTargetLoadAction::ENoAction; Output.UpdateVisualizeTextureExtent(); } FGTAOInnerIntegratePS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->View = View.ViewUniformBuffer; PassParameters->SceneTextures = CommonParameters.SceneTexturesUniformBuffer; PassParameters->Substrate = Substrate::BindSubstrateGlobalUniformParameters(View); PassParameters->SSAOParameters = GetSSAOShaderParameters(View, InputViewport, OutputViewport, CommonParameters.SceneTexturesViewport, EAOTechnique::GTAO); PassParameters->GTAOParameters = GetGTAOShaderParameters(View, OutputViewport.Extent); PassParameters->HorizonsTexture = HorizonsTexture.Texture; PassParameters->HorizonsTextureSampler = TStaticSamplerState::GetRHI(); PassParameters->RenderTargets[0] = Output.GetRenderTargetBinding(); TShaderMapRef PixelShader(View.ShaderMap); AddDrawScreenPass( GraphBuilder, RDG_EVENT_NAME("GTAOInnerIntegratePS %dx%d Downscale=%d", OutputViewport.Rect.Width(), OutputViewport.Rect.Height(), CommonParameters.DownscaleFactor), View, OutputViewport, InputViewport, PixelShader, PassParameters); return MoveTemp(Output); } //---------------------------------------------------------------------------------------------------------------------- class FGTAOHorizonSearchCS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FGTAOHorizonSearchCS); SHADER_USE_PARAMETER_STRUCT(FGTAOHorizonSearchCS, FGlobalShader); class FShaderQualityDim : SHADER_PERMUTATION_INT("SHADER_QUALITY", 5); using FPermutationDomain = TShaderPermutationDomain; static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("COMPUTE_SHADER"), 1); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEX"), 8); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEY"), 8); } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5); } BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures) SHADER_PARAMETER_STRUCT_INCLUDE(FHZBParameters, HZBParameters) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSubstrateGlobalUniformParameters, Substrate) SHADER_PARAMETER_STRUCT_INCLUDE(FSSAOShaderParameters, SSAOParameters) SHADER_PARAMETER_STRUCT_INCLUDE(FGTAOShaderParameters, GTAOParameters) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, HorizonOutTexture) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, DepthOutTexture) END_GLOBAL_SHADER_PARAMETER_STRUCT(); }; IMPLEMENT_GLOBAL_SHADER(FGTAOHorizonSearchCS, "/Engine/Private/PostProcessAmbientOcclusion.usf", "HorizonSearchCS", SF_Compute); FGTAOHorizonSearchOutputs AddGTAOHorizonSearchPass( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FGTAOCommonParameters& CommonParameters, FScreenPassTexture SceneDepth, FScreenPassRenderTarget HorizonOutput) { RDG_EVENT_SCOPE_STAT(GraphBuilder, GTAO_HorizonSearch, "GTAO HorizonSearch"); RDG_GPU_STAT_SCOPE(GraphBuilder, GTAO_HorizonSearch); const FScreenPassTextureViewport SceneViewport(SceneDepth); const FScreenPassTextureViewport OutputViewport(HorizonOutput); FGTAOHorizonSearchCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->View = View.ViewUniformBuffer; PassParameters->SceneTextures = CommonParameters.SceneTexturesUniformBuffer; PassParameters->HZBParameters = GetHZBParametersForAO(GraphBuilder, View, SceneViewport.Extent, EAOTechnique::GTAO); PassParameters->Substrate = Substrate::BindSubstrateGlobalUniformParameters(View); PassParameters->SSAOParameters = GetSSAOShaderParameters(View, SceneViewport, OutputViewport, CommonParameters.SceneTexturesViewport, EAOTechnique::GTAO); PassParameters->GTAOParameters = GetGTAOShaderParameters(View, OutputViewport.Extent); PassParameters->HorizonOutTexture = GraphBuilder.CreateUAV(HorizonOutput.Texture); FGTAOHorizonSearchCS::FPermutationDomain PermutationVector; PermutationVector.Set(CommonParameters.ShaderQuality); TShaderMapRef ComputeShader(View.ShaderMap, PermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("HorizonSearchCS %dx%d ShaderQuality=%d", OutputViewport.Rect.Width(), OutputViewport.Rect.Height(), CommonParameters.ShaderQuality), ERDGPassFlags::AsyncCompute, ComputeShader, PassParameters, FComputeShaderUtils::GetGroupCount(OutputViewport.Rect.Size(), 8)); FGTAOHorizonSearchOutputs Outputs; Outputs.Color = HorizonOutput; return MoveTemp(Outputs); } //---------------------------------------------------------------------------------------------------------------------- class FGTAOTemporalFilterCS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FGTAOTemporalFilterCS); SHADER_USE_PARAMETER_STRUCT(FGTAOTemporalFilterCS, FGlobalShader); static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("COMPUTE_SHADER"), 1); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEX"), 8); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEY"), 8); } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5); } BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures) SHADER_PARAMETER_STRUCT_INCLUDE(FSSAOShaderParameters, SSAOParameters) SHADER_PARAMETER_STRUCT_INCLUDE(FGTAOShaderParameters, GTAOParameters) SHADER_PARAMETER(FVector4f, PrevScreenPositionScaleBias) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, GTAOTemporalInput) SHADER_PARAMETER_SAMPLER(SamplerState, GTAOTemporalSampler) SHADER_PARAMETER(FVector2f, GTAOTemporalInputPixelSize) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, HistoryTexture) SHADER_PARAMETER_SAMPLER(SamplerState, HistoryTextureSampler) SHADER_PARAMETER(FVector2f, HistoryTextureSize) SHADER_PARAMETER(FVector2f, HistoryTexturePixelSize) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ZCurrTexture) SHADER_PARAMETER_SAMPLER(SamplerState, ZCurrTextureSampler) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneVelocityTexture) SHADER_PARAMETER_SAMPLER(SamplerState, SceneVelocityTextureSampler) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutTexture) END_GLOBAL_SHADER_PARAMETER_STRUCT(); }; IMPLEMENT_GLOBAL_SHADER(FGTAOTemporalFilterCS, "/Engine/Private/PostProcessAmbientOcclusion.usf", "GTAOTemporalFilterCS", SF_Compute); FGTAOTemporalOutputs AddGTAOTemporalPass( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FGTAOCommonParameters& CommonParameters, FScreenPassTexture Input, FScreenPassTexture SceneDepth, FScreenPassTexture SceneVelocity, FScreenPassTexture HistoryColor, FScreenPassTextureViewport HistoryViewport) { RDG_EVENT_SCOPE_STAT(GraphBuilder, GTAO_TemporalFilter, "GTAO Temportal Filter"); RDG_GPU_STAT_SCOPE(GraphBuilder, GTAO_TemporalFilter); const FScreenPassTextureViewport InputViewport(Input); const FScreenPassTextureViewport OutputViewport(InputViewport); FScreenPassRenderTarget OutputAO; { FRDGTextureDesc OutputDesc = Input.Texture->Desc; OutputDesc.Reset(); OutputDesc.ClearValue = FClearValueBinding::None; OutputDesc.Flags &= ~TexCreate_DepthStencilTargetable; OutputDesc.Flags |= TexCreate_RenderTargetable | TexCreate_ShaderResource | TexCreate_UAV; OutputDesc.Extent = OutputViewport.Extent; OutputDesc.Format = PF_G8; OutputAO.Texture = GraphBuilder.CreateTexture(OutputDesc, TEXT("GTAOTemporalOutput")); OutputAO.ViewRect = OutputViewport.Rect; OutputAO.LoadAction = ERenderTargetLoadAction::ENoAction; OutputAO.UpdateVisualizeTextureExtent(); } const FVector2f HistoryTextureSize = FVector2f(HistoryColor.Texture->Desc.Extent); const FVector2f HistoryTexturePixelSize = FVector2f(1.0f) / HistoryTextureSize; const FIntPoint ViewportOffset = HistoryViewport.Rect.Min; const FIntPoint ViewportExtent = HistoryViewport.Rect.Size(); const FIntPoint BufferSize = HistoryViewport.Extent; const FVector4f PrevScreenPositionScaleBiasValue = FVector4f( ViewportExtent.X * 0.5f / BufferSize.X, -ViewportExtent.Y * 0.5f / BufferSize.Y, (ViewportExtent.X * 0.5f + ViewportOffset.X) / BufferSize.X, (ViewportExtent.Y * 0.5f + ViewportOffset.Y) / BufferSize.Y); FGTAOTemporalFilterCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->View = View.ViewUniformBuffer; PassParameters->SceneTextures = CommonParameters.SceneTexturesUniformBuffer; PassParameters->SSAOParameters = GetSSAOShaderParameters(View, InputViewport, OutputViewport, CommonParameters.SceneTexturesViewport, EAOTechnique::GTAO); PassParameters->GTAOParameters = GetGTAOShaderParameters(View, OutputViewport.Extent); PassParameters->PrevScreenPositionScaleBias = PrevScreenPositionScaleBiasValue; PassParameters->GTAOTemporalInput = Input.Texture; PassParameters->GTAOTemporalSampler = TStaticSamplerState::GetRHI(); PassParameters->GTAOTemporalInputPixelSize = FVector2f(1.0f) / FVector2f(InputViewport.Extent); PassParameters->HistoryTexture = HistoryColor.Texture; PassParameters->HistoryTextureSampler = TStaticSamplerState::GetRHI(); PassParameters->HistoryTextureSize = HistoryTextureSize; PassParameters->HistoryTexturePixelSize = HistoryTexturePixelSize; PassParameters->ZCurrTexture = SceneDepth.Texture; PassParameters->ZCurrTextureSampler = TStaticSamplerState::GetRHI(); PassParameters->SceneVelocityTexture = SceneVelocity.Texture; PassParameters->SceneVelocityTextureSampler = TStaticSamplerState::GetRHI(); PassParameters->OutTexture = GraphBuilder.CreateUAV(OutputAO.Texture); TShaderMapRef ComputeShader(View.ShaderMap); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("GTAOTemporalFilterCS %dx%d", OutputViewport.Rect.Width(), OutputViewport.Rect.Height()), ERDGPassFlags::Compute, ComputeShader, PassParameters, FComputeShaderUtils::GetGroupCount(OutputViewport.Rect.Size(), 8)); FGTAOTemporalOutputs Outputs; Outputs.OutputAO = OutputAO; Outputs.TargetExtent = OutputViewport.Extent; Outputs.ViewportRect = OutputViewport.Rect; return MoveTemp(Outputs); } //---------------------------------------------------------------------------------------------------------------------- class FGTAOSpatialFilterCS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FGTAOSpatialFilterCS); SHADER_USE_PARAMETER_STRUCT(FGTAOSpatialFilterCS, FGlobalShader); static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("COMPUTE_SHADER"), 1); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEX"), 8); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEY"), 8); } BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures) SHADER_PARAMETER_STRUCT_INCLUDE(FSSAOShaderParameters, SSAOParameters) SHADER_PARAMETER(FIntPoint, GTAOSpatialFilterExtents) SHADER_PARAMETER(FVector4f, GTAOSpatialFilterParams) SHADER_PARAMETER(FVector4f, GTAOSpatialFilterWidth) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, GTAOSpatialFilterTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, GTAOSpatialFilterDepthTexture) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutTexture) END_GLOBAL_SHADER_PARAMETER_STRUCT(); }; IMPLEMENT_GLOBAL_SHADER(FGTAOSpatialFilterCS, "/Engine/Private/PostProcessAmbientOcclusion.usf", "GTAOSpatialFilterCS", SF_Compute); FScreenPassTexture AddGTAOSpatialFilter( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FGTAOCommonParameters& CommonParameters, FScreenPassTexture Input, FScreenPassTexture InputDepth, FScreenPassRenderTarget SuggestedOutput) { RDG_EVENT_SCOPE_STAT(GraphBuilder, GTAO_SpatialFilter, "GTAO Spatial Filter"); RDG_GPU_STAT_SCOPE(GraphBuilder, GTAO_SpatialFilter); const FScreenPassTextureViewport InputViewport(Input); const FScreenPassTextureViewport OutputViewport(InputViewport); FScreenPassRenderTarget Output = SuggestedOutput; if (!Output.IsValid()) { FRDGTextureDesc OutputDesc = Input.Texture->Desc; OutputDesc.Reset(); OutputDesc.Format = PF_G8; OutputDesc.ClearValue = FClearValueBinding::None; OutputDesc.Flags &= ~TexCreate_DepthStencilTargetable; OutputDesc.Flags |= TexCreate_RenderTargetable | TexCreate_ShaderResource | TexCreate_UAV; OutputDesc.Extent = OutputViewport.Extent; Output.Texture = GraphBuilder.CreateTexture(OutputDesc, TEXT("GTAOFilter")); Output.ViewRect = OutputViewport.Rect; Output.LoadAction = ERenderTargetLoadAction::ENoAction; Output.UpdateVisualizeTextureExtent(); } FGTAOSpatialFilterCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->View = View.ViewUniformBuffer; PassParameters->SceneTextures = CommonParameters.SceneTexturesUniformBuffer; PassParameters->SSAOParameters = GetSSAOShaderParameters(View, InputViewport, OutputViewport, CommonParameters.SceneTexturesViewport, EAOTechnique::GTAO); PassParameters->GTAOSpatialFilterExtents = OutputViewport.Rect.Size(); FVector4f FilterWidthParamsValue(0.0f, 0.0f, 0.0f, 0.0f); float FilterWidth = CVarGTAOFilterWidth.GetValueOnRenderThread(); if (FilterWidth == 3.0f) { FilterWidthParamsValue.X = -1.0f; FilterWidthParamsValue.Y = 1.0f; } else if (FilterWidth == 4.0f) { FilterWidthParamsValue.X = -1.0f; FilterWidthParamsValue.Y = 2.0f; } else { FilterWidthParamsValue.X = -2.0f; FilterWidthParamsValue.Y = 2.0f; } PassParameters->GTAOSpatialFilterWidth = FilterWidthParamsValue; float DownsampleFactor = 1.0; FVector4f FilterParamsValue((float)DownsampleFactor, 0.0f, 0.0f, 0.0f); // JDW TODO PassParameters->GTAOSpatialFilterParams = FilterParamsValue; PassParameters->GTAOSpatialFilterTexture = Input.Texture; PassParameters->GTAOSpatialFilterDepthTexture = InputDepth.Texture; PassParameters->OutTexture = GraphBuilder.CreateUAV(Output.Texture); TShaderMapRef ComputeShader(View.ShaderMap); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("GTAOSpatialFilterCS %dx%d", OutputViewport.Rect.Width(), OutputViewport.Rect.Height()), CommonParameters.GTAOType == EGTAOType::EAsyncCombinedSpatial ? ERDGPassFlags::AsyncCompute : ERDGPassFlags::Compute, ComputeShader, PassParameters, FComputeShaderUtils::GetGroupCount(OutputViewport.Rect.Size(), 8)); return MoveTemp(Output); } //---------------------------------------------------------------------------------------------------------------------- class FGTAOUpsamplePS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FGTAOUpsamplePS); SHADER_USE_PARAMETER_STRUCT(FGTAOUpsamplePS, FGlobalShader); static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("COMPUTE_SHADER"), 0); } BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, GTAOUpsampleTexture) SHADER_PARAMETER_SAMPLER(SamplerState, GTAOUpsampleSampler) SHADER_PARAMETER(FVector2f, GTAOUpsamplePixelSize) RENDER_TARGET_BINDING_SLOTS() END_GLOBAL_SHADER_PARAMETER_STRUCT(); }; IMPLEMENT_GLOBAL_SHADER(FGTAOUpsamplePS, "/Engine/Private/PostProcessAmbientOcclusion.usf", "GTAOUpsamplePS", SF_Pixel); FScreenPassTexture AddGTAOUpsamplePass( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FGTAOCommonParameters& CommonParameters, FScreenPassTexture Input, FScreenPassTexture SceneDepth, FScreenPassRenderTarget Output) { RDG_EVENT_SCOPE_STAT(GraphBuilder, GTAO_Upsample, "GTAO Upsample"); RDG_GPU_STAT_SCOPE(GraphBuilder, GTAO_Upsample); const FScreenPassTextureViewport InputViewport(Input); const FScreenPassTextureViewport OutputViewport(Output); // Pixel Shader FGTAOUpsamplePS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->GTAOUpsampleTexture = Input.Texture; PassParameters->GTAOUpsampleSampler = TStaticSamplerState::GetRHI(); PassParameters->GTAOUpsamplePixelSize = FVector2f(1.0f) / FVector2f(InputViewport.Extent); PassParameters->RenderTargets[0] = Output.GetRenderTargetBinding(); TShaderMapRef VertexShader(View.ShaderMap); TShaderMapRef PixelShader(View.ShaderMap); AddDrawScreenPass( GraphBuilder, RDG_EVENT_NAME("GTAOUpsamplePS %dx%d", OutputViewport.Rect.Width(), OutputViewport.Rect.Height()), View, OutputViewport, InputViewport, VertexShader, PixelShader, TStaticBlendState<>::GetRHI(), TStaticDepthStencilState::GetRHI(), PassParameters); return MoveTemp(Output); }