Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/PathTracing.cpp
aurel cordonnier d17d20ca36 Merge from Release-Engine-Test @ 16758890 to UE5/Main
This represents UE4/Main @ 16738161 and Dev-PerfTest @ 16737719 (and Release-17.00 @ 16658211)

[CL 16763350 by aurel cordonnier in ue5-main branch]
2021-06-23 17:51:32 -04:00

1510 lines
64 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "PathTracing.h"
#include "RHI.h"
#include "PathTracingDenoiser.h"
PathTracingDenoiserFunction* GPathTracingDenoiserFunc = nullptr;
TAutoConsoleVariable<int32> CVarPathTracing(
TEXT("r.PathTracing"),
0,
TEXT("Enables the path tracing renderer (to guard the compilation of path tracer specific material permutations)"),
ECVF_RenderThreadSafe | ECVF_ReadOnly
);
#if RHI_RAYTRACING
#include "RendererPrivate.h"
#include "GlobalShader.h"
#include "DeferredShadingRenderer.h"
#include "HAL/PlatformApplicationMisc.h"
#include "RayTracingTypes.h"
#include "RayTracingDefinitions.h"
#include "PathTracingDefinitions.h"
#include "RayTracing/RayTracingMaterialHitShaders.h"
#include "RenderCore/Public/GenerateMips.h"
#include <limits>
TAutoConsoleVariable<int32> CVarPathTracingMaxBounces(
TEXT("r.PathTracing.MaxBounces"),
-1,
TEXT("Sets the maximum number of path tracing bounces (default = -1 (driven by postprocesing volume))"),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarPathTracingSamplesPerPixel(
TEXT("r.PathTracing.SamplesPerPixel"),
-1,
TEXT("Sets the maximum number of samples per pixel (default = -1 (driven by postprocesing volume))"),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<float> CVarPathTracingFilterWidth(
TEXT("r.PathTracing.FilterWidth"),
-1,
TEXT("Sets the anti-aliasing filter width (default = -1 (driven by postprocesing volume))"),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarPathTracingMISMode(
TEXT("r.PathTracing.MISMode"),
2,
TEXT("Selects the sampling technique for light integration (default = 2 (MIS enabled))\n")
TEXT("0: Material sampling\n")
TEXT("1: Light sampling\n")
TEXT("2: MIS betwen material and light sampling (default)\n"),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarPathTracingMISCompensation(
TEXT("r.PathTracing.MISCompensation"),
1,
TEXT("Activates MIS compensation for skylight importance sampling. (default = 1 (enabled))\n")
TEXT("This option only takes effect when r.PathTracing.MISMode = 2\n"),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarPathTracingSkylightCaching(
TEXT("r.PathTracing.SkylightCaching"),
1,
TEXT("Attempts to re-use skylight data between frames. (default = 1 (enabled))\n")
TEXT("When set to 0, the skylight texture and importance samping data will be regenerated every frame. This is mainly intended as a benchmarking and debugging aid\n"),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarPathTracingVisibleLights(
TEXT("r.PathTracing.VisibleLights"),
0,
TEXT("Should light sources be visible to camera rays? (default = 0 (off))\n")
TEXT("0: Hide lights from camera rays (default)\n")
TEXT("1: Make lights visible to camera\n"),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarPathTracingMaxSSSBounces(
TEXT("r.PathTracing.MaxSSSBounces"),
256,
TEXT("Sets the maximum number of bounces inside subsurface materials. Lowering this value can make subsurface scattering render too dim, while setting it too high can cause long render times. (default = 256)"),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<float> CVarPathTracingMaxPathIntensity(
TEXT("r.PathTracing.MaxPathIntensity"),
-1,
TEXT("When positive, light paths greater that this amount are clamped to prevent fireflies (default = -1 (driven by postprocesing volume))"),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarPathTracingApproximateCaustics(
TEXT("r.PathTracing.ApproximateCaustics"),
1,
TEXT("When non-zero, the path tracer will approximate caustic paths to reduce noise. This reduces speckles and noise from low-roughness glass and metals. (default = 1 (enabled))"),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarPathTracingEnableEmissive(
TEXT("r.PathTracing.EnableEmissive"),
-1,
TEXT("Indicates if emissive materials should contribute to scene lighting (default = -1 (driven by postprocesing volume)"),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarPathTracingEnableCameraBackfaceCulling(
TEXT("r.PathTracing.EnableCameraBackfaceCulling"),
1,
TEXT("When non-zero, the path tracer will skip over backfacing triangles when tracing primary rays from the camera. (default = 1 (enabled))"),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarPathTracingFrameIndependentTemporalSeed(
TEXT("r.PathTracing.FrameIndependentTemporalSeed"),
1,
TEXT("Indicates to use different temporal seed for each sample across frames rather than resetting the sequence at the start of each frame\n")
TEXT("0: off\n")
TEXT("1: on (default)\n"),
ECVF_RenderThreadSafe
);
// See PATHTRACER_SAMPLER_* defines
TAutoConsoleVariable<int32> CVarPathTracingSamplerType(
TEXT("r.PathTracing.SamplerType"),
PATHTRACER_SAMPLER_DEFAULT,
TEXT("Controls the way the path tracer generates its random numbers\n")
TEXT("0: use a different high quality random sequence per pixel\n")
TEXT("1: optimize the random sequence across pixels to reduce visible error at the target sample count\n")
TEXT("2: share random seeds across pixels to improve coherence of execution on the GPU. This trades some correlation across the image in exchange for better performance.\n"),
ECVF_RenderThreadSafe
);
#if 0
// TODO: re-enable this when multi-gpu is supported again
// r.PathTracing.GPUCount is read only because ComputeViewGPUMasks results cannot change after UE has been launched
TAutoConsoleVariable<int32> CVarPathTracingGPUCount(
TEXT("r.PathTracing.GPUCount"),
1,
TEXT("Sets the amount of GPUs used for computing the path tracing pass (default = 1 GPU)"),
ECVF_RenderThreadSafe | ECVF_ReadOnly
);
#endif
TAutoConsoleVariable<int32> CVarPathTracingWiperMode(
TEXT("r.PathTracing.WiperMode"),
0,
TEXT("Enables wiper mode to render using the path tracer only in a region of the screen for debugging purposes (default = 0, wiper mode disabled)"),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarPathTracingProgressDisplay(
TEXT("r.PathTracing.ProgressDisplay"),
0,
TEXT("Enables an in-frame display of progress towards the defined sample per pixel limit. The indicator dissapears when the maximum is reached and sample accumulation has stopped (default = 0)\n")
TEXT("0: off (default)\n")
TEXT("1: on\n"),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarPathTracingLightGridResolution(
TEXT("r.PathTracing.LightGridResolution"),
256,
TEXT("Controls the resolution of the 2D light grid used to cull irrelevant lights from lighting calculations (default = 256)\n"),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarPathTracingLightGridMaxCount(
TEXT("r.PathTracing.LightGridMaxCount"),
128,
TEXT("Controls the maximum number of lights per cell in the 2D light grid. The minimum of this value and the number of lights in the scene is used. (default = 128)\n"),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarPathTracingLightGridVisualize(
TEXT("r.PathTracing.LightGridVisualize"),
0,
TEXT("Enables a visualization mode of the light grid density where red indicates the maximum light count has been reached (default = 0)\n")
TEXT("0: off (default)\n")
TEXT("1: light count heatmap (red - close to overflow, increase r.PathTracing.LightGridMaxCount)\n")
TEXT("2: unique light lists (colors are a function of which lights occupy each cell)\n")
TEXT("3: area light visualization (green: point light sources only, blue: some area light sources)\n"),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> 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
);
BEGIN_SHADER_PARAMETER_STRUCT(FPathTracingData, )
SHADER_PARAMETER(uint32, Iteration)
SHADER_PARAMETER(uint32, TemporalSeed)
SHADER_PARAMETER(uint32, MaxSamples)
SHADER_PARAMETER(uint32, MaxBounces)
SHADER_PARAMETER(uint32, MaxSSSBounces)
SHADER_PARAMETER(uint32, MISMode)
SHADER_PARAMETER(uint32, ApproximateCaustics)
SHADER_PARAMETER(uint32, EnableCameraBackfaceCulling)
SHADER_PARAMETER(uint32, EnableDirectLighting)
SHADER_PARAMETER(uint32, EnableEmissive)
SHADER_PARAMETER(uint32, SamplerType)
SHADER_PARAMETER(uint32, VisualizeLightGrid)
SHADER_PARAMETER(float, MaxPathIntensity)
SHADER_PARAMETER(float, MaxNormalBias)
SHADER_PARAMETER(float, FilterWidth)
END_SHADER_PARAMETER_STRUCT()
// Store the rendering options used on the previous frame so we can correctly invalidate when things change
struct FPathTracingConfig
{
FPathTracingData PathTracingData;
FIntRect ViewRect;
int LightShowFlags;
int LightGridResolution;
int LightGridMaxCount;
bool VisibleLights;
bool UseMISCompensation;
bool LockedSamplingPattern;
int DenoiserMode; // NOTE: does not require path tracing invalidation
bool IsDifferent(const FPathTracingConfig& Other) const
{
// If any of these parameters if different, we will need to restart path tracing accuulation
return
PathTracingData.MaxSamples != Other.PathTracingData.MaxSamples ||
PathTracingData.MaxBounces != Other.PathTracingData.MaxBounces ||
PathTracingData.MaxSSSBounces != Other.PathTracingData.MaxSSSBounces ||
PathTracingData.MISMode != Other.PathTracingData.MISMode ||
PathTracingData.SamplerType != Other.PathTracingData.SamplerType ||
PathTracingData.ApproximateCaustics != Other.PathTracingData.ApproximateCaustics ||
PathTracingData.EnableCameraBackfaceCulling != Other.PathTracingData.EnableCameraBackfaceCulling ||
PathTracingData.EnableDirectLighting != Other.PathTracingData.EnableDirectLighting ||
PathTracingData.EnableEmissive != Other.PathTracingData.EnableEmissive ||
PathTracingData.VisualizeLightGrid != Other.PathTracingData.VisualizeLightGrid ||
PathTracingData.MaxPathIntensity != Other.PathTracingData.MaxPathIntensity ||
PathTracingData.FilterWidth != Other.PathTracingData.FilterWidth ||
ViewRect != Other.ViewRect ||
LightShowFlags != Other.LightShowFlags ||
LightGridResolution != Other.LightGridResolution ||
LightGridMaxCount != Other.LightGridMaxCount ||
VisibleLights != Other.VisibleLights ||
UseMISCompensation != Other.UseMISCompensation ||
LockedSamplingPattern != Other.LockedSamplingPattern;
}
};
// This function prepares the portion of shader arguments that may involve invalidating the path traced state
static void PrepareShaderArgs(const FViewInfo& View, FPathTracingData& PathTracingData)
{
PathTracingData.EnableDirectLighting = true;
int32 MaxBounces = CVarPathTracingMaxBounces.GetValueOnRenderThread();
if (MaxBounces < 0)
{
MaxBounces = View.FinalPostProcessSettings.PathTracingMaxBounces;
}
if (View.Family->EngineShowFlags.DirectLighting)
{
if (!View.Family->EngineShowFlags.GlobalIllumination)
{
// direct lighting, but no GI
MaxBounces = 1;
}
}
else
{
PathTracingData.EnableDirectLighting = false;
if (View.Family->EngineShowFlags.GlobalIllumination)
{
// skip direct lighting, but still do the full bounces
}
else
{
// neither direct, nor GI is on
MaxBounces = 0;
}
}
PathTracingData.MaxBounces = MaxBounces;
PathTracingData.MaxSSSBounces = CVarPathTracingMaxSSSBounces.GetValueOnRenderThread();
PathTracingData.MaxNormalBias = GetRaytracingMaxNormalBias();
PathTracingData.MISMode = CVarPathTracingMISMode.GetValueOnRenderThread();
PathTracingData.MaxPathIntensity = CVarPathTracingMaxPathIntensity.GetValueOnRenderThread();
if (PathTracingData.MaxPathIntensity <= 0)
{
// cvar clamp disabled, use PPV exposure value instad
PathTracingData.MaxPathIntensity = FMath::Pow(2.0f, View.FinalPostProcessSettings.PathTracingMaxPathExposure);
}
PathTracingData.ApproximateCaustics = CVarPathTracingApproximateCaustics.GetValueOnRenderThread();
PathTracingData.EnableCameraBackfaceCulling = CVarPathTracingEnableCameraBackfaceCulling.GetValueOnRenderThread();
PathTracingData.SamplerType = CVarPathTracingSamplerType.GetValueOnRenderThread();
int32 EnableEmissive = CVarPathTracingEnableEmissive.GetValueOnRenderThread();
PathTracingData.EnableEmissive = EnableEmissive < 0 ? View.FinalPostProcessSettings.PathTracingEnableEmissive : EnableEmissive;
PathTracingData.VisualizeLightGrid = CVarPathTracingLightGridVisualize.GetValueOnRenderThread();
float FilterWidth = CVarPathTracingFilterWidth.GetValueOnRenderThread();
if (FilterWidth < 0)
{
FilterWidth = View.FinalPostProcessSettings.PathTracingFilterWidth;
}
PathTracingData.FilterWidth = FilterWidth;
}
static bool ShouldCompilePathTracingShadersForProject(EShaderPlatform ShaderPlatform)
{
return ShouldCompileRayTracingShadersForProject(ShaderPlatform) &&
FDataDrivenShaderPlatformInfo::GetSupportsPathTracing(ShaderPlatform) &&
CVarPathTracing.GetValueOnAnyThread() != 0;
}
class FPathTracingSkylightPrepareCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FPathTracingSkylightPrepareCS)
SHADER_USE_PARAMETER_STRUCT(FPathTracingSkylightPrepareCS, FGlobalShader)
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
// NOTE: skylight code is shared with RT passes
return ShouldCompileRayTracingShadersForProject(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
//OutEnvironment.CompilerFlags.Add(CFLAG_WarningsAsErrors);
OutEnvironment.SetDefine(TEXT("THREADGROUPSIZE_X"), FComputeShaderUtils::kGolden2DGroupSize);
OutEnvironment.SetDefine(TEXT("THREADGROUPSIZE_Y"), FComputeShaderUtils::kGolden2DGroupSize);
}
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_TEXTURE(TextureCube, SkyLightCubemap0)
SHADER_PARAMETER_TEXTURE(TextureCube, SkyLightCubemap1)
SHADER_PARAMETER_SAMPLER(SamplerState, SkyLightCubemapSampler0)
SHADER_PARAMETER_SAMPLER(SamplerState, SkyLightCubemapSampler1)
SHADER_PARAMETER(float, SkylightBlendFactor)
SHADER_PARAMETER(float, SkylightInvResolution)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, SkylightTextureOutput)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, SkylightTexturePdf)
SHADER_PARAMETER(FVector3f, SkyColor)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_SHADER_TYPE(, FPathTracingSkylightPrepareCS, TEXT("/Engine/Private/PathTracing/PathTracingSkylightPrepare.usf"), TEXT("PathTracingSkylightPrepareCS"), SF_Compute);
class FPathTracingSkylightMISCompensationCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FPathTracingSkylightMISCompensationCS)
SHADER_USE_PARAMETER_STRUCT(FPathTracingSkylightMISCompensationCS, FGlobalShader)
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
// NOTE: skylight code is shared with RT passes
return ShouldCompileRayTracingShadersForProject(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
//OutEnvironment.CompilerFlags.Add(CFLAG_WarningsAsErrors);
OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads);
OutEnvironment.SetDefine(TEXT("THREADGROUPSIZE_X"), FComputeShaderUtils::kGolden2DGroupSize);
OutEnvironment.SetDefine(TEXT("THREADGROUPSIZE_Y"), FComputeShaderUtils::kGolden2DGroupSize);
}
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, SkylightTexturePdfAverage)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, SkylightTextureOutput)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, SkylightTexturePdf)
SHADER_PARAMETER(FVector3f, SkyColor)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_SHADER_TYPE(, FPathTracingSkylightMISCompensationCS, TEXT("/Engine/Private/PathTracing/PathTracingSkylightMISCompensation.usf"), TEXT("PathTracingSkylightMISCompensationCS"), SF_Compute);
// this struct holds a light grid for both building or rendering
BEGIN_SHADER_PARAMETER_STRUCT(FPathTracingLightGrid, RENDERER_API)
SHADER_PARAMETER(uint32, SceneInfiniteLightCount)
SHADER_PARAMETER(FVector3f, SceneLightsBoundMin)
SHADER_PARAMETER(FVector3f, SceneLightsBoundMax)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, LightGrid)
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<uint>, LightGridData)
SHADER_PARAMETER(unsigned, LightGridResolution)
SHADER_PARAMETER(unsigned, LightGridMaxCount)
SHADER_PARAMETER(int, LightGridAxis)
END_SHADER_PARAMETER_STRUCT()
class FPathTracingBuildLightGridCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FPathTracingBuildLightGridCS)
SHADER_USE_PARAMETER_STRUCT(FPathTracingBuildLightGridCS, FGlobalShader)
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return ShouldCompileRayTracingShadersForProject(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
//OutEnvironment.CompilerFlags.Add(CFLAG_WarningsAsErrors);
OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads);
OutEnvironment.SetDefine(TEXT("THREADGROUPSIZE_X"), FComputeShaderUtils::kGolden2DGroupSize);
OutEnvironment.SetDefine(TEXT("THREADGROUPSIZE_Y"), FComputeShaderUtils::kGolden2DGroupSize);
}
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<FPathTracingLight>, SceneLights)
SHADER_PARAMETER(uint32, SceneLightCount)
SHADER_PARAMETER_STRUCT_INCLUDE(FPathTracingLightGrid, LightGridParameters)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWLightGrid)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, RWLightGridData)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_SHADER_TYPE(, FPathTracingBuildLightGridCS, TEXT("/Engine/Private/PathTracing/PathTracingBuildLightGrid.usf"), TEXT("PathTracingBuildLightGridCS"), SF_Compute);
class FPathTracingRG : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FPathTracingRG)
SHADER_USE_ROOT_PARAMETER_STRUCT(FPathTracingRG, FGlobalShader)
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return ShouldCompilePathTracingShadersForProject(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
//OutEnvironment.CompilerFlags.Add(CFLAG_WarningsAsErrors);
OutEnvironment.SetDefine(TEXT("USE_RECT_LIGHT_TEXTURES"), 1);
}
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, RadianceTexture)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, AlbedoTexture)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, NormalTexture)
SHADER_PARAMETER_SRV(RaytracingAccelerationStructure, TLAS)
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer)
SHADER_PARAMETER_STRUCT_INCLUDE(FPathTracingData, PathTracingData)
// scene lights
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<FPathTracingLight>, SceneLights)
SHADER_PARAMETER(uint32, SceneLightCount)
SHADER_PARAMETER(uint32, SceneVisibleLightCount)
SHADER_PARAMETER_STRUCT_INCLUDE(FPathTracingLightGrid, LightGridParameters)
// Skylight
SHADER_PARAMETER_STRUCT_INCLUDE(FPathTracingSkylight, SkylightParameters)
// IES Profiles
SHADER_PARAMETER_RDG_TEXTURE(Texture2DArray, IESTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, IESTextureSampler) // Shared sampler for all IES profiles
// Rect lights
SHADER_PARAMETER_TEXTURE_ARRAY(Texture2D, RectLightTexture, [PATHTRACER_MAX_RECT_TEXTURES])
SHADER_PARAMETER_SAMPLER(SamplerState, RectLightSampler) // Shared sampler for all rectlights
// Subsurface data
SHADER_PARAMETER_TEXTURE(Texture2D, SSProfilesTexture)
// Used by multi-GPU rendering
SHADER_PARAMETER(FIntVector, TileOffset)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FPathTracingRG, "/Engine/Private/PathTracing/PathTracing.usf", "PathTracingMainRG", SF_RayGen);
class FPathTracingIESAtlasCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FPathTracingIESAtlasCS)
SHADER_USE_PARAMETER_STRUCT(FPathTracingIESAtlasCS, FGlobalShader)
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return ShouldCompileRayTracingShadersForProject(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
//OutEnvironment.CompilerFlags.Add(CFLAG_WarningsAsErrors);
OutEnvironment.SetDefine(TEXT("THREADGROUPSIZE_X"), FComputeShaderUtils::kGolden2DGroupSize);
OutEnvironment.SetDefine(TEXT("THREADGROUPSIZE_Y"), FComputeShaderUtils::kGolden2DGroupSize);
}
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_TEXTURE(Texture2D, IESTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, IESSampler)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2DArray, IESAtlas)
SHADER_PARAMETER(int32, IESAtlasSlice)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_SHADER_TYPE(, FPathTracingIESAtlasCS, TEXT("/Engine/Private/PathTracing/PathTracingIESAtlas.usf"), TEXT("PathTracingIESAtlasCS"), SF_Compute);
template<bool UseAnyHitShader>
class TPathTracingMaterial : public FMeshMaterialShader
{
DECLARE_SHADER_TYPE(TPathTracingMaterial, MeshMaterial);
public:
TPathTracingMaterial() = default;
TPathTracingMaterial(const FMeshMaterialShaderType::CompiledShaderInitializerType& Initializer)
: FMeshMaterialShader(Initializer)
{}
static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters)
{
return Parameters.VertexFactoryType->SupportsRayTracing()
&& ((Parameters.MaterialParameters.bIsMasked || Parameters.MaterialParameters.BlendMode != BLEND_Opaque) == UseAnyHitShader)
&& ShouldCompilePathTracingShadersForProject(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
OutEnvironment.SetDefine(TEXT("USE_MATERIAL_CLOSEST_HIT_SHADER"), 1);
OutEnvironment.SetDefine(TEXT("USE_MATERIAL_ANY_HIT_SHADER"), UseAnyHitShader ? 1 : 0);
OutEnvironment.SetDefine(TEXT("USE_RAYTRACED_TEXTURE_RAYCONE_LOD"), 0);
OutEnvironment.SetDefine(TEXT("SCENE_TEXTURES_DISABLED"), 1);
OutEnvironment.SetDefine(TEXT("SIMPLIFIED_MATERIAL_SHADER"), 0); // TODO: expose a permutation so we can unify with GPULightmass?
FMeshMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
}
static bool ValidateCompiledResult(EShaderPlatform Platform, const FShaderParameterMap& ParameterMap, TArray<FString>& OutError)
{
if (ParameterMap.ContainsParameterAllocation(FSceneTextureUniformParameters::StaticStructMetadata.GetShaderVariableName()))
{
OutError.Add(TEXT("Ray tracing closest hit shaders cannot read from the SceneTexturesStruct."));
return false;
}
for (const auto& It : ParameterMap.GetParameterMap())
{
const FParameterAllocation& ParamAllocation = It.Value;
if (ParamAllocation.Type != EShaderParameterType::UniformBuffer
&& ParamAllocation.Type != EShaderParameterType::LooseData)
{
OutError.Add(FString::Printf(TEXT("Invalid ray tracing shader parameter '%s'. Only uniform buffers and loose data parameters are supported."), *(It.Key)));
return false;
}
}
return true;
}
};
using FPathTracingMaterialCHS = TPathTracingMaterial<false>;
using FPathTracingMaterialCHS_AHS = TPathTracingMaterial<true>;
IMPLEMENT_MATERIAL_SHADER_TYPE(template <>, FPathTracingMaterialCHS , TEXT("/Engine/Private/PathTracing/PathTracingMaterialHitShader.usf"), TEXT("closesthit=PathTracingMaterialCHS"), SF_RayHitGroup);
IMPLEMENT_MATERIAL_SHADER_TYPE(template <>, FPathTracingMaterialCHS_AHS, TEXT("/Engine/Private/PathTracing/PathTracingMaterialHitShader.usf"), TEXT("closesthit=PathTracingMaterialCHS anyhit=PathTracingMaterialAHS"), SF_RayHitGroup);
bool FRayTracingMeshProcessor::ProcessPathTracing(
const FMeshBatch& RESTRICT MeshBatch,
uint64 BatchElementMask,
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
const FMaterialRenderProxy& RESTRICT MaterialRenderProxy,
const FMaterial& RESTRICT MaterialResource)
{
const FVertexFactory* VertexFactory = MeshBatch.VertexFactory;
TMeshProcessorShaders<
FMeshMaterialShader,
FMeshMaterialShader,
FMeshMaterialShader,
FMeshMaterialShader,
FMeshMaterialShader> RayTracingShaders;
FMaterialShaderTypes ShaderTypes;
if (MaterialResource.IsMasked() || MaterialResource.GetBlendMode() != BLEND_Opaque)
{
ShaderTypes.AddShaderType<FPathTracingMaterialCHS_AHS>();
}
else
{
ShaderTypes.AddShaderType<FPathTracingMaterialCHS>();
}
FMaterialShaders Shaders;
if (!MaterialResource.TryGetShaders(ShaderTypes, VertexFactory->GetType(), Shaders))
{
return false;
}
check(Shaders.TryGetShader(SF_RayHitGroup, RayTracingShaders.RayHitGroupShader));
TBasePassShaderElementData<FUniformLightMapPolicy> ShaderElementData(MeshBatch.LCI);
ShaderElementData.InitializeMeshMaterialData(ViewIfDynamicMeshCommand, PrimitiveSceneProxy, MeshBatch, -1, true);
BuildRayTracingMeshCommands(
MeshBatch,
BatchElementMask,
PrimitiveSceneProxy,
MaterialRenderProxy,
MaterialResource,
PassDrawRenderState,
RayTracingShaders,
ShaderElementData);
return true;
}
RENDERER_API void PrepareSkyTexture_Internal(
FRDGBuilder& GraphBuilder,
FReflectionUniformParameters& Parameters,
uint32 Size,
FLinearColor SkyColor,
bool UseMISCompensation,
// Out
FRDGTextureRef& SkylightTexture,
FRDGTextureRef& SkylightPdf,
float& SkylightInvResolution,
int32& SkylightMipCount
)
{
FRDGTextureDesc SkylightTextureDesc = FRDGTextureDesc::Create2D(
FIntPoint(Size, Size),
PF_A32B32G32R32F, // half precision might be ok?
FClearValueBinding::None,
TexCreate_ShaderResource | TexCreate_UAV);
SkylightTexture = GraphBuilder.CreateTexture(SkylightTextureDesc, TEXT("PathTracer.Skylight"), ERDGTextureFlags::None);
FRDGTextureDesc SkylightPdfDesc = FRDGTextureDesc::Create2D(
FIntPoint(Size, Size),
PF_R32_FLOAT, // half precision might be ok?
FClearValueBinding::None,
TexCreate_ShaderResource | TexCreate_UAV,
FMath::CeilLogTwo(Size) + 1);
SkylightPdf = GraphBuilder.CreateTexture(SkylightPdfDesc, TEXT("PathTracer.SkylightPdf"), ERDGTextureFlags::None);
SkylightInvResolution = 1.0f / Size;
SkylightMipCount = SkylightPdfDesc.NumMips;
// run a simple compute shader to sample the cubemap and prep the top level of the mipmap hierarchy
{
TShaderMapRef<FPathTracingSkylightPrepareCS> ComputeShader(GetGlobalShaderMap(GMaxRHIFeatureLevel));
FPathTracingSkylightPrepareCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FPathTracingSkylightPrepareCS::FParameters>();
PassParameters->SkyColor = FVector(SkyColor.R, SkyColor.G, SkyColor.B);
PassParameters->SkyLightCubemap0 = Parameters.SkyLightCubemap;
PassParameters->SkyLightCubemap1 = Parameters.SkyLightBlendDestinationCubemap;
PassParameters->SkyLightCubemapSampler0 = Parameters.SkyLightCubemapSampler;
PassParameters->SkyLightCubemapSampler1 = Parameters.SkyLightBlendDestinationCubemapSampler;
PassParameters->SkylightBlendFactor = Parameters.SkyLightParameters.W;
PassParameters->SkylightInvResolution = SkylightInvResolution;
PassParameters->SkylightTextureOutput = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(SkylightTexture, 0));
PassParameters->SkylightTexturePdf = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(SkylightPdf, 0));
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("SkylightPrepare"),
ComputeShader,
PassParameters,
FComputeShaderUtils::GetGroupCount(FIntPoint(Size, Size), FComputeShaderUtils::kGolden2DGroupSize));
}
FGenerateMips::ExecuteCompute(GraphBuilder, SkylightPdf, TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI());
if (UseMISCompensation)
{
TShaderMapRef<FPathTracingSkylightMISCompensationCS> ComputeShader(GetGlobalShaderMap(GMaxRHIFeatureLevel));
FPathTracingSkylightMISCompensationCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FPathTracingSkylightMISCompensationCS::FParameters>();
PassParameters->SkylightTexturePdfAverage = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForMipLevel(SkylightPdf, SkylightMipCount - 1));
PassParameters->SkylightTextureOutput = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(SkylightTexture, 0));
PassParameters->SkylightTexturePdf = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(SkylightPdf, 0));
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("SkylightMISCompensation"),
ComputeShader,
PassParameters,
FComputeShaderUtils::GetGroupCount(FIntPoint(Size, Size), FComputeShaderUtils::kGolden2DGroupSize));
FGenerateMips::ExecuteCompute(GraphBuilder, SkylightPdf, TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI());
}
}
RENDERER_API FRDGTexture* PrepareIESAtlas(const TMap<FTexture*, int>& InIESLightProfilesMap, FRDGBuilder& GraphBuilder)
{
// We found some IES profiles to use -- upload them into a single atlas so we can access them easily in HLSL
// TODO: This is redundant because all the IES textures are already on the GPU. Handling IES profiles via Miss shaders
// would be cleaner.
// TODO: This is also redundant with the logic in RayTracingLighting.cpp, but the latter is limitted to 1D profiles and
// does not consider the same set of lights as the path tracer. Longer term we should aim to unify the representation of lights
// across both passes
// TODO: This process is repeated every frame! More motivation to move to a Miss shader based implementation
// This size matches the import resolution of light profiles (see FIESLoader::GetWidth)
const int kIESAtlasSize = 256;
const int NumSlices = InIESLightProfilesMap.Num();
FRDGTextureDesc IESTextureDesc = FRDGTextureDesc::Create2DArray(
FIntPoint(kIESAtlasSize, kIESAtlasSize),
PF_R32_FLOAT,
FClearValueBinding::None,
TexCreate_ShaderResource | TexCreate_UAV,
NumSlices);
FRDGTexture* IESTexture = GraphBuilder.CreateTexture(IESTextureDesc, TEXT("PathTracer.IESAtlas"), ERDGTextureFlags::None);
for (auto&& Entry : InIESLightProfilesMap)
{
FPathTracingIESAtlasCS::FParameters* AtlasPassParameters = GraphBuilder.AllocParameters<FPathTracingIESAtlasCS::FParameters>();
const int Slice = Entry.Value;
AtlasPassParameters->IESTexture = Entry.Key->TextureRHI;
AtlasPassParameters->IESSampler = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
AtlasPassParameters->IESAtlas = GraphBuilder.CreateUAV(IESTexture);
AtlasPassParameters->IESAtlasSlice = Slice;
TShaderMapRef<FPathTracingIESAtlasCS> ComputeShader(GetGlobalShaderMap(GMaxRHIFeatureLevel));
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("Path Tracing IES Atlas (Slice=%d)", Slice),
ComputeShader,
AtlasPassParameters,
FComputeShaderUtils::GetGroupCount(FIntPoint(kIESAtlasSize, kIESAtlasSize), FComputeShaderUtils::kGolden2DGroupSize));
}
return IESTexture;
}
RDG_REGISTER_BLACKBOARD_STRUCT(FPathTracingSkylight)
bool PrepareSkyTexture(FRDGBuilder& GraphBuilder, FScene* Scene, const FViewInfo& View, bool SkylightEnabled, bool UseMISCompensation, FPathTracingSkylight* SkylightParameters)
{
SkylightParameters->SkylightTextureSampler = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
FReflectionUniformParameters Parameters;
SetupReflectionUniformParameters(View, Parameters);
if (!SkylightEnabled || !(Parameters.SkyLightParameters.Y > 0))
{
// textures not ready, or skylight not active
// just put in a placeholder
SkylightParameters->SkylightTexture = GraphBuilder.RegisterExternalTexture(GSystemTextures.BlackDummy);
SkylightParameters->SkylightPdf = GraphBuilder.RegisterExternalTexture(GSystemTextures.BlackDummy);
SkylightParameters->SkylightInvResolution = 0;
SkylightParameters->SkylightMipCount = 0;
return false;
}
// the sky is actually enabled, lets see if someone already made use of it for this frame
const FPathTracingSkylight* PreviousSkylightParameters = GraphBuilder.Blackboard.Get<FPathTracingSkylight>();
if (PreviousSkylightParameters != nullptr)
{
*SkylightParameters = *PreviousSkylightParameters;
return true;
}
// should we remember the skylight prep for the next frame?
const bool IsSkylightCachingEnabled = CVarPathTracingSkylightCaching.GetValueOnAnyThread() != 0;
if (!IsSkylightCachingEnabled)
{
// we don't want any caching - release what we might have been holding onto
Scene->PathTracingSkylightTexture.SafeRelease();
Scene->PathTracingSkylightPdf.SafeRelease();
}
if (Scene->PathTracingSkylightTexture.IsValid() &&
Scene->PathTracingSkylightPdf.IsValid())
{
// we already have a valid texture and pdf, just re-use them!
// it is the responsability of code that may invalidate the contents to reset these pointers
SkylightParameters->SkylightTexture = GraphBuilder.RegisterExternalTexture(Scene->PathTracingSkylightTexture, TEXT("PathTracer.Skylight"));
SkylightParameters->SkylightPdf = GraphBuilder.RegisterExternalTexture(Scene->PathTracingSkylightPdf, TEXT("PathTracer.SkylightPdf"));
SkylightParameters->SkylightInvResolution = 1.0f / SkylightParameters->SkylightTexture->Desc.GetSize().X;
SkylightParameters->SkylightMipCount = SkylightParameters->SkylightPdf->Desc.NumMips;
return true;
}
RDG_EVENT_SCOPE(GraphBuilder, "Path Tracing SkylightPrepare");
FLinearColor SkyColor = Scene->SkyLight->GetEffectiveLightColor();
// since we are resampled into an octahedral layout, we multiply the cubemap resolution by 2 to get roughly the same number of texels
uint32 Size = FMath::RoundUpToPowerOfTwo(2 * Scene->SkyLight->CaptureCubeMapResolution);
PrepareSkyTexture_Internal(
GraphBuilder,
Parameters,
Size,
SkyColor,
UseMISCompensation,
// Out
SkylightParameters->SkylightTexture,
SkylightParameters->SkylightPdf,
SkylightParameters->SkylightInvResolution,
SkylightParameters->SkylightMipCount
);
// hang onto these for next time (if caching is enabled)
if (IsSkylightCachingEnabled)
{
GraphBuilder.QueueTextureExtraction(SkylightParameters->SkylightTexture, &Scene->PathTracingSkylightTexture);
GraphBuilder.QueueTextureExtraction(SkylightParameters->SkylightPdf, &Scene->PathTracingSkylightPdf);
}
// remember the skylight parameters for future passes within this frame
GraphBuilder.Blackboard.Create<FPathTracingSkylight>() = *SkylightParameters;
return true;
}
RENDERER_API void PrepareLightGrid(FRDGBuilder& GraphBuilder, FPathTracingLightGrid* LightGridParameters, const FPathTracingLight* Lights, uint32 NumLights, uint32 NumInfiniteLights, FRDGBufferSRV* LightsSRV)
{
const float Inf = std::numeric_limits<float>::infinity();
LightGridParameters->SceneInfiniteLightCount = NumInfiniteLights;
LightGridParameters->SceneLightsBoundMin = FVector(+Inf, +Inf, +Inf);
LightGridParameters->SceneLightsBoundMax = FVector(-Inf, -Inf, -Inf);
LightGridParameters->LightGrid = nullptr;
LightGridParameters->LightGridData = nullptr;
int NumFiniteLights = NumLights - NumInfiniteLights;
// if we have some finite lights -- build a light grid
if (NumFiniteLights > 0)
{
// get bounding box of all finite lights
const FPathTracingLight* FiniteLights = Lights + NumInfiniteLights;
for (int Index = 0; Index < NumFiniteLights; Index++)
{
const FPathTracingLight& Light = FiniteLights[Index];
LightGridParameters->SceneLightsBoundMin = FVector::Min(LightGridParameters->SceneLightsBoundMin, Light.BoundMin);
LightGridParameters->SceneLightsBoundMax = FVector::Max(LightGridParameters->SceneLightsBoundMax, Light.BoundMax);
}
const uint32 Resolution = FMath::RoundUpToPowerOfTwo(CVarPathTracingLightGridResolution.GetValueOnRenderThread());
const uint32 MaxCount = FMath::Clamp(
CVarPathTracingLightGridMaxCount.GetValueOnRenderThread(),
1,
FMath::Min(NumFiniteLights, RAY_TRACING_LIGHT_COUNT_MAXIMUM)
);
LightGridParameters->LightGridResolution = Resolution;
LightGridParameters->LightGridMaxCount = MaxCount;
// pick the shortest axis
FVector Diag = LightGridParameters->SceneLightsBoundMax - LightGridParameters->SceneLightsBoundMin;
if (Diag.X < Diag.Y && Diag.X < Diag.Z)
{
LightGridParameters->LightGridAxis = 0;
}
else if (Diag.Y < Diag.Z)
{
LightGridParameters->LightGridAxis = 1;
}
else
{
LightGridParameters->LightGridAxis = 2;
}
FPathTracingBuildLightGridCS::FParameters* LightGridPassParameters = GraphBuilder.AllocParameters< FPathTracingBuildLightGridCS::FParameters>();
FRDGTextureDesc LightGridDesc = FRDGTextureDesc::Create2D(
FIntPoint(Resolution, Resolution),
PF_R32_UINT,
FClearValueBinding::None,
TexCreate_ShaderResource | TexCreate_UAV);
FRDGTexture* LightGridTexture = GraphBuilder.CreateTexture(LightGridDesc, TEXT("PathTracer.LightGrid"), ERDGTextureFlags::None);
LightGridPassParameters->RWLightGrid = GraphBuilder.CreateUAV(LightGridTexture);
EPixelFormat LightGridDataFormat = PF_R32_UINT;
size_t LightGridDataNumBytes = sizeof(uint32);
if (NumLights <= (MAX_uint8 + 1))
{
LightGridDataFormat = PF_R8_UINT;
LightGridDataNumBytes = sizeof(uint8);
}
else if (NumLights <= (MAX_uint16 + 1))
{
LightGridDataFormat = PF_R16_UINT;
LightGridDataNumBytes = sizeof(uint16);
}
FRDGBufferDesc LightGridDataDesc = FRDGBufferDesc::CreateBufferDesc(LightGridDataNumBytes, MaxCount * Resolution * Resolution);
FRDGBuffer* LightGridData = GraphBuilder.CreateBuffer(LightGridDataDesc, TEXT("PathTracer.LightGridData"));
LightGridPassParameters->RWLightGridData = GraphBuilder.CreateUAV(LightGridData, LightGridDataFormat);
LightGridPassParameters->LightGridParameters = *LightGridParameters;
LightGridPassParameters->SceneLights = LightsSRV;
LightGridPassParameters->SceneLightCount = NumLights;
TShaderMapRef<FPathTracingBuildLightGridCS> ComputeShader(GetGlobalShaderMap(GMaxRHIFeatureLevel));
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("Light Grid Create (%u lights)", NumFiniteLights),
ComputeShader,
LightGridPassParameters,
FComputeShaderUtils::GetGroupCount(FIntPoint(Resolution, Resolution), FComputeShaderUtils::kGolden2DGroupSize));
// hookup to the actual rendering pass
LightGridParameters->LightGrid = LightGridTexture;
LightGridParameters->LightGridData = GraphBuilder.CreateSRV(LightGridData, LightGridDataFormat);
}
else
{
// light grid is not needed - just hookup dummy data
LightGridParameters->LightGridResolution = 0;
LightGridParameters->LightGridMaxCount = 0;
LightGridParameters->LightGridAxis = 0;
LightGridParameters->LightGrid = GraphBuilder.RegisterExternalTexture(GSystemTextures.BlackDummy);
FRDGBufferDesc LightGridDataDesc = FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), 1);
FRDGBuffer* LightGridData = GraphBuilder.CreateBuffer(LightGridDataDesc, TEXT("PathTracer.LightGridData"));
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(LightGridData, PF_R32_UINT), 0);
LightGridParameters->LightGridData = GraphBuilder.CreateSRV(LightGridData, PF_R32_UINT);
}
}
void SetLightParameters(FRDGBuilder& GraphBuilder, FPathTracingRG::FParameters* PassParameters, FScene* Scene, const FViewInfo& View, bool UseMISCompensation)
{
PassParameters->SceneVisibleLightCount = 0;
// Lights
uint32 MaxNumLights = 1 + Scene->Lights.Num(); // upper bound
// Allocate from the graph builder so that we don't need to copy the data again when queuing the upload
FPathTracingLight* Lights = (FPathTracingLight*) GraphBuilder.Alloc(sizeof(FPathTracingLight) * MaxNumLights, 16);
uint32 NumLights = 0;
// Prepend SkyLight to light buffer since it is not part of the regular light list
const float Inf = std::numeric_limits<float>::infinity();
if (PrepareSkyTexture(GraphBuilder, Scene, View, true, UseMISCompensation, &PassParameters->SkylightParameters))
{
check(Scene->SkyLight != nullptr);
FPathTracingLight& DestLight = Lights[NumLights++];
DestLight.Color = FVector(1, 1, 1); // not used (it is folded into the importance table directly)
DestLight.Flags = Scene->SkyLight->bTransmission ? PATHTRACER_FLAG_TRANSMISSION_MASK : 0;
DestLight.Flags |= PATHTRACER_FLAG_LIGHTING_CHANNEL_MASK;
DestLight.Flags |= PATHTRACING_LIGHT_SKY;
DestLight.Flags |= Scene->SkyLight->bCastShadows ? PATHTRACER_FLAG_CAST_SHADOW_MASK : 0;
DestLight.IESTextureSlice = -1;
DestLight.BoundMin = FVector(-Inf, -Inf, -Inf);
DestLight.BoundMax = FVector( Inf, Inf, Inf);
if (Scene->SkyLight->bRealTimeCaptureEnabled)
{
// When using the realtime capture system, always make the skylight visible
// because this is our only way of "seeing" the atmo/clouds at the moment
PassParameters->SceneVisibleLightCount = 1;
}
}
// Add directional lights next (all lights with infinite bounds should come first)
if (View.Family->EngineShowFlags.DirectionalLights)
{
for (auto Light : Scene->Lights)
{
ELightComponentType LightComponentType = (ELightComponentType)Light.LightSceneInfo->Proxy->GetLightType();
if (LightComponentType != LightType_Directional)
{
continue;
}
FLightShaderParameters LightParameters;
Light.LightSceneInfo->Proxy->GetLightShaderParameters(LightParameters);
if (LightParameters.Color.IsZero())
{
continue;
}
FPathTracingLight& DestLight = Lights[NumLights++];
uint32 Transmission = Light.LightSceneInfo->Proxy->Transmission();
uint8 LightingChannelMask = Light.LightSceneInfo->Proxy->GetLightingChannelMask();
DestLight.Flags = Transmission ? PATHTRACER_FLAG_TRANSMISSION_MASK : 0;
DestLight.Flags |= LightingChannelMask & PATHTRACER_FLAG_LIGHTING_CHANNEL_MASK;
DestLight.Flags |= Light.LightSceneInfo->Proxy->CastsDynamicShadow() ? PATHTRACER_FLAG_CAST_SHADOW_MASK : 0;
DestLight.IESTextureSlice = -1;
DestLight.RectLightTextureIndex = -1;
// these mean roughly the same thing across all light types
DestLight.Color = LightParameters.Color;
DestLight.Position = LightParameters.Position;
DestLight.Normal = -LightParameters.Direction;
DestLight.dPdu = FVector::CrossProduct(LightParameters.Tangent, LightParameters.Direction);
DestLight.dPdv = LightParameters.Tangent;
DestLight.Attenuation = LightParameters.InvRadius;
DestLight.FalloffExponent = 0;
DestLight.Normal = LightParameters.Direction;
DestLight.Dimensions = FVector(LightParameters.SourceRadius, LightParameters.SoftSourceRadius, 0.0f);
DestLight.Flags |= PATHTRACING_LIGHT_DIRECTIONAL;
DestLight.BoundMin = FVector(-Inf, -Inf, -Inf);
DestLight.BoundMax = FVector( Inf, Inf, Inf);
}
}
uint32 NumInfiniteLights = NumLights;
int32 NextRectTextureIndex = 0;
TMap<FTexture*, int> IESLightProfilesMap;
for (auto Light : Scene->Lights)
{
ELightComponentType LightComponentType = (ELightComponentType)Light.LightSceneInfo->Proxy->GetLightType();
if ( (LightComponentType == LightType_Directional) /* already handled by the loop above */ ||
((LightComponentType == LightType_Rect ) && !View.Family->EngineShowFlags.RectLights ) ||
((LightComponentType == LightType_Spot ) && !View.Family->EngineShowFlags.SpotLights ) ||
((LightComponentType == LightType_Point ) && !View.Family->EngineShowFlags.PointLights ))
{
// This light type is not currently enabled
continue;
}
FLightShaderParameters LightParameters;
Light.LightSceneInfo->Proxy->GetLightShaderParameters(LightParameters);
if (LightParameters.Color.IsZero())
{
continue;
}
FPathTracingLight& DestLight = Lights[NumLights++];
uint32 Transmission = Light.LightSceneInfo->Proxy->Transmission();
uint8 LightingChannelMask = Light.LightSceneInfo->Proxy->GetLightingChannelMask();
DestLight.Flags = Transmission ? PATHTRACER_FLAG_TRANSMISSION_MASK : 0;
DestLight.Flags |= LightingChannelMask & PATHTRACER_FLAG_LIGHTING_CHANNEL_MASK;
DestLight.Flags |= Light.LightSceneInfo->Proxy->CastsDynamicShadow() ? PATHTRACER_FLAG_CAST_SHADOW_MASK : 0;
DestLight.IESTextureSlice = -1;
DestLight.RectLightTextureIndex = -1;
if (View.Family->EngineShowFlags.TexturedLightProfiles)
{
FTexture* IESTexture = Light.LightSceneInfo->Proxy->GetIESTextureResource();
if (IESTexture != nullptr)
{
// Only add a given texture once
DestLight.IESTextureSlice = IESLightProfilesMap.FindOrAdd(IESTexture, IESLightProfilesMap.Num());
}
}
// these mean roughly the same thing across all light types
DestLight.Color = LightParameters.Color;
DestLight.Position = LightParameters.Position;
DestLight.Normal = -LightParameters.Direction;
DestLight.dPdu = FVector::CrossProduct(LightParameters.Tangent, LightParameters.Direction);
DestLight.dPdv = LightParameters.Tangent;
DestLight.Attenuation = LightParameters.InvRadius;
DestLight.FalloffExponent = 0;
switch (LightComponentType)
{
case LightType_Rect:
{
DestLight.Dimensions = FVector(2.0f * LightParameters.SourceRadius, 2.0f * LightParameters.SourceLength, 0.0f);
DestLight.Shaping = FVector2D(LightParameters.RectLightBarnCosAngle, LightParameters.RectLightBarnLength);
DestLight.FalloffExponent = LightParameters.FalloffExponent;
DestLight.Flags |= Light.LightSceneInfo->Proxy->IsInverseSquared() ? 0 : PATHTRACER_FLAG_NON_INVERSE_SQUARE_FALLOFF_MASK;
DestLight.Flags |= PATHTRACING_LIGHT_RECT;
if (Light.LightSceneInfo->Proxy->HasSourceTexture())
{
// there is an actual texture associated with this light, go look for it
FLightShaderParameters ShaderParameters;
Light.LightSceneInfo->Proxy->GetLightShaderParameters(ShaderParameters);
FRHITexture* TextureRHI = ShaderParameters.SourceTexture;
if (TextureRHI != nullptr)
{
// have we already given this texture an index?
// NOTE: linear search is ok since max texture is small
for (int Index = 0; Index < NextRectTextureIndex; Index++)
{
if (PassParameters->RectLightTexture[Index] == TextureRHI)
{
DestLight.RectLightTextureIndex = Index;
break;
}
}
if (DestLight.RectLightTextureIndex == -1 && NextRectTextureIndex < PATHTRACER_MAX_RECT_TEXTURES)
{
// first time we see this texture and we still have free slots available
// assign texture to next slot and store it in the light
DestLight.RectLightTextureIndex = NextRectTextureIndex;
PassParameters->RectLightTexture[NextRectTextureIndex] = TextureRHI;
NextRectTextureIndex++;
}
}
}
float Radius = 1.0f / LightParameters.InvRadius;
FVector Center = DestLight.Position;
FVector Normal = DestLight.Normal;
FVector Disc = FVector(
FMath::Sqrt(FMath::Clamp(1 - Normal.X * Normal.X, 0.0f, 1.0f)),
FMath::Sqrt(FMath::Clamp(1 - Normal.Y * Normal.Y, 0.0f, 1.0f)),
FMath::Sqrt(FMath::Clamp(1 - Normal.Z * Normal.Z, 0.0f, 1.0f))
);
// quad bbox is the bbox of the disc + the tip of the hemisphere
// TODO: is it worth trying to account for barndoors? seems unlikely to cut much empty space since the volume _inside_ the barndoor receives light
FVector Tip = Center + Normal * Radius;
DestLight.BoundMin = FVector::Min(Tip, Center - Radius * Disc);
DestLight.BoundMax = FVector::Max(Tip, Center + Radius * Disc);
break;
}
case LightType_Spot:
{
DestLight.Dimensions = FVector(LightParameters.SourceRadius, LightParameters.SoftSourceRadius, LightParameters.SourceLength);
DestLight.Shaping = LightParameters.SpotAngles;
DestLight.FalloffExponent = LightParameters.FalloffExponent;
DestLight.Flags |= Light.LightSceneInfo->Proxy->IsInverseSquared() ? 0 : PATHTRACER_FLAG_NON_INVERSE_SQUARE_FALLOFF_MASK;
DestLight.Flags |= PATHTRACING_LIGHT_SPOT;
float Radius = 1.0f / LightParameters.InvRadius;
FVector Center = DestLight.Position;
FVector Normal = DestLight.Normal;
FVector Disc = FVector(
FMath::Sqrt(FMath::Clamp(1 - Normal.X * Normal.X, 0.0f, 1.0f)),
FMath::Sqrt(FMath::Clamp(1 - Normal.Y * Normal.Y, 0.0f, 1.0f)),
FMath::Sqrt(FMath::Clamp(1 - Normal.Z * Normal.Z, 0.0f, 1.0f))
);
// box around ray from light center to tip of the cone
FVector Tip = Center + Normal * Radius;
DestLight.BoundMin = FVector::Min(Center, Tip);
DestLight.BoundMax = FVector::Max(Center, Tip);
// expand by disc around the farthest part of the cone
float CosOuter = LightParameters.SpotAngles.X;
float SinOuter = FMath::Sqrt(1.0f - CosOuter * CosOuter);
DestLight.BoundMin = FVector::Min(DestLight.BoundMin, Center + Radius * (Normal * CosOuter - Disc * SinOuter));
DestLight.BoundMax = FVector::Max(DestLight.BoundMax, Center + Radius * (Normal * CosOuter + Disc * SinOuter));
break;
}
case LightType_Point:
{
DestLight.Dimensions = FVector(LightParameters.SourceRadius, LightParameters.SoftSourceRadius, LightParameters.SourceLength);
DestLight.FalloffExponent = LightParameters.FalloffExponent;
DestLight.Flags |= Light.LightSceneInfo->Proxy->IsInverseSquared() ? 0 : PATHTRACER_FLAG_NON_INVERSE_SQUARE_FALLOFF_MASK;
DestLight.Flags |= PATHTRACING_LIGHT_POINT;
float Radius = 1.0f / LightParameters.InvRadius;
FVector Center = DestLight.Position;
// simple sphere of influence
DestLight.BoundMin = Center - FVector(Radius, Radius, Radius);
DestLight.BoundMax = Center + FVector(Radius, Radius, Radius);
break;
}
default:
{
// Just in case someone adds a new light type one day ...
checkNoEntry();
break;
}
}
}
{
// assign dummy textures to the remaining unused slots
for (int32 Index = NextRectTextureIndex; Index < PATHTRACER_MAX_RECT_TEXTURES; Index++)
{
PassParameters->RectLightTexture[Index] = GWhiteTexture->TextureRHI;
}
PassParameters->RectLightSampler = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
}
PassParameters->SceneLightCount = NumLights;
{
// Upload the buffer of lights to the GPU
uint32 NumCopyLights = FMath::Max(1u, NumLights); // need at least one since zero-sized buffers are not allowed
size_t DataSize = sizeof(FPathTracingLight) * NumCopyLights;
PassParameters->SceneLights = GraphBuilder.CreateSRV(FRDGBufferSRVDesc(CreateStructuredBuffer(GraphBuilder, TEXT("PathTracer.LightsBuffer"), sizeof(FPathTracingLight), NumCopyLights, Lights, DataSize, ERDGInitialDataFlags::NoCopy)));
}
if (CVarPathTracingVisibleLights.GetValueOnRenderThread() != 0)
{
PassParameters->SceneVisibleLightCount = PassParameters->SceneLightCount;
}
if (!IESLightProfilesMap.IsEmpty())
{
PassParameters->IESTexture = PrepareIESAtlas(IESLightProfilesMap, GraphBuilder);
}
else
{
PassParameters->IESTexture = GraphBuilder.RegisterExternalTexture(GSystemTextures.WhiteDummy);
}
PrepareLightGrid(GraphBuilder, &PassParameters->LightGridParameters, Lights, NumLights, NumInfiniteLights, PassParameters->SceneLights);
}
class FPathTracingCompositorPS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FPathTracingCompositorPS)
SHADER_USE_PARAMETER_STRUCT(FPathTracingCompositorPS, FGlobalShader)
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return ShouldCompileRayTracingShadersForProject(Parameters.Platform);
}
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D<float4>, RadianceTexture)
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer)
SHADER_PARAMETER(uint32, Iteration)
SHADER_PARAMETER(uint32, MaxSamples)
SHADER_PARAMETER(int, ProgressDisplayEnabled)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_SHADER_TYPE(, FPathTracingCompositorPS, TEXT("/Engine/Private/PathTracing/PathTracingCompositingPixelShader.usf"), TEXT("CompositeMain"), SF_Pixel);
void FDeferredShadingSceneRenderer::PreparePathTracing(const FSceneViewFamily& ViewFamily, TArray<FRHIRayTracingShader*>& OutRayGenShaders)
{
if (ViewFamily.EngineShowFlags.PathTracing
&& ShouldCompilePathTracingShadersForProject(ViewFamily.GetShaderPlatform()))
{
// Declare all RayGen shaders that require material closest hit shaders to be bound
auto RayGenShader = GetGlobalShaderMap(ViewFamily.GetShaderPlatform())->GetShader<FPathTracingRG>();
OutRayGenShaders.Add(RayGenShader.GetRayTracingShader());
}
}
void FSceneViewState::PathTracingInvalidate()
{
PathTracingRadianceRT.SafeRelease();
PathTracingAlbedoRT.SafeRelease();
PathTracingNormalRT.SafeRelease();
PathTracingRadianceDenoisedRT.SafeRelease();
PathTracingSampleIndex = 0;
}
uint32 FSceneViewState::GetPathTracingSampleIndex() const {
return PathTracingSampleIndex;
}
uint32 FSceneViewState::GetPathTracingSampleCount() const {
FPathTracingConfig* LastConfig = PathTracingLastConfig.Get();
if (LastConfig)
{
return LastConfig->PathTracingData.MaxSamples;
}
return 0;
}
BEGIN_SHADER_PARAMETER_STRUCT(FDenoiseTextureParameters, )
RDG_TEXTURE_ACCESS(InputTexture, ERHIAccess::CopySrc)
RDG_TEXTURE_ACCESS(InputAlbedo, ERHIAccess::CopySrc)
RDG_TEXTURE_ACCESS(InputNormal, ERHIAccess::CopySrc)
RDG_TEXTURE_ACCESS(OutputTexture, ERHIAccess::CopyDest)
END_SHADER_PARAMETER_STRUCT()
DECLARE_GPU_STAT_NAMED(Stat_GPU_PathTracing, TEXT("Path Tracing"));
void FDeferredShadingSceneRenderer::RenderPathTracing(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
TRDGUniformBufferRef<FSceneTextureUniformParameters> SceneTexturesUniformBuffer,
FRDGTextureRef SceneColorOutputTexture)
{
RDG_GPU_STAT_SCOPE(GraphBuilder, Stat_GPU_PathTracing);
RDG_EVENT_SCOPE(GraphBuilder, "Path Tracing");
if (!ensureMsgf(FDataDrivenShaderPlatformInfo::GetSupportsPathTracing(View.GetShaderPlatform()),
TEXT("Attempting to use path tracing on unsupported platform.")))
{
return;
}
if (CVarPathTracing.GetValueOnRenderThread() == 0)
{
// Path tracing is not enabled on this project (should not be seen by end-users since the menu entry to pick path tracing should be hidden)
// If they reach this code through ShowFlag manipulation, they may observe an incomplete image. Is there a way to inform the user here?
return;
}
FPathTracingConfig Config = {};
// Get current value of MaxSPP and reset render if it has changed
// NOTE: we ignore the CVar when using offline rendering
int32 SamplesPerPixelCVar = View.bIsOfflineRender ? -1 : CVarPathTracingSamplesPerPixel.GetValueOnRenderThread();
uint32 MaxSPP = SamplesPerPixelCVar > -1 ? SamplesPerPixelCVar : View.FinalPostProcessSettings.PathTracingSamplesPerPixel;
MaxSPP = FMath::Max(MaxSPP, 1u);
Config.LockedSamplingPattern = CVarPathTracingFrameIndependentTemporalSeed.GetValueOnRenderThread() == 0;
// compute an integer code of what show flags related to lights are currently enabled so we can detect changes
Config.LightShowFlags = 0;
Config.LightShowFlags |= View.Family->EngineShowFlags.SkyLighting ? 1 << 0 : 0;
Config.LightShowFlags |= View.Family->EngineShowFlags.DirectionalLights ? 1 << 1 : 0;
Config.LightShowFlags |= View.Family->EngineShowFlags.RectLights ? 1 << 2 : 0;
Config.LightShowFlags |= View.Family->EngineShowFlags.SpotLights ? 1 << 3 : 0;
Config.LightShowFlags |= View.Family->EngineShowFlags.PointLights ? 1 << 4 : 0;
Config.LightShowFlags |= View.Family->EngineShowFlags.TexturedLightProfiles ? 1 << 5 : 0;
PrepareShaderArgs(View, Config.PathTracingData);
Config.VisibleLights = CVarPathTracingVisibleLights.GetValueOnRenderThread() != 0;
Config.UseMISCompensation = Config.PathTracingData.MISMode == 2 && CVarPathTracingMISCompensation.GetValueOnRenderThread() != 0;
Config.ViewRect = View.ViewRect;
Config.LightGridResolution = FMath::RoundUpToPowerOfTwo(CVarPathTracingLightGridResolution.GetValueOnRenderThread());
Config.LightGridMaxCount = FMath::Clamp(CVarPathTracingLightGridMaxCount.GetValueOnRenderThread(), 1, RAY_TRACING_LIGHT_COUNT_MAXIMUM);
Config.PathTracingData.MaxSamples = MaxSPP;
bool FirstTime = false;
if (!View.ViewState->PathTracingLastConfig.IsValid())
{
View.ViewState->PathTracingLastConfig = MakePimpl<FPathTracingConfig>();
FirstTime = true; // we just initialized the option state for this view -- don't bother comparing in this case
}
if (FirstTime || Config.UseMISCompensation != View.ViewState->PathTracingLastConfig->UseMISCompensation)
{
// if the mode changes we need to rebuild the importance table
Scene->PathTracingSkylightTexture.SafeRelease();
Scene->PathTracingSkylightPdf.SafeRelease();
}
// If the scene has changed in some way (camera move, object movement, etc ...)
// we must invalidate the ViewState to start over from scratch
if (FirstTime || Config.IsDifferent(*View.ViewState->PathTracingLastConfig))
{
// remember the options we used for next time
*View.ViewState->PathTracingLastConfig = Config;
View.ViewState->PathTracingInvalidate();
}
// Setup temporal seed _after_ invalidation in case we got reset
if (Config.LockedSamplingPattern)
{
// Count samples from 0 for deterministic results
Config.PathTracingData.TemporalSeed = View.ViewState->PathTracingSampleIndex;
}
else
{
// Count samples from an ever-increasing counter to avoid screen-door effect
Config.PathTracingData.TemporalSeed = View.ViewState->PathTracingFrameIndex;
}
Config.PathTracingData.Iteration = View.ViewState->PathTracingSampleIndex;
// Prepare radiance buffer (will be shared with display pass)
FRDGTexture* RadianceTexture = nullptr;
FRDGTexture* AlbedoTexture = nullptr;
FRDGTexture* NormalTexture = nullptr;
if (View.ViewState->PathTracingRadianceRT)
{
// we already have a valid radiance texture, re-use it
RadianceTexture = GraphBuilder.RegisterExternalTexture(View.ViewState->PathTracingRadianceRT, TEXT("PathTracer.Radiance"));
AlbedoTexture = GraphBuilder.RegisterExternalTexture(View.ViewState->PathTracingAlbedoRT, TEXT("PathTracer.Albedo"));
NormalTexture = GraphBuilder.RegisterExternalTexture(View.ViewState->PathTracingNormalRT, TEXT("PathTracer.Normal"));
}
else
{
// First time through, need to make a new texture
FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(
View.ViewRect.Size(),
PF_A32B32G32R32F,
FClearValueBinding::None,
TexCreate_ShaderResource | TexCreate_UAV);
RadianceTexture = GraphBuilder.CreateTexture(Desc, TEXT("PathTracer.Radiance"), ERDGTextureFlags::MultiFrame);
AlbedoTexture = GraphBuilder.CreateTexture(Desc, TEXT("PathTracer.Albedo") , ERDGTextureFlags::MultiFrame);
NormalTexture = GraphBuilder.CreateTexture(Desc, TEXT("PathTracer.Normal") , ERDGTextureFlags::MultiFrame);
}
const bool bNeedsMoreRays = Config.PathTracingData.Iteration < MaxSPP;
if (bNeedsMoreRays)
{
FPathTracingRG::FParameters* PassParameters = GraphBuilder.AllocParameters<FPathTracingRG::FParameters>();
PassParameters->TLAS = View.GetRayTracingSceneViewChecked();
PassParameters->ViewUniformBuffer = View.ViewUniformBuffer;
PassParameters->PathTracingData = Config.PathTracingData;
// upload sky/lights data
SetLightParameters(GraphBuilder, PassParameters, Scene, View, Config.UseMISCompensation);
if (Config.PathTracingData.EnableDirectLighting == 0)
{
PassParameters->SceneVisibleLightCount = 0;
}
PassParameters->IESTextureSampler = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
PassParameters->RadianceTexture = GraphBuilder.CreateUAV(RadianceTexture);
PassParameters->AlbedoTexture = GraphBuilder.CreateUAV(AlbedoTexture);
PassParameters->NormalTexture = GraphBuilder.CreateUAV(NormalTexture);
PassParameters->SSProfilesTexture = GetSubsufaceProfileTexture_RT(GraphBuilder.RHICmdList)->GetShaderResourceRHI();
// TODO: in multi-gpu case, split image into tiles
PassParameters->TileOffset.X = 0;
PassParameters->TileOffset.Y = 0;
TShaderMapRef<FPathTracingRG> RayGenShader(View.ShaderMap);
ClearUnusedGraphResources(RayGenShader, PassParameters);
GraphBuilder.AddPass(
RDG_EVENT_NAME("Path Tracer Compute (%d x %d) Sample=%d/%d NumLights=%d", View.ViewRect.Size().X, View.ViewRect.Size().Y, View.ViewState->PathTracingSampleIndex, MaxSPP, PassParameters->SceneLightCount),
PassParameters,
ERDGPassFlags::Compute,
[PassParameters, RayGenShader, &View](FRHICommandListImmediate& RHICmdList)
{
FRHIRayTracingScene* RayTracingSceneRHI = View.GetRayTracingSceneChecked();
// Round up to coherent path tracing tile size to simplify pixel shuffling
// TODO: be careful not to write extra pixels past the boundary when using multi-gpu
const int32 TS = PATHTRACER_COHERENT_TILE_SIZE;
int32 DispatchSizeX = FMath::DivideAndRoundUp(View.ViewRect.Size().X, TS) * TS;
int32 DispatchSizeY = FMath::DivideAndRoundUp(View.ViewRect.Size().Y, TS) * TS;
FRayTracingShaderBindingsWriter GlobalResources;
SetShaderParameters(GlobalResources, RayGenShader, *PassParameters);
RHICmdList.RayTraceDispatch(
View.RayTracingMaterialPipeline,
RayGenShader.GetRayTracingShader(),
RayTracingSceneRHI, GlobalResources,
DispatchSizeX, DispatchSizeY
);
});
// After we are done, make sure we remember our texture for next time so that we can accumulate samples across frames
GraphBuilder.QueueTextureExtraction(RadianceTexture, &View.ViewState->PathTracingRadianceRT);
GraphBuilder.QueueTextureExtraction(AlbedoTexture , &View.ViewState->PathTracingAlbedoRT );
GraphBuilder.QueueTextureExtraction(NormalTexture , &View.ViewState->PathTracingNormalRT );
// Bump counters for next frame
++View.ViewState->PathTracingSampleIndex;
++View.ViewState->PathTracingFrameIndex;
}
FRDGTexture* DenoisedRadianceTexture = nullptr;
int DenoiserMode = CVarPathTracingDenoiser.GetValueOnRenderThread();
if (DenoiserMode < 0)
{
DenoiserMode = View.FinalPostProcessSettings.PathTracingEnableDenoiser;
}
const bool IsDenoiserEnabled = DenoiserMode != 0 && GPathTracingDenoiserFunc != nullptr;
if (IsDenoiserEnabled)
{
// request denoise if this is the last sample
bool NeedsDenoise = (Config.PathTracingData.Iteration + 1) == MaxSPP;
// also allow turning on the denoiser after the image has stopped accumulating samples
if (!bNeedsMoreRays)
{
// we aren't currently rendering, run the denoiser if we just turned it on
NeedsDenoise |= DenoiserMode != View.ViewState->PathTracingLastConfig->DenoiserMode;
}
if (View.ViewState->PathTracingRadianceDenoisedRT)
{
// we already have a texture for this
DenoisedRadianceTexture = GraphBuilder.RegisterExternalTexture(View.ViewState->PathTracingRadianceDenoisedRT, TEXT("PathTracer.DenoisedRadiance"));
}
if (NeedsDenoise)
{
if (DenoisedRadianceTexture == nullptr)
{
// First time through, need to make a new texture
FRDGTextureDesc RadianceTextureDesc = FRDGTextureDesc::Create2D(
View.ViewRect.Size(),
PF_A32B32G32R32F,
FClearValueBinding::None,
TexCreate_ShaderResource | TexCreate_UAV);
DenoisedRadianceTexture = GraphBuilder.CreateTexture(RadianceTextureDesc, TEXT("PathTracer.DenoisedRadiance"), ERDGTextureFlags::MultiFrame);
}
FDenoiseTextureParameters* DenoiseParameters = GraphBuilder.AllocParameters<FDenoiseTextureParameters>();
DenoiseParameters->InputTexture = RadianceTexture;
DenoiseParameters->InputAlbedo = AlbedoTexture;
DenoiseParameters->InputNormal = NormalTexture;
DenoiseParameters->OutputTexture = DenoisedRadianceTexture;
GraphBuilder.AddPass(RDG_EVENT_NAME("Path Tracer Denoiser Plugin"), DenoiseParameters, ERDGPassFlags::Readback,
[DenoiseParameters, DenoiserMode](FRHICommandListImmediate& RHICmdList)
{
GPathTracingDenoiserFunc(RHICmdList,
DenoiseParameters->InputTexture->GetRHI()->GetTexture2D(),
DenoiseParameters->InputAlbedo->GetRHI()->GetTexture2D(),
DenoiseParameters->InputNormal->GetRHI()->GetTexture2D(),
DenoiseParameters->OutputTexture->GetRHI()->GetTexture2D());
}
);
GraphBuilder.QueueTextureExtraction(DenoisedRadianceTexture, &View.ViewState->PathTracingRadianceDenoisedRT);
}
}
View.ViewState->PathTracingLastConfig->DenoiserMode = DenoiserMode;
// now add a pixel shader pass to display our Radiance buffer
FPathTracingCompositorPS::FParameters* DisplayParameters = GraphBuilder.AllocParameters<FPathTracingCompositorPS::FParameters>();
DisplayParameters->Iteration = Config.PathTracingData.Iteration;
DisplayParameters->MaxSamples = MaxSPP;
DisplayParameters->ProgressDisplayEnabled = CVarPathTracingProgressDisplay.GetValueOnRenderThread();
DisplayParameters->ViewUniformBuffer = View.ViewUniformBuffer;
DisplayParameters->RadianceTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::Create(DenoisedRadianceTexture ? DenoisedRadianceTexture : RadianceTexture));
DisplayParameters->RenderTargets[0] = FRenderTargetBinding(SceneColorOutputTexture, ERenderTargetLoadAction::ELoad);
FScreenPassTextureViewport Viewport(SceneColorOutputTexture, View.ViewRect);
// wiper mode - reveals the render below the path tracing display
// NOTE: we still path trace the full resolution even while wiping the cursor so that rendering does not get out of sync
if (CVarPathTracingWiperMode.GetValueOnRenderThread() != 0)
{
float DPIScale = FPlatformApplicationMisc::GetDPIScaleFactorAtPoint(View.CursorPos.X, View.CursorPos.Y);
Viewport.Rect.Min.X = View.CursorPos.X / DPIScale;
}
TShaderMapRef<FPathTracingCompositorPS> PixelShader(View.ShaderMap);
AddDrawScreenPass(
GraphBuilder,
RDG_EVENT_NAME("Path Tracer Display (%d x %d)", View.ViewRect.Size().X, View.ViewRect.Size().Y),
View,
Viewport,
Viewport,
PixelShader,
DisplayParameters
);
}
#endif