// Copyright Epic Games, Inc. All Rights Reserved. #include "PathTracingSpatialTemporalDenoising.h" #include "PathTracing.h" #include "RHI.h" class FDenoiserManager { public: static FDenoiserManager& Get() { static FDenoiserManager DenoiserManager; return DenoiserManager; } void RegisterSpatialDenoiser(TUniquePtr InDenoiser, FString Name) { check(!SpatialDenoiser.Contains(Name)); bNeedTextureCreateExtraFlags |= InDenoiser->NeedTextureCreateExtraFlags(); SpatialDenoiser.Add(Name, MoveTemp(InDenoiser)); } void RegisterSpatialTemporalDenoiser(TUniquePtr InDenoiser, FString Name) { check(!PathTracingSpatialTemporalDenoisers.Contains(Name)); bNeedTextureCreateExtraFlags |= InDenoiser->NeedTextureCreateExtraFlags(); PathTracingSpatialTemporalDenoisers.Add(Name, MoveTemp(InDenoiser)); } bool HasSpatialDenoiser()const { return SpatialDenoiser.Num() > 0; } bool HasSpatialTemporalDenoiser()const { return PathTracingSpatialTemporalDenoisers.Num() > 0; } bool HasDenoiser()const { return HasSpatialDenoiser() || HasSpatialTemporalDenoiser();} UE::Renderer::Private::IPathTracingDenoiser* GetSpatialDenoiser(FString Name, bool bMatch) { if (SpatialDenoiser.Contains(Name)) { return SpatialDenoiser[Name].Get(); } else if (SpatialDenoiser.Num() > 0 && !bMatch) { return SpatialDenoiser.CreateConstIterator()->Value.Get(); } return nullptr; } UE::Renderer::Private::IPathTracingSpatialTemporalDenoiser* GetSpatialTemporalDenoiser(FString Name, bool bMatch) { if (PathTracingSpatialTemporalDenoisers.Contains(Name)) { return PathTracingSpatialTemporalDenoisers[Name].Get(); } else if (PathTracingSpatialTemporalDenoisers.Num() > 0 && !bMatch) { return PathTracingSpatialTemporalDenoisers.CreateConstIterator()->Value.Get(); } return nullptr; } void UnregisterDenoiser(FString Name) { SpatialDenoiser.Remove(Name); PathTracingSpatialTemporalDenoisers.Remove(Name); } /** If any plugin needs extra creation flag*/ bool NeedTextureCreateExtraFlags() { return bNeedTextureCreateExtraFlags; } private: FDenoiserManager() = default; FDenoiserManager(const FDenoiserManager&) = delete; FDenoiserManager& operator=(const FDenoiserManager&) = delete; TMap > SpatialDenoiser; /**Spatial-temporal denoiser can work as both spatial and temporal denoiser*/ TMap > PathTracingSpatialTemporalDenoisers; /** Since all plugins can be dynamically switched, need to increase the compatibility*/ bool bNeedTextureCreateExtraFlags = false; }; void RegisterSpatialDenoiser(TUniquePtr PathTracingDenoiser, FString Name) { FDenoiserManager::Get().RegisterSpatialDenoiser(MoveTemp(PathTracingDenoiser), Name); } void RegisterSpatialTemporalDenoiser(TUniquePtr PathTracingDenoiser, FString Name) { FDenoiserManager::Get().RegisterSpatialTemporalDenoiser(MoveTemp(PathTracingDenoiser), Name); } void UnregisterDenoiser(FString Name) { FDenoiserManager::Get().UnregisterDenoiser(Name); } bool HasTemporalDenoiser() { return FDenoiserManager::Get().HasSpatialTemporalDenoiser(); } #if RHI_RAYTRACING #include "DeferredShadingRenderer.h" #include "GenerateMips.h" #include "GlobalShader.h" #include "HAL/PlatformApplicationMisc.h" #include "PathTracingDefinitions.h" #include "RayTracing/RayTracingMaterialHitShaders.h" #include "RayTracingDefinitions.h" #include "RayTracingTypes.h" #include "RenderGraphDefinitions.h" #include "RendererPrivate.h" #include "PostProcess/PostProcessMaterial.h" #include DEFINE_LOG_CATEGORY_STATIC(LogPathTracingDenoising, Log, All); namespace { TAutoConsoleVariable CVarPathTracingDenoiser( TEXT("r.PathTracing.Denoiser"), -1, TEXT("Enable denoising of the path traced output (if a denoiser plugin is active) (default = -1 (driven by postprocesing volume))\n") TEXT("-1: inherit from PostProcessVolume\n") TEXT("0: disable denoiser\n") TEXT("1: enable denoiser (if a denoiser plugin is active)\n"), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarPathTracingDenoiserNormalSpace( TEXT("r.PathTracing.Denoiser.NormalSpace"), 0, TEXT("The space normal is in\n") TEXT("0: World space (default)\n") TEXT("1: Camera space. Some denoisers require camera space normal\n"), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarPathTracingDenoiserPrepassVarianceType( TEXT("r.PathTracing.Denoiser.Prepass.VarianceType"), 1, TEXT("Select the per-pixel variance type:\n") TEXT("0: Multiple channel (RGB) variance for radiance\n") TEXT("1: Combined single channel variance for radiance, albedo and normal\n"), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarPathTracingDenoiserPrepassOutputVarianceTexture( TEXT("r.PathTracing.Denoiser.Prepass.OutputVarianceTexture"), 0, TEXT("0: Output variance texture on demand\n") TEXT("1: Always output variance texture to denoisers, or the postprocess material usually used by MRQ\n") ); TAutoConsoleVariable CVarPathTracingDenoiserPrepassRankedLuminanceVariance( TEXT("r.PathTracing.Denoiser.Prepass.RankedLuminanceVariance"), 1, TEXT("Select the luminance type when calculating the variance:\n") TEXT("0: use default luminance to estimate variance.\n") TEXT("1: Use channel ranked luminance when calculating variance.\n"), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarPathTracingSpatialDenoiser( TEXT("r.PathTracing.SpatialDenoiser"), 1, TEXT("Enable spatial denoising of the path traced output\n") TEXT("-1: inherit from PostProcessVolume\n") TEXT("0: disable denoiser\n") TEXT("1: enable denoiser (if a denoiser plugin is active)\n"), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarPathTracingDenoiserName( TEXT("r.PathTracing.Denoiser.Name"), "NNEDenoiser", TEXT("Set the spatial denoiser name. It is the corresponding name registered by a denoiser plugin\n") TEXT("Any registered denoiser should be able to denoise a single frame spatially.\n") ); TAutoConsoleVariable CVarPathTracingTemporalDenoiserName( TEXT("r.PathTracing.TemporalDenoiser.Name"), "NFOR", TEXT("Set the temporal denoiser name. It is the corresponding name registered by a denoiser plugin\n") TEXT("The temporal denoiser usually has better temporal stability when rendering offline.\n") ); TAutoConsoleVariable CVarPathTracingSpatialDenoiserType( TEXT("r.PathTracing.SpatialDenoiser.Type"), 0, TEXT("The type of spatial denoiser\n") TEXT("0: Use spatial denoiser only plugin\n") TEXT("1: Use spatial denoiser plugin that also provides temporal denoising\n"), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarPathTracingTemporalDenoiser( TEXT("r.PathTracing.TemporalDenoiser"), 0, TEXT("Enable temporal denoising of the path traced output\n") TEXT("-1: inherit from PostProcessVolume (TODO when out of experimental phase)\n") TEXT("0: disable denoiser\n") TEXT("1: enable denoiser (if a denoiser plugin is active)\n"), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarPathTracingTemporalDenoiserType( TEXT("r.PathTracing.TemporalDenoiser.Type"), 0, TEXT("The type of temporal denoiser\n") TEXT("0: Use the built-in temporal denoiser\n") TEXT("1: Use the temporal denoiser from plugin\n"), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarPathTracingTemporalDenoiserMotionVectorType( TEXT("r.PathTracing.TemporalDenoiser.MotionVector.Type"), 0, TEXT("The type of motion vecotr estimation algorithm\n") TEXT("0: Built-in motion vector estimator\n") TEXT("1: Motion vector estimator from the plugin\n"), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarPathTracingTemporalDenoiserVisualizeMotionVector( TEXT("r.PathTracing.TemporalDenoiser.VisualizeMotionVector"), 0, TEXT("1: visualize the motion vector compared to the raster motion vector\n"), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarPathTracingTemporalDenoiserEnableSubPixelOffset( TEXT("r.PathTracing.TemporalDenoiser.EnableSubPixelOffset"), 1, TEXT("Enable subpixel offset when merging\n") TEXT("-1: inherit from PostProcessVolume\n") TEXT("0: disable denoiser\n") TEXT("1: enable denoiser (if a denoiser plugin is active)\n"), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarPathTracingTemporalDenoiserKappa( TEXT("r.PathTracing.TemporalDenoiser.kappa"), -1.0f, TEXT("Scaling parameter to determine how fast the history weight falls and the cutting point to zero. Use DeltaE to derive kappa if -1\n"), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarPathTracingTemporalDenoiserEta( TEXT("r.PathTracing.TemporalDenoiser.eta"), 1.0f, TEXT("Eta param. Error distance below this will have max history weight. Use DeltaE to derive Eta if -1\n"), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarPathTracingTemporalDenoiserDeltaE( TEXT("r.PathTracing.TemporalDenoiser.DeltaE"), 5.0f, TEXT("Cut off the history weight to zero at CIE DeltaE for low frequency.\n") TEXT("1.0 :the just noticeable difference (JND),\n") TEXT("2.0 :perceptible for close look\n") TEXT("10.0:Perceptible at a glance ") TEXT("This works as an alternative control instead of kappa. 2 as default."), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarPathTracingTemporalDenoiserDeltaEHighFrequency( TEXT("r.PathTracing.TemporalDenoiser.DeltaE.HighFrequency"), 2.1f, TEXT("Cut off the history weight to zero when using high frequency per pixel difference.\n") TEXT("It should only be enabled when the source image is smooth or denoised."), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarPathTracingTemporalDenoiserAlpha( TEXT("r.PathTracing.TemporalDenoiser.alpha"), 1.0f, TEXT("The weight of history in the exponential mean average\n"), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarPathTracingTemporalDenoiserMode( TEXT("r.PathTracing.TemporalDenoiser.mode"), 1, TEXT("0: disabled \n") TEXT("1: offline rendering only\n") TEXT("2: online rendering (for debug)\n"), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarPathTracingTemporalDenoiserSource( TEXT("r.PathTracing.TemporalDenoiser.source"), 0, TEXT("0: Denoised Radance when possible (Default) \n") TEXT("1: Normal\n") TEXT("2: Albedo\n") TEXT("3: Raw Radiance") TEXT("Otherwise: Feature Fusion (TODO)"), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarPathTracingTemporalDenoiserDenoiseSourceImageFirst( TEXT("r.PathTracing.TemporalDenoiser.DenoiseSourceImageFirst"), 0, TEXT("Denoise the source image with IntelImageDenoisier"), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarPathTracingTemporalDenoiserSubPixelOffsetStartMip( TEXT("r.PathTracing.TemporalDenoiser.SubPixelOffset.StartMip"), 8, TEXT("From 0 to this mip, we will perform subpixel offset"), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarPathTracingTemporalDenoiserMotionOperation( TEXT("r.PathTracing.TemporalDenoiser.MotionOperation"), 1, TEXT("0: use the motion vector directly estimated") TEXT("1: subtract between the motion"), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarPathTracingTemporalDenoiserVisWarp( TEXT("r.PathTracing.TemporalDenoiser.VisWarp"), 0, TEXT("0: disable") TEXT("1: visualize warped source by the motion vector") TEXT("2: weights, warped source, and combined"), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarPathTracingTemporalDenoiserDistanceMetrics( TEXT("r.PathTracing.TemporalDenoiser.DistanceMetrics"), 2, TEXT("0: Luminance based metrics for distance estimation. Color with same luminance will create error in motion and history weights estimation.") TEXT("1: Direct color difference") TEXT("2: Visual color difference based on CIELAB2000 color difference."), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarPathTracingTemporalDenoiserTotalVariation( TEXT("r.PathTracing.TemporalDenoiser.TotalVariation"), 1, TEXT("!=0: Use less history if the total variation is large in a local patch"), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarPathTracingTemporalDenoiserPatchCount( TEXT("r.PathTracing.TemporalDenoiser.PatchCount"), 1, TEXT("The number of similar patches found by Non-Local Mean to use for temporal denoising\n") TEXT("1: default. Accumulae the one with the minimal distance exponentially.") TEXT(">1 && < 16: Use bilaterial filtering to accumulate multiple patches."), ECVF_RenderThreadSafe ); DECLARE_GPU_STAT(PathTracingSpatialTemporalDenoising); } int GetPathTracingDenoiserMode(const FViewInfo& View) { int DenoiserMode = CVarPathTracingDenoiser.GetValueOnRenderThread(); if (DenoiserMode < 0) { DenoiserMode = View.FinalPostProcessSettings.PathTracingEnableDenoiser; } return DenoiserMode; } bool IsPathTracingDenoiserEnabled(const FViewInfo& View) { return GetPathTracingDenoiserMode(View) != 0 && FDenoiserManager::Get().HasDenoiser(); } static bool ShouldDenoiseWithNormalInCameraSpace() { int NormalSpace = CVarPathTracingDenoiserNormalSpace.GetValueOnRenderThread(); return NormalSpace != 0; } enum class ESpatialDenoiserType : int32 { NONE = -1, SPATIAL_DENOISER_PLUGIN, SPATIAL_TEMPORAL_DENOISER_PLUGIN, MAX }; enum class ETemporalDenoisingMode : uint32 { ETDM_DISABLED, ETDM_OFFLINE, ETDM_ONLINE, ETDM_MAX }; enum class ETemporalDenoiserType : int32 { NONE = -1, BUILTIN_TEMPORAL_DENOISER, SPATIAL_TEMPORAL_DENOISER_PLUGIN, MAX }; enum class ETemporalDenoiserMotionVectorType : int32 { NONE = -1, BUILTIN, PLUGIN, MAX }; bool ShouldEnablePathTracingDenoiserRealtimeDebug() { int TemporalDenoiserMode = CVarPathTracingTemporalDenoiserMode.GetValueOnRenderThread(); TemporalDenoiserMode = FMath::Clamp(TemporalDenoiserMode, 0, static_cast(ETemporalDenoisingMode::ETDM_MAX)); ETemporalDenoisingMode Mode = static_cast(TemporalDenoiserMode); return Mode == ETemporalDenoisingMode::ETDM_ONLINE; } static bool ShouldApplySpatialDenoiser() { return CVarPathTracingSpatialDenoiser.GetValueOnRenderThread() != 0; } TArray GetAvailableSpatialDenoiserTypes() { TArray Types; if (FDenoiserManager::Get().HasSpatialTemporalDenoiser()) { Types.Add(ESpatialDenoiserType::SPATIAL_TEMPORAL_DENOISER_PLUGIN); } if (FDenoiserManager::Get().HasSpatialDenoiser()) { Types.Add(ESpatialDenoiserType::SPATIAL_DENOISER_PLUGIN); } if (Types.Num() == 0) { Types.Add(ESpatialDenoiserType::NONE); } return Types; } ESpatialDenoiserType GetSpatialDenoiserType() { int32 Type = CVarPathTracingSpatialDenoiserType.GetValueOnRenderThread(); Type = FMath::Clamp(Type, static_cast(ESpatialDenoiserType::NONE) + 1, static_cast(ESpatialDenoiserType::MAX) - 1); ESpatialDenoiserType DenoiserType = static_cast(Type); if (DenoiserType == ESpatialDenoiserType::SPATIAL_DENOISER_PLUGIN && FDenoiserManager::Get().HasSpatialDenoiser()) { return DenoiserType; } else if (DenoiserType == ESpatialDenoiserType::SPATIAL_TEMPORAL_DENOISER_PLUGIN && FDenoiserManager::Get().HasSpatialTemporalDenoiser()) { return DenoiserType; } else { // Fallback to the one that is available instead of the requested one DenoiserType = GetAvailableSpatialDenoiserTypes()[0]; } return DenoiserType; } static bool ShouldApplyTemporalDenoiser(const FPathTracingSpatialTemporalDenoisingContext& DenoisingContext, const FViewInfo& View) { const bool bApplyTemporalDenoiser = (CVarPathTracingTemporalDenoiser.GetValueOnAnyThread() == 1) && DenoisingContext.LastDenoisedRadianceTexture && DenoisingContext.LastDenoisedRadianceTexture != DenoisingContext.RadianceTexture; return bApplyTemporalDenoiser; } ETemporalDenoiserType GetTemporalDenoiserType() { ESpatialDenoiserType SpatialDenoiserType = GetSpatialDenoiserType(); if ( SpatialDenoiserType == ESpatialDenoiserType::SPATIAL_DENOISER_PLUGIN) { return ETemporalDenoiserType::BUILTIN_TEMPORAL_DENOISER; } else if (SpatialDenoiserType == ESpatialDenoiserType::SPATIAL_TEMPORAL_DENOISER_PLUGIN) { return ETemporalDenoiserType::SPATIAL_TEMPORAL_DENOISER_PLUGIN; } return ETemporalDenoiserType::NONE; } ETemporalDenoiserMotionVectorType GetTemporalDenoiserMotionVectorType() { int32 Type = CVarPathTracingTemporalDenoiserMotionVectorType.GetValueOnRenderThread(); Type = FMath::Clamp(Type, static_cast(ETemporalDenoiserMotionVectorType::NONE) + 1, static_cast(ETemporalDenoiserMotionVectorType::MAX) - 1); return static_cast(Type); } UE::Renderer::Private::IPathTracingDenoiser* GetActiveSpatialDenoiser(bool bMatch = false) { FString DenoiserName = CVarPathTracingDenoiserName.GetValueOnRenderThread(); return FDenoiserManager::Get().GetSpatialDenoiser(DenoiserName, bMatch); } UE::Renderer::Private::IPathTracingSpatialTemporalDenoiser* GetActiveSpatialTemporalDenoiser(bool bMatch = false) { FString DenoiserName = CVarPathTracingTemporalDenoiserName.GetValueOnRenderThread(); return FDenoiserManager::Get().GetSpatialTemporalDenoiser(DenoiserName, bMatch); } ETextureCreateFlags GetExtraTextureCreateFlagsForDenoiser() { ESpatialDenoiserType SpatialDenoiserType = GetSpatialDenoiserType(); ETemporalDenoiserType TemporalDenoiserType = GetTemporalDenoiserType(); ETextureCreateFlags TextureCreateFlags = ETextureCreateFlags::None; bool bThirdPartyDenoiserNeedsTextureCreateExtraFlags = FDenoiserManager::Get().NeedTextureCreateExtraFlags(); if (SpatialDenoiserType == ESpatialDenoiserType::SPATIAL_TEMPORAL_DENOISER_PLUGIN || TemporalDenoiserType == ETemporalDenoiserType::SPATIAL_TEMPORAL_DENOISER_PLUGIN || bThirdPartyDenoiserNeedsTextureCreateExtraFlags) { TextureCreateFlags |= TexCreate_Shared | TexCreate_RenderTargetable; } return TextureCreateFlags; } static float GetHighFrequencyCutoffDeltaE() { return FMath::Max(1.0f, CVarPathTracingTemporalDenoiserDeltaEHighFrequency.GetValueOnRenderThread()); } static bool ShouldRemoveSelfSubpixelOffset(int MipLevel) { bool bRemoveSelfSubpixelOffset = CVarPathTracingTemporalDenoiserMotionOperation.GetValueOnRenderThread() == 1 && MipLevel <= CVarPathTracingTemporalDenoiserSubPixelOffsetStartMip.GetValueOnRenderThread() && CVarPathTracingTemporalDenoiserEnableSubPixelOffset.GetValueOnAnyThread() != 0; return bRemoveSelfSubpixelOffset; } static float GetErrorDistanceBasedOnDeltaE(float DeltaE) { const float NormalizeDeltaEAndEuclideanDistanceToSameMean = 0.0141f; float ErrorDistanceFromDeltaE = log2f(2 + DeltaE * NormalizeDeltaEAndEuclideanDistanceToSameMean); return ErrorDistanceFromDeltaE; } static void GetBlendingFactor(float& Kappa, float& Eta, float& Alpha) { Kappa = CVarPathTracingTemporalDenoiserKappa.GetValueOnRenderThread(); Eta = CVarPathTracingTemporalDenoiserEta.GetValueOnRenderThread(); Alpha = CVarPathTracingTemporalDenoiserAlpha.GetValueOnRenderThread(); if (Eta == -1) { // Eta is set to 1 JND (just noticeable difference). Eta = GetErrorDistanceBasedOnDeltaE(1.0f); } if (Kappa == -1) { // Use DeltaE to determine Kappa in our modified weight formula such that // the history weight falls off to zero when we reaches DeltaE. float DeltaE =CVarPathTracingTemporalDenoiserDeltaE.GetValueOnRenderThread(); DeltaE = FMath::Max(DeltaE, 1.0f + 1e-6f); float FallOffToZeroErrorDistance = GetErrorDistanceBasedOnDeltaE(DeltaE); Kappa = 1 / (FallOffToZeroErrorDistance - Eta); } } static bool ShouldCompilePathTracingDenoiserShadersForProject(EShaderPlatform ShaderPlatform) { static const auto CVarLocalVariablePathTracing = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.PathTracing")); const int PathTracing = CVarLocalVariablePathTracing ? CVarLocalVariablePathTracing->GetValueOnAnyThread() : 0; return ShouldCompileRayTracingShadersForProject(ShaderPlatform) && FDataDrivenShaderPlatformInfo::GetSupportsPathTracing(ShaderPlatform) && PathTracing != 0; } static bool ShouldGenerateVarianceMap() { int32 PathTracingTemporalDenoiserSource = CVarPathTracingTemporalDenoiserSource.GetValueOnRenderThread(); return PathTracingTemporalDenoiserSource < 0 || PathTracingTemporalDenoiserSource > 3; } static bool ShouldVisualizePathTracingVelocityState(const FPathTracingSpatialTemporalDenoisingContext& DenoisingContext) { bool bVisualizeMotionVector = CVarPathTracingTemporalDenoiserVisualizeMotionVector.GetValueOnRenderThread() != 0; return bVisualizeMotionVector && DenoisingContext.MotionVector; } static bool ShouldVisualizeWarping(const FPathTracingSpatialTemporalDenoisingContext& DenoisingContext) { return CVarPathTracingTemporalDenoiserVisWarp.GetValueOnRenderThread() != 0 && DenoisingContext.MotionVector && DenoisingContext.LastDenoisedRadianceTexture; } static int32 GetTemporalAccumulationPatchCount() { return FMath::Clamp(CVarPathTracingTemporalDenoiserPatchCount.GetValueOnRenderThread(), 1, 16); } static bool ShouldUseTotalVariation(uint32 MipLevel) { return CVarPathTracingTemporalDenoiserTotalVariation.GetValueOnRenderThread() != 0.0f && (MipLevel == 0) && CVarPathTracingTemporalDenoiserEnableSubPixelOffset.GetValueOnAnyThread() != 0.0f; } static bool ShouldEnableSubpixelOffset(uint32 MipLevel) { uint32 PixelOffsetStartMip = CVarPathTracingTemporalDenoiserSubPixelOffsetStartMip.GetValueOnRenderThread(); bool bShouldEnableSubpixelOffset = (MipLevel <= PixelOffsetStartMip) && CVarPathTracingTemporalDenoiserEnableSubPixelOffset.GetValueOnAnyThread() != 0; return bShouldEnableSubpixelOffset; } static bool IsVarianceTextureRequiredForCurrentDenoiser() { bool NeedVarianceTexture = false; using UE::Renderer::Private::IPathTracingSpatialTemporalDenoiser; IPathTracingSpatialTemporalDenoiser* ActiveSpatialTemporalDenoiser = nullptr; if (GetSpatialDenoiserType() == ESpatialDenoiserType::SPATIAL_TEMPORAL_DENOISER_PLUGIN) { ActiveSpatialTemporalDenoiser = GetActiveSpatialTemporalDenoiser(); } else { FString DenoiserName = CVarPathTracingDenoiserName.GetValueOnRenderThread(); ActiveSpatialTemporalDenoiser = FDenoiserManager::Get().GetSpatialTemporalDenoiser(DenoiserName, /*bMatch*/true); } if (ActiveSpatialTemporalDenoiser) { NeedVarianceTexture = ActiveSpatialTemporalDenoiser->NeedVarianceTexture(); } return NeedVarianceTexture; } static bool ShouldPrepassOutputVarianceTexture(const FViewInfo& View) { static const auto CVarOutputPostProcessResources = IConsoleManager::Get().FindTConsoleVariableDataBool(TEXT("r.PathTracing.OutputPostProcessResources")); const bool bOutputPostProcessResources = CVarOutputPostProcessResources ? CVarOutputPostProcessResources->GetValueOnRenderThread() : false; // Variance texture will be available if // 1. when we always output so it can be accessed for debug // 2. post process requires and we allow output post process resource in path tracing // 3. the denoiser requires a variance texture. return CVarPathTracingDenoiserPrepassOutputVarianceTexture.GetValueOnRenderThread() != 0 || (bOutputPostProcessResources && IsPathTracingVarianceTextureRequiredInPostProcessMaterial(View)) || IsVarianceTextureRequiredForCurrentDenoiser(); } static constexpr uint32 kMipDiffDelta = 2; static constexpr uint32 kNumberOfPixelShifts = 25; static constexpr uint32 kNumberOfShiftsPerTexture = 4; static constexpr uint32 kNumberOfPasses = 1; static constexpr uint32 kNumberOfShiftsPerPass = kNumberOfShiftsPerTexture * kNumberOfPasses; static constexpr uint32 kNumberOfTexturesPerPass = (kNumberOfPixelShifts + kNumberOfShiftsPerPass - 1) / kNumberOfShiftsPerPass; static constexpr uint32 kThreadSize = 8; static_assert(kNumberOfTexturesPerPass <= 7, "The number of buffers for Pixel correspondence estimation can not exceed seven per pass"); static const TCHAR* DistanceTextureNames[7] = { TEXT("PathTracing.EstimateMotion.Disntace.0"), TEXT("PathTracing.EstimateMotion.Disntace.1"), TEXT("PathTracing.EstimateMotion.Disntace.2"), TEXT("PathTracing.EstimateMotion.Disntace.3"), TEXT("PathTracing.EstimateMotion.Disntace.4"), TEXT("PathTracing.EstimateMotion.Disntace.5"), TEXT("PathTracing.EstimateMotion.Disntace.6"), }; BEGIN_SHADER_PARAMETER_STRUCT(FDenoisingCommonParameters, ) // Constant variable in each pass SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, TargetViewport) SHADER_PARAMETER_SAMPLER(SamplerState, SharedTextureSampler) SHADER_PARAMETER(uint32, PatchCount) SHADER_PARAMETER(uint32, NumOfMips) // Dynamic variable in each subpass. SHADER_PARAMETER(uint32, PatchId) SHADER_PARAMETER(uint32, MipLevel) END_SHADER_PARAMETER_STRUCT(); class FTemporalReprojectionAlignCS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FTemporalReprojectionAlignCS); SHADER_USE_PARAMETER_STRUCT(FTemporalReprojectionAlignCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FDenoisingCommonParameters, DenoisingCommonParameters) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, PixelOffsetTexture) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, SourceTexture) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, TargetTexture) SHADER_PARAMETER_RDG_TEXTURE_UAV_ARRAY(RWTexture2D, RWDistanceTextures, [kNumberOfTexturesPerPass]) END_SHADER_PARAMETER_STRUCT() enum class EDistanceMetrics : uint32 { METRICS_LUMINANCE, METRICS_EUCLIDEAN, METRICS_PERCEPTION, MAX }; class FDistanceMetrics : SHADER_PERMUTATION_ENUM_CLASS("DISTANCE_METRICS", EDistanceMetrics); using FPermutationDomain = TShaderPermutationDomain; static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return ShouldCompilePathTracingDenoiserShadersForProject(Parameters.Platform); } static void ModifyCompilationEnvironment(const FShaderPermutationParameters&, FShaderCompilerEnvironment& OutEnvironment) { OutEnvironment.SetDefine(TEXT("TEMPORAL_REPROJECTION_ALIGN"), 1); OutEnvironment.SetDefine(TEXT("K_NUM_OF_TEXTURES_PER_PASS"), kNumberOfTexturesPerPass); } static EDistanceMetrics GetDistanceMetrics() { uint32 DistanceMetrics = CVarPathTracingTemporalDenoiserDistanceMetrics.GetValueOnRenderThread(); DistanceMetrics = FMath::Clamp(DistanceMetrics, static_cast(EDistanceMetrics::METRICS_LUMINANCE), static_cast(EDistanceMetrics::MAX) - 1); return static_cast(DistanceMetrics); } static const TCHAR* GetEventName(EDistanceMetrics DistanceMetrics) { static const TCHAR* const kEventNames[] = { TEXT("Luminance"), TEXT("Euclidean"), TEXT("Perception") }; static_assert(UE_ARRAY_COUNT(kEventNames) == int32(EDistanceMetrics::MAX), "Fix me"); return kEventNames[static_cast(DistanceMetrics)]; } }; IMPLEMENT_GLOBAL_SHADER(FTemporalReprojectionAlignCS, "/Engine/Private/PathTracing/PathTracingSpatialTemporalDenoising.usf", "ReprojectionAlignCS", SF_Compute); class FTemporalReprojectionBlurCS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FTemporalReprojectionBlurCS); SHADER_USE_PARAMETER_STRUCT(FTemporalReprojectionBlurCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FDenoisingCommonParameters, DenoisingCommonParameters) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, InputTexture) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutputTexture) END_SHADER_PARAMETER_STRUCT() class FDimensionDirectCopy : SHADER_PERMUTATION_BOOL("DIRECT_COPY"); using FPermutationDomain = TShaderPermutationDomain; static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return ShouldCompilePathTracingDenoiserShadersForProject(Parameters.Platform); } static void ModifyCompilationEnvironment(const FShaderPermutationParameters&, FShaderCompilerEnvironment& OutEnvironment) { OutEnvironment.SetDefine(TEXT("TEMPORAL_REPROJECTION_BLUR"), 1); OutEnvironment.SetDefine(TEXT("K_NUM_OF_TEXTURES_PER_PASS"), kNumberOfTexturesPerPass); } }; IMPLEMENT_GLOBAL_SHADER(FTemporalReprojectionBlurCS, "/Engine/Private/PathTracing/PathTracingSpatialTemporalDenoising.usf", "ReprojectionBlurCS", SF_Compute); class FTemporalReprojectionMergeCS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FTemporalReprojectionMergeCS); SHADER_USE_PARAMETER_STRUCT(FTemporalReprojectionMergeCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FDenoisingCommonParameters, DenoisingCommonParameters) SHADER_PARAMETER_RDG_TEXTURE_SRV_ARRAY(Texture2D, DistanceTextures, [kNumberOfTexturesPerPass]) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, PixelOffsetTexture) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWPixelOffsetTexture) END_SHADER_PARAMETER_STRUCT() class FSubpixelOffset : SHADER_PERMUTATION_BOOL("SUBPIXEL_OFFSET"); class FTotalVariation : SHADER_PERMUTATION_BOOL("TOTAL_VARIATION"); using FPermutationDomain = TShaderPermutationDomain; static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return ShouldCompilePathTracingDenoiserShadersForProject(Parameters.Platform); } static void ModifyCompilationEnvironment(const FShaderPermutationParameters&, FShaderCompilerEnvironment& OutEnvironment) { OutEnvironment.SetDefine(TEXT("TEMPORAL_REPROJECTION_MERGE"), 1); OutEnvironment.SetDefine(TEXT("K_NUM_OF_TEXTURES_PER_PASS"), kNumberOfTexturesPerPass); OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads); } }; IMPLEMENT_GLOBAL_SHADER(FTemporalReprojectionMergeCS, "/Engine/Private/PathTracing/PathTracingSpatialTemporalDenoising.usf", "ReprojectionMergeCS", SF_Compute); class FMotionVectorSubtractCS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FMotionVectorSubtractCS); SHADER_USE_PARAMETER_STRUCT(FMotionVectorSubtractCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FDenoisingCommonParameters, DenoisingCommonParameters) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, Minuend) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, Subtrahend) END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return ShouldCompilePathTracingDenoiserShadersForProject(Parameters.Platform); } static void ModifyCompilationEnvironment(const FShaderPermutationParameters&, FShaderCompilerEnvironment& OutEnvironment) { OutEnvironment.SetDefine(TEXT("MOTION_VECTOR_SUBTRACT"), 1); OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads); } }; IMPLEMENT_GLOBAL_SHADER(FMotionVectorSubtractCS, "/Engine/Private/PathTracing/PathTracingSpatialTemporalDenoising.usf", "MotionVectorSubtractCS", SF_Compute); // Warp the history texture, and use local high frequency to adjust the final blending factor. class FTemporalHighFrequencyRejectMapCS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FTemporalHighFrequencyRejectMapCS); SHADER_USE_PARAMETER_STRUCT(FTemporalHighFrequencyRejectMapCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER(float, HighFrequencyCutoffDeltaE) SHADER_PARAMETER_STRUCT_INCLUDE(FDenoisingCommonParameters, DenoisingCommonParameters) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, SourceTexture) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, TargetTexture) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, PixelOffsetTexture) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutputTexture) END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return ShouldCompilePathTracingDenoiserShadersForProject(Parameters.Platform); } static void ModifyCompilationEnvironment(const FShaderPermutationParameters&, FShaderCompilerEnvironment& OutEnvironment) { OutEnvironment.SetDefine(TEXT("TEMPORAL_HIGHFREQ_REJECT"), 1); OutEnvironment.SetDefine(TEXT("K_NUM_OF_TEXTURES_PER_PASS"), kNumberOfTexturesPerPass); } }; IMPLEMENT_GLOBAL_SHADER(FTemporalHighFrequencyRejectMapCS, "/Engine/Private/PathTracing/PathTracingSpatialTemporalDenoising.usf", "TemporalHighFrequencyRejectCS", SF_Compute); enum class EFeatureFusionCategory : uint32 { SOURCE, TARGET, MAX }; static constexpr uint32 kNumOfFeatureFusionCategory = static_cast(EFeatureFusionCategory::MAX); class FTemporalFeatureFusionCS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FTemporalFeatureFusionCS) SHADER_USE_PARAMETER_STRUCT(FTemporalFeatureFusionCS, FGlobalShader) BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_TEXTURE_SRV_ARRAY(Texture2D, AlbedoTexture, [kNumOfFeatureFusionCategory]) SHADER_PARAMETER_RDG_TEXTURE_SRV_ARRAY(Texture2D, NormalTexture, [kNumOfFeatureFusionCategory]) SHADER_PARAMETER_RDG_TEXTURE_SRV_ARRAY(Texture2D, RadianceTexture, [kNumOfFeatureFusionCategory]) SHADER_PARAMETER_RDG_BUFFER_SRV_ARRAY(StructuredBuffer, VarianceMap,[kNumOfFeatureFusionCategory]) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, LastDenoisedRadiance) SHADER_PARAMETER_SAMPLER(SamplerState, SharedTextureSampler) SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, TargetViewport) SHADER_PARAMETER_RDG_TEXTURE_UAV_ARRAY(RWTexture2D, OutputTexture, [kNumOfFeatureFusionCategory]) END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return ShouldCompilePathTracingDenoiserShadersForProject(Parameters.Platform); } static void ModifyCompilationEnvironment(const FShaderPermutationParameters&, FShaderCompilerEnvironment& OutEnvironment) { OutEnvironment.SetDefine(TEXT("TEMPORAL_FEATURE_FUSION"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FTemporalFeatureFusionCS, "/Engine/Private/PathTracing/PathTracingSpatialTemporalDenoising.usf", "TemporalFeatureFusionCS", SF_Compute); class FTemporalResolveCS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FTemporalResolveCS); SHADER_USE_PARAMETER_STRUCT(FTemporalResolveCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER(float, Kappa) SHADER_PARAMETER(float, Eta) SHADER_PARAMETER(float, Alpha) SHADER_PARAMETER(float, HighFrequencyCutoffDeltaE) SHADER_PARAMETER_STRUCT_INCLUDE(FDenoisingCommonParameters, DenoisingCommonParameters) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, SourceTexture) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, TargetTexture) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, PixelOffsetTexture) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, HighFrequencyRejectMap) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutputTexture) END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return ShouldCompilePathTracingDenoiserShadersForProject(Parameters.Platform); } static void ModifyCompilationEnvironment(const FShaderPermutationParameters&, FShaderCompilerEnvironment& OutEnvironment) { OutEnvironment.SetDefine(TEXT("TEMPORAL_REPROJECTION_RESOLVE"), 1); OutEnvironment.SetDefine(TEXT("K_NUM_OF_TEXTURES_PER_PASS"), kNumberOfTexturesPerPass); OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads); } }; IMPLEMENT_GLOBAL_SHADER(FTemporalResolveCS, "/Engine/Private/PathTracing/PathTracingSpatialTemporalDenoising.usf", "TemporalResolveCS", SF_Compute); // Add Spatial denoiser class FSpatialDenoiserCS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FSpatialDenoiserCS); SHADER_USE_PARAMETER_STRUCT(FSpatialDenoiserCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, InputTexture) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, InputNormal) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, InputAlbedo) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutputTexture) SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, TargetViewport) END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return ShouldCompilePathTracingDenoiserShadersForProject(Parameters.Platform); } static void ModifyCompilationEnvironment(const FShaderPermutationParameters&, FShaderCompilerEnvironment& OutEnvironment) { OutEnvironment.SetDefine(TEXT("SPATIAL_DENOISING"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FSpatialDenoiserCS, "/Engine/Private/PathTracing/PathTracingSpatialTemporalDenoising.usf", "SpatialDenoiserCS", SF_Compute); class FConvertWorldSpaceNormalToCameraSpaceCS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FConvertWorldSpaceNormalToCameraSpaceCS); SHADER_USE_PARAMETER_STRUCT(FConvertWorldSpaceNormalToCameraSpaceCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, NormalTexture) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER(float, Width) SHADER_PARAMETER(float, Height) END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return ShouldCompilePathTracingDenoiserShadersForProject(Parameters.Platform); } static void ModifyCompilationEnvironment(const FShaderPermutationParameters&, FShaderCompilerEnvironment& OutEnvironment) { OutEnvironment.SetDefine(TEXT("PREPROCESS_BUFFER"), 1); OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads); } }; IMPLEMENT_GLOBAL_SHADER(FConvertWorldSpaceNormalToCameraSpaceCS, "/Engine/Private/PathTracing/PathTracingSpatialTemporalDenoising.usf", "ConvertWorldSpaceNormalToCameraSpaceCS", SF_Compute); static void ConvertNormalSpace(FRDGBuilder& GraphBuilder, const FViewInfo& View, FRDGTextureRef NormalTexture) { typedef FConvertWorldSpaceNormalToCameraSpaceCS SHADER; SHADER::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->NormalTexture = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(NormalTexture)); PassParameters->ViewUniformBuffer = View.ViewUniformBuffer; PassParameters->Width = NormalTexture->Desc.GetSize().X; PassParameters->Height = NormalTexture->Desc.GetSize().Y; TShaderMapRef ComputeShader(View.ShaderMap); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("ConvertWorldSpaceNormalToCameraSpace %dx%d", View.ViewRect.Width(), View.ViewRect.Height()), ComputeShader, PassParameters, FComputeShaderUtils::GetGroupCount(View.ViewRect.Size(), 8)); } static void PathTracingDenoiserPlugin(FRDGBuilder& GraphBuilder, const FViewInfo& View, int DenoiserMode, FRDGTextureRef InputTexture, FRDGTextureRef AlbedoTexture, FRDGTextureRef NormalTexture, FRDGTextureRef DepthTexture, FRDGTextureRef VarianceTexture, FRDGTextureRef OutputTexture) { FRDGTextureRef ProcessedNormalTexture = NormalTexture; if (ShouldDenoiseWithNormalInCameraSpace()) { const FRDGTextureDesc& Desc = NormalTexture->Desc; ProcessedNormalTexture = GraphBuilder.CreateTexture(Desc, TEXT("PathTracing.CameraSpaceNormal")); { FRHICopyTextureInfo CopyInfo; CopyInfo.Size.X = Desc.Extent.X; CopyInfo.Size.Y = Desc.Extent.Y; CopyInfo.Size.Z = 1; CopyInfo.NumMips = Desc.NumMips; AddCopyTexturePass(GraphBuilder, NormalTexture, ProcessedNormalTexture, CopyInfo); } ConvertNormalSpace(GraphBuilder, View, ProcessedNormalTexture); } /** * First try to use the matched denoiser to denoise. * If the requested denoiser is not available, fallback to the spatial denoiser. */ bool bMatch = true; if (UE::Renderer::Private::IPathTracingDenoiser* ActiveSpatialDenoiser = GetActiveSpatialDenoiser(bMatch); ActiveSpatialDenoiser) { ActiveSpatialDenoiser->AddPasses(GraphBuilder, View, { InputTexture, AlbedoTexture, NormalTexture, OutputTexture }); } else { using UE::Renderer::Private::IPathTracingSpatialTemporalDenoiser; FString DenoiserName = CVarPathTracingDenoiserName.GetValueOnRenderThread(); IPathTracingSpatialTemporalDenoiser* ActiveSpatialTemporalDenoiser = FDenoiserManager::Get().GetSpatialTemporalDenoiser(DenoiserName,bMatch); if (ActiveSpatialTemporalDenoiser) { /** * Force the temporal denoiser to denoise a single frame. */ IPathTracingSpatialTemporalDenoiser::FInputs Inputs; Inputs.ColorTex = InputTexture; Inputs.AlbedoTex = AlbedoTexture; Inputs.NormalTex = NormalTexture; Inputs.DepthTex = DepthTexture; Inputs.VarianceTex = VarianceTexture; // Set flow texture to black Inputs.FlowTex = GraphBuilder.CreateTexture(InputTexture->Desc, TEXT("PathTracing.OpticalFlow")); AddClearRenderTargetPass(GraphBuilder, Inputs.FlowTex, FLinearColor::Black); Inputs.PreviousOutputTex = InputTexture; Inputs.OutputTex = OutputTexture; Inputs.DenoisingFrameId = 0; Inputs.bForceSpatialDenoiserOnly = true; // Force to use spatial denoiser. ActiveSpatialTemporalDenoiser->AddPasses(GraphBuilder, View, Inputs); } else { //fallback to the default spatial denoiser bMatch = false; GetActiveSpatialDenoiser(bMatch)->AddPasses(GraphBuilder, View, { InputTexture, AlbedoTexture, NormalTexture, OutputTexture }); } } } static void PathTracingSpatialTemporalDenoiserPlugin(FRDGBuilder& GraphBuilder, const FViewInfo& View, int DenoiserMode, FRDGTextureRef InputTexture, FRDGTextureRef AlbedoTexture, FRDGTextureRef NormalTexture, FRDGTextureRef DepthTexture, FRDGTextureRef FlowTexture, FRDGTextureRef PreviousOutputFrameTexture, FRDGTextureRef OutputTexture, int DenoisingFrameId, bool bForceSpatialDenoiserOnly, FPathTracingSpatialTemporalDenoisingContext& Context) { check(GetActiveSpatialTemporalDenoiser()); FRDGTextureRef ProcessedNormalTexture = NormalTexture; if (ShouldDenoiseWithNormalInCameraSpace()) { const FRDGTextureDesc& Desc = NormalTexture->Desc; ProcessedNormalTexture = GraphBuilder.CreateTexture(Desc, TEXT("PathTracing.CameraSpaceNormal")); { FRHICopyTextureInfo CopyInfo; CopyInfo.Size.X = Desc.Extent.X; CopyInfo.Size.Y = Desc.Extent.Y; CopyInfo.Size.Z = 1; CopyInfo.NumMips = Desc.NumMips; AddCopyTexturePass(GraphBuilder, NormalTexture, ProcessedNormalTexture, CopyInfo); } ConvertNormalSpace(GraphBuilder, View, ProcessedNormalTexture); } using UE::Renderer::Private::IPathTracingSpatialTemporalDenoiser; IPathTracingSpatialTemporalDenoiser::FInputs Inputs; Inputs.ColorTex = InputTexture; Inputs.AlbedoTex = AlbedoTexture; Inputs.NormalTex = NormalTexture; Inputs.DepthTex = DepthTexture; Inputs.VarianceTex = Context.VarianceTexture; Inputs.OutputTex = OutputTexture; Inputs.FlowTex = FlowTexture; Inputs.PreviousOutputTex = PreviousOutputFrameTexture; Inputs.DenoisingFrameId = DenoisingFrameId; Inputs.bForceSpatialDenoiserOnly = bForceSpatialDenoiserOnly; if (Context.SpatialTemporalDenoiserHistory && Context.SpatialTemporalDenoiserHistory->GetDebugName() == GetActiveSpatialTemporalDenoiser()->GetDebugName()) { Inputs.PrevHistory = Context.SpatialTemporalDenoiserHistory; } IPathTracingSpatialTemporalDenoiser::FOutputs Outputs = GetActiveSpatialTemporalDenoiser()->AddPasses(GraphBuilder, View, Inputs); if (Outputs.NewHistory) { Context.SpatialTemporalDenoiserHistory = Outputs.NewHistory; } } static bool ShouldApplyPreExposureToMotionVectorEstimation() { int32 DenoiserSource = CVarPathTracingTemporalDenoiserSource.GetValueOnRenderThread(); if (DenoiserSource == 0 || DenoiserSource == 3) { return true; } return false; } static void PathTracingMotionVectorPlugin(FRDGBuilder& GraphBuilder, const FViewInfo& View, FRDGTextureRef InputFrameTexture, FRDGTextureRef ReferenceFrameTexture, FRDGTextureRef OutputTexture) { check(GetActiveSpatialTemporalDenoiser()); bool bShouldApplyPreExposure = ShouldApplyPreExposureToMotionVectorEstimation(); float PreExposure = bShouldApplyPreExposure ? View.PreExposure : 1.0f; GetActiveSpatialTemporalDenoiser()->AddMotionVectorPass(GraphBuilder, View, {InputFrameTexture, ReferenceFrameTexture, OutputTexture, PreExposure}); } class FMotionVectorEstimationContext { public: const TCHAR* PixelOffsetTextureNames[2] = { TEXT("PathTracing.EstimateMotion.PixelOffset.Ping"), TEXT("PathTracing.EstimateMotion.PixelOffset.Pong"), }; FRDGTextureRef PixelOffsetTextures[2]; FScreenPassTextureViewport TargetViewport; FRDGTextureDesc TextureWithNMipsDescriptor; FRDGTextureDesc TextureDescriptor; FRDGTextureDesc TextureDescriptorFinalOutput; FRDGTextureRef SourceTexture; FRDGTextureRef TargetTexture; FRDGTextureRef SourceMipTexture; FRDGTextureRef TargetMipTexture; FRHISamplerState* BilinearClampSampler; FRDGTextureRef DistanceTextures[kNumberOfTexturesPerPass]; FDenoisingCommonParameters DenoisingCommonParameters; public: FMotionVectorEstimationContext(FRDGTextureRef InSourceTexture, FRDGTextureRef InTargetTexture): SourceTexture(InSourceTexture), TargetTexture(InTargetTexture){} bool InitContext(FRDGBuilder& GraphBuilder, const FViewInfo& View) { TargetViewport = FScreenPassTextureViewport(View.ViewRect); uint32 NumOfMips = FMath::Min(7u, 1 + FMath::FloorLog2((uint32)TargetViewport.Extent.GetMin())); if (!ensureMsgf(NumOfMips == 7u, TEXT("The image is too small to estimate temporal reprojection NumOfMips(%d)< 7"), NumOfMips)) { return false; } // Set up common denoising parameters for shader { DenoisingCommonParameters.ViewUniformBuffer = View.ViewUniformBuffer; DenoisingCommonParameters.TargetViewport = GetScreenPassTextureViewportParameters(TargetViewport); DenoisingCommonParameters.SharedTextureSampler = TStaticSamplerState::GetRHI(); DenoisingCommonParameters.PatchCount = GetTemporalAccumulationPatchCount(); DenoisingCommonParameters.NumOfMips = NumOfMips; } TextureWithNMipsDescriptor = FRDGTextureDesc::Create2D( TargetViewport.Extent, PF_A32B32G32R32F, FClearValueBinding(), TexCreate_RenderTargetable | TexCreate_ShaderResource | TexCreate_UAV, NumOfMips); TextureDescriptor = FRDGTextureDesc::Create2D( TargetViewport.Extent, PF_A32B32G32R32F, FClearValueBinding(), TexCreate_RenderTargetable | TexCreate_ShaderResource | TexCreate_UAV); TextureDescriptorFinalOutput = FRDGTextureDesc::Create2D( TargetViewport.Extent, PF_A32B32G32R32F, FClearValueBinding(), TexCreate_RenderTargetable | TexCreate_ShaderResource | TexCreate_UAV | GetExtraTextureCreateFlagsForDenoiser()); PixelOffsetTextures[0] = GraphBuilder.CreateTexture(TextureWithNMipsDescriptor, PixelOffsetTextureNames[0]); PixelOffsetTextures[1] = GraphBuilder.CreateTexture(TextureWithNMipsDescriptor, PixelOffsetTextureNames[1]); FRDGTextureDesc SourceDesc = SourceTexture->Desc; FRDGTextureDesc TargetDesc = TargetTexture->Desc; // Copy and create the mipmap for both source and target texture SourceMipTexture = GraphBuilder.CreateTexture(TextureWithNMipsDescriptor, TEXT("PathTracing.EstimateMotion.Source")); TargetMipTexture = GraphBuilder.CreateTexture(TextureWithNMipsDescriptor, TEXT("PathTracing.EstimateMotion.Target")); BilinearClampSampler = TStaticSamplerState::GetRHI(); { FRHICopyTextureInfo CopyInfo; CopyInfo.Size.X = FMath::Min(SourceDesc.Extent.X, TargetDesc.Extent.X); CopyInfo.Size.Y = FMath::Min(SourceDesc.Extent.Y, TargetDesc.Extent.Y); CopyInfo.Size.Z = 1; CopyInfo.NumMips = FMath::Min(SourceDesc.NumMips, TargetDesc.NumMips); AddCopyTexturePass(GraphBuilder, SourceTexture, SourceMipTexture, CopyInfo); FGenerateMips::Execute(GraphBuilder, View.FeatureLevel, SourceMipTexture, BilinearClampSampler); AddCopyTexturePass(GraphBuilder, TargetTexture, TargetMipTexture, CopyInfo); FGenerateMips::Execute(GraphBuilder, View.FeatureLevel, TargetMipTexture, BilinearClampSampler); } for (int TextureId = 0; TextureId < kNumberOfTexturesPerPass; ++TextureId) { DistanceTextures[TextureId] = GraphBuilder.CreateTexture(TextureWithNMipsDescriptor, DistanceTextureNames[TextureId]); } return true; } uint32 GetPatchCount() const { return DenoisingCommonParameters.PatchCount; } uint32 GetNumOfMips() const { return DenoisingCommonParameters.NumOfMips; } void UpdatePatchId(uint32 PatchId) { DenoisingCommonParameters.PatchId = PatchId; } void UpdateMipLevel(uint32 MipLevel) { DenoisingCommonParameters.MipLevel = MipLevel; } uint32 GetMipLevel() const { return DenoisingCommonParameters.MipLevel; } FIntVector GetAlignGroupCount() const { return FComputeShaderUtils::GetGroupCount( FIntVector(TargetViewport.Extent.X, TargetViewport.Extent.Y, kNumberOfTexturesPerPass), FIntVector(kThreadSize, kThreadSize, 1)); } }; static void AlignTexture(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FMotionVectorEstimationContext& Context, FRDGTextureSRVDesc PixelOffsetSRVDesc, FRDGTextureRef SourceMipTexture, FRDGTextureRef TargetMipTexture) { uint32 MipLevel = Context.GetMipLevel(); typedef FTemporalReprojectionAlignCS SHADER; SHADER::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->PixelOffsetTexture = GraphBuilder.CreateSRV(PixelOffsetSRVDesc); PassParameters->SourceTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForMipLevel(SourceMipTexture, MipLevel)); PassParameters->TargetTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForMipLevel(TargetMipTexture, MipLevel)); PassParameters->DenoisingCommonParameters = Context.DenoisingCommonParameters; for (int TextureId = 0; TextureId < kNumberOfTexturesPerPass; ++TextureId) { PassParameters->RWDistanceTextures[TextureId] = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(Context.DistanceTextures[TextureId], MipLevel)); } SHADER::EDistanceMetrics DistanceMetrics = SHADER::GetDistanceMetrics(); SHADER::FPermutationDomain ComputeShaderPermutationVector; ComputeShaderPermutationVector.Set(DistanceMetrics); TShaderMapRef ComputeShader(View.ShaderMap,ComputeShaderPermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("PathTracing::Denoising::Align %dx%d (%s)", Context.TargetViewport.Extent.X, Context.TargetViewport.Extent.Y, SHADER::GetEventName(DistanceMetrics)), ComputeShader, PassParameters, Context.GetAlignGroupCount()); } // Determine the image blur size based on the current mip level and image size. // TODO: scale blur size based on viewport size static int32 GetBlurSize(int32 MipLevel, FIntPoint ViewportExtent) { int BlurSize = 1; switch (MipLevel) { case 0: BlurSize = 3; break; case 2: BlurSize = 2; break; default: BlurSize = 1; break; }; return BlurSize; } static void BlurDistanceMetrics(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FMotionVectorEstimationContext& Context) { struct FReprojectionBulrPassInfo { FReprojectionBulrPassInfo(const TCHAR* InName, FRDGTextureRef InInput, FRDGTextureRef InOutput) :Name(InName), Input(InInput), Output(InOutput) {} const TCHAR* Name; FRDGTextureRef Input; FRDGTextureRef Output; }; FRDGTextureRef TempBuffer = GraphBuilder.CreateTexture(Context.TextureWithNMipsDescriptor, TEXT("PathTracing.EstimateMotion.TempBuffer")); uint32 MipLevel = Context.GetMipLevel(); int BlurSize = GetBlurSize(MipLevel, Context.TargetViewport.Extent); for (int TextureId = 0; TextureId < kNumberOfTexturesPerPass; ++TextureId) { const int NumOfBlurPass = 2; const FReprojectionBulrPassInfo BlurPassInfos[NumOfBlurPass] = { {TEXT("PathTracing::Denoising::Blur0"), Context.DistanceTextures[TextureId], TempBuffer}, {TEXT("PathTracing::Denoising::Blur1"), TempBuffer, Context.DistanceTextures[TextureId]} }; for (int i = 0; i < BlurSize; ++i) { for (int PassIndex = 0; PassIndex < NumOfBlurPass; ++PassIndex) { FReprojectionBulrPassInfo PassInfo = BlurPassInfos[PassIndex]; FRDGTextureSRVDesc InputSRVDesc = FRDGTextureSRVDesc::CreateForMipLevel(PassInfo.Input, MipLevel); FRDGTextureUAVDesc OutputUAVDesc(PassInfo.Output, MipLevel); typedef FTemporalReprojectionBlurCS SHADER; SHADER::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->InputTexture = GraphBuilder.CreateSRV(InputSRVDesc); PassParameters->OutputTexture = GraphBuilder.CreateUAV(OutputUAVDesc); PassParameters->DenoisingCommonParameters = Context.DenoisingCommonParameters; SHADER::FPermutationDomain ComputeShaderPermutationVector; ComputeShaderPermutationVector.Set(false); TShaderMapRef ComputeShader(View.ShaderMap, ComputeShaderPermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("%s %dx%d Mip=%d (DistPatch %d/%d)", BlurPassInfos[PassIndex].Name, Context.TargetViewport.Extent.X, Context.TargetViewport.Extent.Y, MipLevel, TextureId + 1, kNumberOfTexturesPerPass), ComputeShader, PassParameters, FComputeShaderUtils::GetGroupCount(Context.TargetViewport.Extent, 8)); } } } } static void MergeDistanceMetrics(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FMotionVectorEstimationContext& Context, const FRDGTextureSRVDesc& LastPixelOffsetSRVDesc, FRDGTextureRef TargetPixelOffsetTexture) { uint32 MipLevel = Context.GetMipLevel(); typedef FTemporalReprojectionMergeCS SHADER; SHADER::FParameters* PassParameters = GraphBuilder.AllocParameters(); for (int TextureId = 0; TextureId < kNumberOfTexturesPerPass; ++TextureId) { PassParameters->DistanceTextures[TextureId] = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForMipLevel(Context.DistanceTextures[TextureId], MipLevel)); } PassParameters->PixelOffsetTexture = GraphBuilder.CreateSRV(LastPixelOffsetSRVDesc); PassParameters->RWPixelOffsetTexture = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(TargetPixelOffsetTexture, MipLevel)); PassParameters->DenoisingCommonParameters = Context.DenoisingCommonParameters; bool bShouldUseTotalVariation = ShouldUseTotalVariation(MipLevel); bool bShouldEnableSubpixelOffset = ShouldEnableSubpixelOffset(MipLevel); SHADER::FPermutationDomain ComputeShaderPermutationVector; ComputeShaderPermutationVector.Set(bShouldEnableSubpixelOffset); ComputeShaderPermutationVector.Set(bShouldUseTotalVariation); TShaderMapRef ComputeShader(View.ShaderMap, ComputeShaderPermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("PathTracing::Denoising::Merge %dx%d", Context.TargetViewport.Extent.X, Context.TargetViewport.Extent.Y), ComputeShader, PassParameters, FComputeShaderUtils::GetGroupCount(Context.TargetViewport.Extent, 8)); } // Estimate the motion vector (pixel correspondence offset) static FRDGTextureRef EstimateMotionVector(FRDGBuilder& GraphBuilder, const FViewInfo& View, FMotionVectorEstimationContext& Context) { int PixelOffsetIndex = 0; int NumOfMips = Context.GetNumOfMips(); for (int MipLevel = NumOfMips - 1; MipLevel >= 0; MipLevel -= kMipDiffDelta) { Context.UpdateMipLevel(MipLevel); for (int Pass = 0; Pass < kNumberOfPasses; ++Pass) { const bool UseBlackDummpy = ((MipLevel == (NumOfMips - 1)) && Pass == 0); // Write the calibration subpixel offset to the source so that we can remove it. FRDGTextureSRVDesc SelfLastPixelOffsetSRVDesk = FRDGTextureSRVDesc::CreateForMipLevel( GraphBuilder.RegisterExternalTexture(GSystemTextures.BlackDummy, Context.PixelOffsetTextureNames[1]), 0); FRDGTextureRef SelfTargetPixelOffsetTexture = Context.PixelOffsetTextures[PixelOffsetIndex % 2]; FRDGTextureSRVDesc PixelOffsetSRVDesc = FRDGTextureSRVDesc::CreateForMipLevel( UseBlackDummpy ? GraphBuilder.RegisterExternalTexture(GSystemTextures.BlackDummy, Context.PixelOffsetTextureNames[0]) : Context.PixelOffsetTextures[PixelOffsetIndex % 2], UseBlackDummpy ? 0 : (MipLevel + kMipDiffDelta)); FRDGTextureRef TargetPixelOffsetTexture = Context.PixelOffsetTextures[(++PixelOffsetIndex) % 2]; // Calculate the subpixel offset for the source image, and write to the unused texture in the source mipmap if(ShouldRemoveSelfSubpixelOffset(MipLevel)) { AlignTexture(GraphBuilder, View, Context, SelfLastPixelOffsetSRVDesk, Context.SourceMipTexture, Context.SourceMipTexture); BlurDistanceMetrics(GraphBuilder, View, Context); // Merge: Find the smallest distance within [-2,2]^2 MergeDistanceMetrics(GraphBuilder, View, Context, SelfLastPixelOffsetSRVDesk, SelfTargetPixelOffsetTexture); } // Calculate the subpixel offset between the source and the target { AlignTexture(GraphBuilder, View, Context, PixelOffsetSRVDesc, Context.SourceMipTexture, Context.TargetMipTexture); BlurDistanceMetrics(GraphBuilder, View, Context); // Merge: Find the smallest distance within [-2,2]^2 MergeDistanceMetrics(GraphBuilder, View, Context, PixelOffsetSRVDesc, TargetPixelOffsetTexture); } // Subtract the distance from source to target if (ShouldRemoveSelfSubpixelOffset(MipLevel)) { typedef FMotionVectorSubtractCS SHADER; SHADER::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->Minuend = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(TargetPixelOffsetTexture, MipLevel)); PassParameters->Subtrahend = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForMipLevel(SelfTargetPixelOffsetTexture, MipLevel)); PassParameters->DenoisingCommonParameters = Context.DenoisingCommonParameters; TShaderMapRef ComputeShader(View.ShaderMap); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("PathTracing::Denoising::MotionVectorDiff %dx%d (Mip=%d)", Context.TargetViewport.Extent.X, Context.TargetViewport.Extent.Y, MipLevel), ComputeShader, PassParameters, FComputeShaderUtils::GetGroupCount(Context.TargetViewport.Extent, 8)); } } } return Context.PixelOffsetTextures[PixelOffsetIndex % 2]; } // combine albedo, normal, and radiance into a single feature texture static void FuseTemporalFeature(FRDGBuilder& GraphBuilder, const FViewInfo& View, FPathTracingSpatialTemporalDenoisingContext& DenoisingContext, FRDGTextureRef& OutSourceTexture, FRDGTextureRef& OutTargetTexture) { const FScreenPassTextureViewport TargetViewport(View.ViewRect); const FScreenPassTextureViewportParameters TargetViewportParameters = GetScreenPassTextureViewportParameters(TargetViewport); const FRDGTextureDesc TextureDescriptor = FRDGTextureDesc::Create2D( TargetViewport.Extent, PF_A32B32G32R32F, FClearValueBinding(), TexCreate_RenderTargetable | TexCreate_ShaderResource | TexCreate_UAV | GetExtraTextureCreateFlagsForDenoiser()); OutSourceTexture = GraphBuilder.CreateTexture(TextureDescriptor, TEXT("PathTracing.Denoising.Feature.Source")); OutTargetTexture = GraphBuilder.CreateTexture(TextureDescriptor, TEXT("PathTracing.Denoising.Feature.Target")); typedef FTemporalFeatureFusionCS SHADER; SHADER::FParameters* PassParameters = GraphBuilder.AllocParameters(); { PassParameters->AlbedoTexture[0] = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::Create(DenoisingContext.LastAlbedoTexture)); PassParameters->AlbedoTexture[1] = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::Create(DenoisingContext.AlbedoTexture)); PassParameters->NormalTexture[0] = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::Create(DenoisingContext.LastNormalTexture)); PassParameters->NormalTexture[1] = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::Create(DenoisingContext.NormalTexture)); PassParameters->RadianceTexture[0] = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::Create(DenoisingContext.LastRadianceTexture)); PassParameters->RadianceTexture[1] = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::Create(DenoisingContext.RadianceTexture)); PassParameters->VarianceMap[0] = GraphBuilder.CreateSRV(DenoisingContext.LastVarianceBuffer ? DenoisingContext.LastVarianceBuffer : DenoisingContext.VarianceBuffer,EPixelFormat::PF_R32_FLOAT); PassParameters->VarianceMap[1] = GraphBuilder.CreateSRV(DenoisingContext.VarianceBuffer, EPixelFormat::PF_R32_FLOAT); PassParameters->OutputTexture[0] = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(OutSourceTexture)); PassParameters->OutputTexture[1] = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(OutTargetTexture)); PassParameters->LastDenoisedRadiance = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::Create(DenoisingContext.LastDenoisedRadianceTexture)); PassParameters->SharedTextureSampler = TStaticSamplerState::GetRHI(); PassParameters->TargetViewport = TargetViewportParameters; } TShaderMapRef ComputeShader(View.ShaderMap); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("PathTracing::Denoising::FeatureFusion %dx%d", TargetViewport.Extent.X, TargetViewport.Extent.Y), ComputeShader, PassParameters, FComputeShaderUtils::GetGroupCount(TargetViewport.Extent, 8)); } static bool SelectMotionSourceAndTargetTextures( FRDGBuilder& GraphBuilder, const FViewInfo& View, FRDGTextureRef InSourceTexture, FRDGTextureRef InTargetTexture, FPathTracingSpatialTemporalDenoisingContext& DenoisingContext, FRDGTextureRef& MotionEstimationSourceTexture, FRDGTextureRef& MotionEstimationTargetTexture) { int32 MotionSource = CVarPathTracingTemporalDenoiserSource.GetValueOnAnyThread(); switch (MotionSource) { case 3: MotionEstimationSourceTexture = DenoisingContext.LastRadianceTexture; MotionEstimationTargetTexture = DenoisingContext.RadianceTexture; break; case 2: MotionEstimationSourceTexture = DenoisingContext.LastAlbedoTexture; MotionEstimationTargetTexture = DenoisingContext.AlbedoTexture; break; case 1: MotionEstimationSourceTexture = DenoisingContext.LastNormalTexture; MotionEstimationTargetTexture = DenoisingContext.NormalTexture; break; case 0: MotionEstimationSourceTexture = InSourceTexture; MotionEstimationTargetTexture = InTargetTexture; break; default: FuseTemporalFeature(GraphBuilder, View, DenoisingContext, MotionEstimationSourceTexture, MotionEstimationTargetTexture); break; } return MotionEstimationSourceTexture->Desc.Extent == MotionEstimationTargetTexture->Desc.Extent; } // Estimate the motion vector from source to target static FRDGTextureRef TemporalReprojectionWithoutMotionVector(FRDGBuilder& GraphBuilder, const FViewInfo& View, FRDGTextureRef InSourceTexture, FRDGTextureRef InTargetTexture, FRDGTextureRef MotionEstimationSourceTexture, FRDGTextureRef MotionEstimationTargetTexture, FPathTracingSpatialTemporalDenoisingContext& DenoisingContext) { FMotionVectorEstimationContext EstimationContext(MotionEstimationSourceTexture, MotionEstimationTargetTexture); if (!EstimationContext.InitContext(GraphBuilder, View)) { return nullptr; } FRDGTextureRef HighFrequencyRejectMap = GraphBuilder.CreateTexture(EstimationContext.TextureDescriptor, TEXT("PathTracing.EstimateMotion.HighFrequencyRejectMap")); FRDGTextureRef FinalAccumulation = GraphBuilder.CreateTexture(EstimationContext.TextureDescriptorFinalOutput, TEXT("PathTracing.EstimateMotion.FinalAccumulation")); int PatchCount = EstimationContext.GetPatchCount(); float HighFrequencyCutoffDeltaE = GetHighFrequencyCutoffDeltaE(); FRDGTextureRef TempAccumulation = nullptr; if (PatchCount > 1) { TempAccumulation = GraphBuilder.CreateTexture(EstimationContext.TextureDescriptor, TEXT("PathTracing.EstimateMotion.TempAccumulation")); } // Perform accumulation on Miplevel 0 const uint32 AccumulationMipLevel = 0; float Kappa, Eta, Alpha; GetBlendingFactor(Kappa, Eta, Alpha); for (int PatchId = 0; PatchId < PatchCount; ++PatchId) { EstimationContext.UpdatePatchId(PatchId); FRDGTextureRef PixelOffsetTexture = EstimateMotionVector( GraphBuilder, View, EstimationContext); DenoisingContext.MotionVector = PixelOffsetTexture; // Used for debugging // Final resolve, accumulate the temporal information based on the pixel offset texture FRDGTextureRef BlendSourceTexture = InSourceTexture; FRDGTextureRef BlendTargetTexture = (PatchId == 0) ? InTargetTexture : TempAccumulation; FRDGTextureRef BlendFinalTexture = (PatchId + 1 == PatchCount)? FinalAccumulation: TempAccumulation; EstimationContext.UpdateMipLevel(AccumulationMipLevel); // Warp the source image so that we can adjust the blending weight based on high frequency information. // E.g., if the source and target is the albedo. We can use visual perception difference of 1dE, 2dE or 10dE { typedef FTemporalHighFrequencyRejectMapCS SHADER; SHADER::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->PixelOffsetTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForMipLevel(PixelOffsetTexture, AccumulationMipLevel)); PassParameters->SourceTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForMipLevel(BlendSourceTexture, AccumulationMipLevel)); PassParameters->TargetTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForMipLevel(InTargetTexture, AccumulationMipLevel)); PassParameters->OutputTexture = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(HighFrequencyRejectMap)); // the difference of the optimal shift. PassParameters->DenoisingCommonParameters = EstimationContext.DenoisingCommonParameters; PassParameters->HighFrequencyCutoffDeltaE = HighFrequencyCutoffDeltaE; TShaderMapRef ComputeShader(View.ShaderMap); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("PathTracing::Denoising::RejectionMap %dx%d", EstimationContext.TargetViewport.Extent.X, EstimationContext.TargetViewport.Extent.Y), ComputeShader, PassParameters, FComputeShaderUtils::GetGroupCount(EstimationContext.TargetViewport.Extent, 8)); } { typedef FTemporalResolveCS SHADER; SHADER::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->PixelOffsetTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForMipLevel(PixelOffsetTexture, AccumulationMipLevel)); PassParameters->SourceTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForMipLevel(BlendSourceTexture, AccumulationMipLevel)); PassParameters->TargetTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForMipLevel(BlendTargetTexture, AccumulationMipLevel)); PassParameters->HighFrequencyRejectMap = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForMipLevel(HighFrequencyRejectMap, AccumulationMipLevel)); PassParameters->OutputTexture = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(BlendFinalTexture)); // the difference of the optimal shift. PassParameters->DenoisingCommonParameters = EstimationContext.DenoisingCommonParameters; PassParameters->HighFrequencyCutoffDeltaE = HighFrequencyCutoffDeltaE; PassParameters->Alpha = Alpha; PassParameters->Kappa = Kappa; PassParameters->Eta = Eta; TShaderMapRef ComputeShader(View.ShaderMap); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("PathTracing::Denoising::TemporalResolve %dx%d", EstimationContext.TargetViewport.Extent.X, EstimationContext.TargetViewport.Extent.Y), ComputeShader, PassParameters, FComputeShaderUtils::GetGroupCount(EstimationContext.TargetViewport.Extent, 8)); } } return FinalAccumulation; } struct FPixelMaterialLightingFingerprint { FVector4 Mean; FVector4 Var; }; class FTemporalPrepassCS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FTemporalPrepassCS); SHADER_USE_PARAMETER_STRUCT(FTemporalPrepassCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, InputTexture) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, AlbedoTexture) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, NormalTexture) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWVarianceMap) SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, TargetViewport) SHADER_PARAMETER(int, Iteration) END_SHADER_PARAMETER_STRUCT() enum class EVarianceType : uint32 { RadianceMultiChannel = 0, RadianceAlbedoNormalSingleChannel, MAX }; class FPrepassPhase : SHADER_PERMUTATION_BOOL("PREPASS_PHASE"); // 0: initialize, 1: update class FVarianceType : SHADER_PERMUTATION_ENUM_CLASS("VARIANCE_TYPE", EVarianceType); class FRankedLuminanceVariance : SHADER_PERMUTATION_BOOL("RANKED_LUMINANCE_VARIANCE"); using FPermutationDomain = TShaderPermutationDomain; static bool UseRankedLuminanceVariance() { return CVarPathTracingDenoiserPrepassRankedLuminanceVariance.GetValueOnRenderThread() != 0; } static EVarianceType GetVarianceType() { return static_cast( FMath::Clamp(CVarPathTracingDenoiserPrepassVarianceType.GetValueOnRenderThread(), 0, static_cast(EVarianceType::MAX) - 1)); } static const TCHAR* GetEventName(EVarianceType VarianceType) { static const TCHAR* const kEventName[] = { TEXT("Radiance"), TEXT("Rad,Albedo,Norm") }; static_assert(UE_ARRAY_COUNT(kEventName) == int32(EVarianceType::MAX), "Fix me"); return kEventName[int32(VarianceType)]; } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return ShouldCompilePathTracingDenoiserShadersForProject(Parameters.Platform); } static void ModifyCompilationEnvironment(const FShaderPermutationParameters&, FShaderCompilerEnvironment& OutEnvironment) { OutEnvironment.SetDefine(TEXT("TEMPORAL_PREPASS"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FTemporalPrepassCS, "/Engine/Private/PathTracing/PathTracingSpatialTemporalDenoising.usf", "TemporalPrepassCS", SF_Compute); class FPrepassGenerateTextureCS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FPrepassGenerateTextureCS); SHADER_USE_PARAMETER_STRUCT(FPrepassGenerateTextureCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, VarianceMap) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutputTexture) SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, TargetViewport) SHADER_PARAMETER(int32, Iteration) END_SHADER_PARAMETER_STRUCT() class FVarianceType : SHADER_PERMUTATION_ENUM_CLASS("VARIANCE_TYPE", FTemporalPrepassCS::EVarianceType); using FPermutationDomain = TShaderPermutationDomain; static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return ShouldCompilePathTracingDenoiserShadersForProject(Parameters.Platform); } static void ModifyCompilationEnvironment(const FShaderPermutationParameters&, FShaderCompilerEnvironment& OutEnvironment) { OutEnvironment.SetDefine(TEXT("PREPASS_GENERATE_TEXTURE"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FPrepassGenerateTextureCS, "/Engine/Private/PathTracing/PathTracingSpatialTemporalDenoising.usf", "PrepassGenerateTextureCS", SF_Compute); void PathTracingSpatialTemporalDenoisingPrePass(FRDGBuilder& GraphBuilder, const FViewInfo& View, int IterationNumber, int MaxSPP, FPathTracingSpatialTemporalDenoisingContext& SpatialTemporalDenoisingContext) { bool bShouldPrepassOutputVarianceTexture = ShouldPrepassOutputVarianceTexture(View); bool bShouldGenerateVarianceMap = ShouldGenerateVarianceMap() || bShouldPrepassOutputVarianceTexture; if (bShouldGenerateVarianceMap) { bool bNeedToUpdateVariance = (IterationNumber < MaxSPP); const FScreenPassTextureViewport TargetViewport(View.ViewRect); const FScreenPassTextureViewportParameters TargetViewportParameters = GetScreenPassTextureViewportParameters(TargetViewport); if (bNeedToUpdateVariance) { if (!SpatialTemporalDenoisingContext.VarianceBuffer) { SpatialTemporalDenoisingContext.VarianceBuffer = GraphBuilder.CreateBuffer( FRDGBufferDesc::CreateStructuredDesc(sizeof(float) * 8, View.ViewRect.Area()), TEXT("PathTracing.VarianceBuffer")); } typedef FTemporalPrepassCS SHADER; SHADER::FParameters* PassParameters = GraphBuilder.AllocParameters(); { PassParameters->InputTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::Create(SpatialTemporalDenoisingContext.RadianceTexture)); PassParameters->AlbedoTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::Create(SpatialTemporalDenoisingContext.AlbedoTexture)); PassParameters->NormalTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::Create(SpatialTemporalDenoisingContext.NormalTexture)); PassParameters->RWVarianceMap = GraphBuilder.CreateUAV(SpatialTemporalDenoisingContext.VarianceBuffer, EPixelFormat::PF_R32_FLOAT); PassParameters->TargetViewport = TargetViewportParameters; PassParameters->Iteration = IterationNumber; } bool bUpdateVarianceMapPhase = (IterationNumber > 0); SHADER::FPermutationDomain ComputeShaderPermutationVector; ComputeShaderPermutationVector.Set(bUpdateVarianceMapPhase); ComputeShaderPermutationVector.Set(SHADER::GetVarianceType()); ComputeShaderPermutationVector.Set(SHADER::UseRankedLuminanceVariance()); TShaderMapRef ComputeShader(View.ShaderMap, ComputeShaderPermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("PathTracing::Denoising::Prepass(%s %dx%d)", SHADER::GetEventName(SHADER::GetVarianceType()), TargetViewport.Extent.X, TargetViewport.Extent.Y), ComputeShader, PassParameters, FComputeShaderUtils::GetGroupCount(TargetViewport.Extent, 8)); } else { SpatialTemporalDenoisingContext.VarianceBuffer = SpatialTemporalDenoisingContext.LastVarianceBuffer; } FRDGBufferRef VarianceBuffer = SpatialTemporalDenoisingContext.VarianceBuffer; if (bShouldPrepassOutputVarianceTexture && VarianceBuffer) { const FRDGTextureDesc TextureDescriptor = FRDGTextureDesc::Create2D( TargetViewport.Extent, PF_A32B32G32R32F, FClearValueBinding(), TexCreate_RenderTargetable | TexCreate_ShaderResource | TexCreate_UAV | GetExtraTextureCreateFlagsForDenoiser()); if (!SpatialTemporalDenoisingContext.VarianceTexture) { SpatialTemporalDenoisingContext.VarianceTexture = GraphBuilder.CreateTexture(TextureDescriptor, TEXT("PathTracing.VarianceTexture")); } typedef FPrepassGenerateTextureCS SHADER; SHADER::FParameters* PassParameters = GraphBuilder.AllocParameters(); { PassParameters->OutputTexture = GraphBuilder.CreateUAV(SpatialTemporalDenoisingContext.VarianceTexture); PassParameters->VarianceMap = GraphBuilder.CreateSRV(VarianceBuffer, EPixelFormat::PF_R32_FLOAT); PassParameters->TargetViewport = TargetViewportParameters; PassParameters->Iteration = FMath::Min(IterationNumber, MaxSPP - 1); } SHADER::FPermutationDomain ComputeShaderPermutationVector; ComputeShaderPermutationVector.Set(FTemporalPrepassCS::GetVarianceType()); TShaderMapRef ComputeShader(View.ShaderMap, ComputeShaderPermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("PathTracing::Denoising::Prepass::Texture(Var[%s] %dx%d)", FTemporalPrepassCS::GetEventName(FTemporalPrepassCS::GetVarianceType()), TargetViewport.Extent.X, TargetViewport.Extent.Y), ComputeShader, PassParameters, FComputeShaderUtils::GetGroupCount(TargetViewport.Extent, 8)); } } } void PathTracingSpatialTemporalDenoising(FRDGBuilder& GraphBuilder, const FViewInfo& View, int DenoiserMode, FRDGTexture*& SpatialTemporalDenoisedTexture, FPathTracingSpatialTemporalDenoisingContext& SpatialTemporalDenoisingContext) { RDG_EVENT_SCOPE_STAT(GraphBuilder, PathTracingSpatialTemporalDenoising, "PathTracingSpatialTemporalDenoising"); RDG_GPU_STAT_SCOPE(GraphBuilder, PathTracingSpatialTemporalDenoising); FRDGTextureDesc RadianceTextureDesc = SpatialTemporalDenoisingContext.RadianceTexture->Desc; const ESpatialDenoiserType SpatialDenoiserType = GetSpatialDenoiserType(); const ETemporalDenoiserType TemporalDenoiserType = GetTemporalDenoiserType(); const bool bApplySpatialDenoiser = ShouldApplySpatialDenoiser(); const bool bApplyTemporalDenoiser = ShouldApplyTemporalDenoiser(SpatialTemporalDenoisingContext, View); FRDGTextureRef TargetTexture = SpatialTemporalDenoisingContext.RadianceTexture; FRDGTextureRef SourceTexture = SpatialTemporalDenoisingContext.LastDenoisedRadianceTexture; FRDGTextureRef TemporalDenoisedTexture = nullptr; if (SpatialDenoiserType == ESpatialDenoiserType::SPATIAL_DENOISER_PLUGIN) { if (bApplySpatialDenoiser) { TargetTexture = GraphBuilder.CreateTexture(RadianceTextureDesc, TEXT("PathTracer.SpatialDenoiser.Output")); PathTracingDenoiserPlugin( GraphBuilder, View, DenoiserMode, SpatialTemporalDenoisingContext.RadianceTexture, SpatialTemporalDenoisingContext.AlbedoTexture, SpatialTemporalDenoisingContext.NormalTexture, SpatialTemporalDenoisingContext.DepthTexture, SpatialTemporalDenoisingContext.VarianceTexture, TargetTexture); } if (bApplyTemporalDenoiser) { // Image space temporal and spatial denoising. // // Select motion source and target for temporal denoising FRDGTextureRef MotionEstimationSourceTexture = nullptr; FRDGTextureRef MotionEstimationTargetTexture = nullptr; bool IsSourceTargetDimensionMatch = SelectMotionSourceAndTargetTextures( GraphBuilder, View, SourceTexture, TargetTexture, SpatialTemporalDenoisingContext, MotionEstimationSourceTexture, MotionEstimationTargetTexture); if (IsSourceTargetDimensionMatch) { UE_LOG(LogPathTracingDenoising, Log, TEXT("Using temporal denoising for frame %i"), SpatialTemporalDenoisingContext.FrameIndex); if (TemporalDenoiserType == ETemporalDenoiserType::BUILTIN_TEMPORAL_DENOISER) { TemporalDenoisedTexture = TemporalReprojectionWithoutMotionVector(GraphBuilder, View, SourceTexture, TargetTexture, MotionEstimationSourceTexture, MotionEstimationTargetTexture, SpatialTemporalDenoisingContext); } else { // not supported } } } } else if (SpatialDenoiserType == ESpatialDenoiserType::SPATIAL_TEMPORAL_DENOISER_PLUGIN) { FRDGTextureRef MotionTexture = nullptr; bool bIsInitialFrame = true; MotionTexture = GraphBuilder.CreateTexture(RadianceTextureDesc, TEXT("PathTracing.EstimateMotion.OpticalFlow")); if (bApplyTemporalDenoiser) { // Select motion source and target for temporal denoising FRDGTextureRef MotionEstimationSourceTexture = nullptr; FRDGTextureRef MotionEstimationTargetTexture = nullptr; bool IsSourceTargetDimensionMatch = SelectMotionSourceAndTargetTextures( GraphBuilder, View, SourceTexture, TargetTexture, SpatialTemporalDenoisingContext, MotionEstimationSourceTexture, MotionEstimationTargetTexture); if (IsSourceTargetDimensionMatch) { if (TemporalDenoiserType == ETemporalDenoiserType::SPATIAL_TEMPORAL_DENOISER_PLUGIN) { FScreenPassTextureViewport TargetViewport = FScreenPassTextureViewport(View.ViewRect); PathTracingMotionVectorPlugin( GraphBuilder, View, MotionEstimationSourceTexture, MotionEstimationTargetTexture, MotionTexture); bIsInitialFrame = false; } } } else { FLinearColor ClearColor = FLinearColor::Black; AddClearRenderTargetPass(GraphBuilder, MotionTexture, ClearColor); } SpatialTemporalDenoisingContext.MotionVector = MotionTexture; TemporalDenoisedTexture = GraphBuilder.CreateTexture(RadianceTextureDesc, TEXT("PathTracing.EstimateMotion.FinalAccumulation")); PathTracingSpatialTemporalDenoiserPlugin( GraphBuilder, View, DenoiserMode, TargetTexture, SpatialTemporalDenoisingContext.AlbedoTexture, SpatialTemporalDenoisingContext.NormalTexture, SpatialTemporalDenoisingContext.DepthTexture, MotionTexture, SourceTexture, TemporalDenoisedTexture, SpatialTemporalDenoisingContext.FrameIndex, bIsInitialFrame, SpatialTemporalDenoisingContext); // zero frame will denoise without temporal } SpatialTemporalDenoisedTexture = TemporalDenoisedTexture ? TemporalDenoisedTexture : TargetTexture; } class FVisualizePathTracingMotionVectorPS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FVisualizePathTracingMotionVectorPS); SHADER_USE_PARAMETER_STRUCT(FVisualizePathTracingMotionVectorPS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, TemporalDenoisingMotionVector) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, RasterMotionVector) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, DenoisedTexture) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return ShouldCompilePathTracingDenoiserShadersForProject(Parameters.Platform); } static void ModifyCompilationEnvironment(const FShaderPermutationParameters&, FShaderCompilerEnvironment& OutEnvironment) { OutEnvironment.SetDefine(TEXT("VISUALIZE_MOTIONVECTOR"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FVisualizePathTracingMotionVectorPS, "/Engine/Private/PathTracing/PathTracingSpatialTemporalDenoising.usf", "VisualizePathTracingMotionVector", SF_Pixel); class FVisualizeWarpingPS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FVisualizeWarpingPS); SHADER_USE_PARAMETER_STRUCT(FVisualizeWarpingPS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, TemporalDenoisingMotionVector) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, SourceTexture) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, DenoisedTexture) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, TargetTexture) SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, TargetViewport) SHADER_PARAMETER_SAMPLER(SamplerState, SharedTextureSampler) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return ShouldCompilePathTracingDenoiserShadersForProject(Parameters.Platform); } static void ModifyCompilationEnvironment(const FShaderPermutationParameters&, FShaderCompilerEnvironment& OutEnvironment) { OutEnvironment.SetDefine(TEXT("VISUALIZE_WARPING"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FVisualizeWarpingPS, "/Engine/Private/PathTracing/PathTracingSpatialTemporalDenoising.usf", "FVisualizeWarpingPS", SF_Pixel); FScreenPassTexture AddVisualizePathTracingDenoisingPass(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FVisualizePathTracingDenoisingInputs& Inputs) { if (ShouldVisualizePathTracingVelocityState(Inputs.DenoisingContext)) { typedef FVisualizePathTracingMotionVectorPS SHADER; SHADER::FParameters* PassParameters = GraphBuilder.AllocParameters(); { PassParameters->ViewUniformBuffer = View.ViewUniformBuffer; PassParameters->TemporalDenoisingMotionVector = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::Create(Inputs.DenoisingContext.MotionVector)); PassParameters->SceneTextures = Inputs.SceneTexturesUniformBuffer; PassParameters->DenoisedTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::Create(Inputs.DenoisedTexture)); PassParameters->RenderTargets[0] = FRenderTargetBinding(Inputs.SceneColor, ERenderTargetLoadAction::ELoad); } TShaderMapRef PixelShader(View.ShaderMap); AddDrawScreenPass( GraphBuilder, RDG_EVENT_NAME("Visualize Motion Vector (%d x %d)", View.ViewRect.Size().X, View.ViewRect.Size().Y), View, Inputs.Viewport, Inputs.Viewport, PixelShader, PassParameters ); } if (ShouldVisualizeWarping(Inputs.DenoisingContext)) { const FScreenPassTextureViewport TargetViewport(Inputs.DenoisedTexture, View.ViewRect); typedef FVisualizeWarpingPS SHADER; SHADER::FParameters* PassParameters = GraphBuilder.AllocParameters(); { const FScreenPassTextureViewportParameters TargetViewportParameters = GetScreenPassTextureViewportParameters(TargetViewport); PassParameters->ViewUniformBuffer = View.ViewUniformBuffer; PassParameters->TemporalDenoisingMotionVector = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::Create(Inputs.DenoisingContext.MotionVector)); PassParameters->SceneTextures = Inputs.SceneTexturesUniformBuffer; PassParameters->DenoisedTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::Create(Inputs.DenoisedTexture)); PassParameters->SourceTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::Create(Inputs.DenoisingContext.LastDenoisedRadianceTexture)); PassParameters->SharedTextureSampler = TStaticSamplerState::GetRHI(); PassParameters->TargetViewport = TargetViewportParameters; PassParameters->RenderTargets[0] = FRenderTargetBinding(Inputs.SceneColor, ERenderTargetLoadAction::ELoad); } TShaderMapRef PixelShader(View.ShaderMap); AddDrawScreenPass( GraphBuilder, RDG_EVENT_NAME("Visualize Source Warping (%d x %d)", View.ViewRect.Size().X, View.ViewRect.Size().Y), View, Inputs.Viewport, Inputs.Viewport, PixelShader, PassParameters ); } return FScreenPassTexture(); } #endif