Files
UnrealEngine/Engine/Source/Runtime/Renderer/Private/Lumen/LumenRadiosity.cpp
2025-05-18 13:04:45 +08:00

1281 lines
61 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "LumenRadiosity.h"
#include "RendererPrivate.h"
#include "ScenePrivate.h"
#include "SceneUtils.h"
#include "PipelineStateCache.h"
#include "ShaderParameterStruct.h"
#include "PixelShaderUtils.h"
#include "ReflectionEnvironment.h"
#include "DistanceFieldAmbientOcclusion.h"
#include "LumenRadianceCache.h"
#include "LumenSceneLighting.h"
#include "LumenTracingUtils.h"
#include "LumenHardwareRayTracingCommon.h"
#include "BlueNoise.h"
#include "ShaderPrintParameters.h"
int32 GLumenRadiosity = 1;
FAutoConsoleVariableRef CVarLumenRadiosity(
TEXT("r.LumenScene.Radiosity"),
GLumenRadiosity,
TEXT("Whether to enable the Radiosity, which is an indirect lighting gather from the Surface Cache that provides multibounce diffuse."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
int32 GLumenRadiosityProbeSpacing = 4;
FAutoConsoleVariableRef CVarLumenRadiosityProbeSpacing(
TEXT("r.LumenScene.Radiosity.ProbeSpacing"),
GLumenRadiosityProbeSpacing,
TEXT("Distance between probes, in Surface Cache texels"),
ECVF_Scalability | ECVF_RenderThreadSafe
);
int32 GLumenRadiosityHemisphereProbeResolution = 4;
FAutoConsoleVariableRef CVarLumenRadiosityHemisphereProbeResolution(
TEXT("r.LumenScene.Radiosity.HemisphereProbeResolution"),
GLumenRadiosityHemisphereProbeResolution,
TEXT("Number of traces along one dimension of the hemisphere probe layout."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
int32 GLumenRadiositySpatialFilterProbes = 1;
FAutoConsoleVariableRef CVarLumenRadiositySpatialFilterProbes(
TEXT("r.LumenScene.Radiosity.SpatialFilterProbes"),
GLumenRadiositySpatialFilterProbes,
TEXT("Whether to spatially filter Radiosity probes. Filtering reduces noise but increases leaking."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
int32 GLumenRadiositySpatialFilterProbesKernelSize = 1;
FAutoConsoleVariableRef CVarLumenRadiositySpatialFilterProbesKernelSize(
TEXT("r.LumenScene.Radiosity.SpatialFilterProbes.KernelSize"),
GLumenRadiositySpatialFilterProbesKernelSize,
TEXT("Larger kernels reduce noise but increase leaking."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
int32 GRadiosityFilteringProbePlaneWeighting = 1;
FAutoConsoleVariableRef CVarRadiosityFilteringProbePlaneWeighting(
TEXT("r.LumenScene.Radiosity.ProbePlaneWeighting"),
GRadiosityFilteringProbePlaneWeighting,
TEXT("Whether to weight Radiosity probes by plane distance, useful to prevent leaking."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
int32 GRadiosityFilteringProbeOcclusion = 0;
FAutoConsoleVariableRef CVarRadiosityFilteringProbeOcclusion(
TEXT("r.LumenScene.Radiosity.ProbeOcclusion"),
GRadiosityFilteringProbeOcclusion,
TEXT("Whether to depth test against the probe hit depths during interpolation and filtering to reduce leaking. Not available with Software Ray Tracing due to imprecision."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
float GRadiosityFilteringProbeOcclusionStrength = .5f;
FAutoConsoleVariableRef CVarRadiosityFilteringProbeOcclusionStrength(
TEXT("r.LumenScene.Radiosity.ProbeOcclusionStrength"),
GRadiosityFilteringProbeOcclusionStrength,
TEXT("Strength of probe occlusion. 0 = No probe occlusion, 1 = Attempt to stop all leaking, but has self-occlusion artifacts, .5 (default) = tradeoff between the extremes."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
float GRadiosityProbePlaneWeightingDepthScale = -100.0f;
FAutoConsoleVariableRef CVarRadiosityProbePlaneWeightingDepthScale(
TEXT("r.LumenScene.Radiosity.SpatialFilterProbes.PlaneWeightingDepthScale"),
GRadiosityProbePlaneWeightingDepthScale,
TEXT("Controls the distance at which probes can be interpolated from. Higher values introduce leaking."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
float GLumenRadiosityMaxRayIntensity = 40.0f;
FAutoConsoleVariableRef CVarLumenRadiosityMaxRayIntensity(
TEXT("r.LumenScene.Radiosity.MaxRayIntensity"),
GLumenRadiosityMaxRayIntensity,
TEXT("Clamps Radiosity trace intensity, relative to current view exposure. Useful for reducing artifacts from small bright emissive sources, but loses energy and adds view dependence."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
float GLumenRadiosityDistanceFieldSurfaceBias = 10.0f;
FAutoConsoleVariableRef CVarLumenRadiositySurfaceBias(
TEXT("r.LumenScene.Radiosity.DistanceFieldSurfaceBias"),
GLumenRadiosityDistanceFieldSurfaceBias,
TEXT("."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
float GLumenRadiosityDistanceFieldSurfaceSlopeBias = 5.0f;
FAutoConsoleVariableRef CVarLumenRadiosityDistanceFieldSurfaceBias(
TEXT("r.LumenScene.Radiosity.DistanceFieldSurfaceSlopeBias"),
GLumenRadiosityDistanceFieldSurfaceBias,
TEXT("."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
float GLumenRadiosityHardwareRayTracingSurfaceBias = 0.1f;
FAutoConsoleVariableRef CVarLumenRadiosityHardwareRayTracingSurfaceBias(
TEXT("r.LumenScene.Radiosity.HardwareRayTracing.SurfaceBias"),
GLumenRadiosityHardwareRayTracingSurfaceBias,
TEXT("."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
float GLumenRadiosityHardwareRayTracingSurfaceSlopeBias = 0.2f;
FAutoConsoleVariableRef CVarLumenRadiosityHardwareRayTracingSlopeSurfaceBias(
TEXT("r.LumenScene.Radiosity.HardwareRayTracing.SlopeSurfaceBias"),
GLumenRadiosityHardwareRayTracingSurfaceSlopeBias,
TEXT("."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarLumenRadiosityHardwareRayTracing(
TEXT("r.LumenScene.Radiosity.HardwareRayTracing"),
1,
TEXT("Enables hardware ray tracing for radiosity (default = 1)."),
ECVF_RenderThreadSafe
);
int32 GLumenRadiosityTemporalAccumulation = 1;
FAutoConsoleVariableRef CVarLumenRadiosityTemporalAccumulation(
TEXT("r.LumenScene.Radiosity.Temporal"),
GLumenRadiosityTemporalAccumulation,
TEXT("Whether to use temporal super sampling on Radiosity. Increases quality, but also adds latency to the speed that lighting changes propagate, and animated noise in the results."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
int32 GLumenRadiosityTemporalMaxFramesAccumulated = 4;
FAutoConsoleVariableRef CVarLumenRadiosityTemporalMaxFramesAccumulated(
TEXT("r.LumenScene.Radiosity.Temporal.MaxFramesAccumulated"),
GLumenRadiosityTemporalMaxFramesAccumulated,
TEXT("Lower values cause the temporal filter to propagate lighting changes faster, but also increase flickering from noise."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
int32 GLumenRadiosityFixedJitterIndex = -1;
FAutoConsoleVariableRef CVarLumenRadiosityFixedJitterIndex(
TEXT("r.LumenScene.Radiosity.Temporal.FixedJitterIndex"),
GLumenRadiosityFixedJitterIndex,
TEXT("If zero or greater, overrides the temporal jitter index with a fixed index. Useful for debugging and inspecting sampling patterns."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarLumenSceneRadiosityVisualizeProbes(
TEXT("r.LumenScene.Radiosity.VisualizeProbes"),
0,
TEXT("Whether to visualize radiosity probes."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarLumenSceneRadiosityVisualizeProbeRadius(
TEXT("r.LumenScene.Radiosity.VisualizeProbeRadius"),
10.0f,
TEXT("Radius of a visualized radiosity probe."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<bool> CVarLumenSceneRadiosityDebug(
TEXT("r.LumenScene.Radiosity.Debug"),
false,
TEXT("Whether to enable debug mode, which prints various extra debug information from shaders."),
ECVF_RenderThreadSafe);
// Used for all Lumen Radiosity shaders
BEGIN_SHADER_PARAMETER_STRUCT(FLumenRadiosityCommonParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_STRUCT_INCLUDE(ShaderPrint::FShaderParameters, ShaderPrintUniformBuffer)
END_SHADER_PARAMETER_STRUCT()
namespace LumenRadiosity
{
void AddRadiosityPass(
FRDGBuilder& GraphBuilder,
const FScene* Scene,
const TArray<FViewInfo>& Views,
bool bRenderSkylight,
FLumenSceneData& LumenSceneData,
const FLumenSceneFrameTemporaries& FrameTemporaries,
const LumenRadiosity::FFrameTemporaries& RadiosityFrameTemporaries,
const FLumenCardUpdateContext& CardUpdateContext,
ERDGPassFlags ComputePassFlags);
uint32 GetRadiosityProbeSpacing(const FViewInfo& View)
{
int32 RadiosityProbeSpacing = GLumenRadiosityProbeSpacing;
if (View.FinalPostProcessSettings.LumenSceneLightingQuality >= 6)
{
RadiosityProbeSpacing /= 2;
}
return FMath::RoundUpToPowerOfTwo(FMath::Clamp<uint32>(RadiosityProbeSpacing, 1, Lumen::CardTileSize));
}
int32 GetHemisphereProbeResolution(const FViewInfo& View)
{
const float LumenSceneLightingQuality = FMath::Clamp<float>(View.FinalPostProcessSettings.LumenSceneLightingQuality, .5f, 4.0f);
return FMath::Clamp<int32>(GLumenRadiosityHemisphereProbeResolution * FMath::Sqrt(LumenSceneLightingQuality), 1, 16);
}
bool UseTemporalAccumulation()
{
return GLumenRadiosityTemporalAccumulation != 0
&& UE::PixelFormat::HasCapabilities(Lumen::GetIndirectLightingAtlasFormat(), EPixelFormatCapabilities::TypedUAVLoad)
&& UE::PixelFormat::HasCapabilities(Lumen::GetNumFramesAccumulatedAtlasFormat(), EPixelFormatCapabilities::TypedUAVLoad);
}
bool UseProbeOcclusion()
{
return GRadiosityFilteringProbeOcclusion != 0 && GRadiosityFilteringProbeOcclusionStrength > 0.0f;
}
}
bool LumenRadiosity::IsEnabled(const FSceneViewFamily& ViewFamily)
{
return GLumenRadiosity != 0
&& ViewFamily.EngineShowFlags.LumenSecondaryBounces;
}
bool Lumen::UseHardwareRayTracedRadiosity(const FSceneViewFamily& ViewFamily)
{
#if RHI_RAYTRACING
return IsRayTracingEnabled()
&& Lumen::UseHardwareRayTracing(ViewFamily)
&& (CVarLumenRadiosityHardwareRayTracing.GetValueOnRenderThread() != 0);
#else
return false;
#endif
}
bool Lumen::ShouldRenderRadiosityHardwareRayTracing(const FSceneViewFamily& ViewFamily)
{
return UseHardwareRayTracedRadiosity(ViewFamily) && LumenRadiosity::IsEnabled(ViewFamily);
}
uint32 LumenRadiosity::GetAtlasDownsampleFactor()
{
// Must match RADIOSITY_ATLAS_DOWNSAMPLE_FACTOR
return 1;
}
FIntPoint FLumenSceneData::GetRadiosityAtlasSize() const
{
return PhysicalAtlasSize / LumenRadiosity::GetAtlasDownsampleFactor();
}
class FBuildRadiosityTilesCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FBuildRadiosityTilesCS);
SHADER_USE_PARAMETER_STRUCT(FBuildRadiosityTilesCS, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
RDG_BUFFER_ACCESS(IndirectArgBuffer, ERHIAccess::IndirectArgs)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenCardScene, LumenCardScene)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<uint>, RWCardTileAllocator)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<uint>, RWCardTileData)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<uint>, CardPageIndexAllocator)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<uint>, CardPageIndexData)
SHADER_PARAMETER(uint32, NumViews)
SHADER_PARAMETER(uint32, MaxCardTiles)
SHADER_PARAMETER_ARRAY(FMatrix44f, FrustumTranslatedWorldToClip, [LUMEN_MAX_VIEWS])
SHADER_PARAMETER_ARRAY(FVector4f, PreViewTranslationHigh, [LUMEN_MAX_VIEWS])
SHADER_PARAMETER_ARRAY(FVector4f, PreViewTranslationLow, [LUMEN_MAX_VIEWS])
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return DoesPlatformSupportLumenGI(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize());
}
static int32 GetGroupSize()
{
return 8;
}
};
IMPLEMENT_GLOBAL_SHADER(FBuildRadiosityTilesCS, "/Engine/Private/Lumen/Radiosity/LumenRadiosityCulling.usf", "BuildRadiosityTilesCS", SF_Compute);
enum class ERadiosityIndirectArgs
{
NumTracesDiv64 = 0 * sizeof(FRHIDispatchIndirectParameters),
NumTracesDiv32 = 1 * sizeof(FRHIDispatchIndirectParameters),
ThreadPerProbe = 2 * sizeof(FRHIDispatchIndirectParameters),
ThreadPerRadiosityTexel = 3 * sizeof(FRHIDispatchIndirectParameters),
HardwareRayTracingThreadPerTrace = 4 * sizeof(FRHIDispatchIndirectParameters),
MAX = 5
};
BEGIN_SHADER_PARAMETER_STRUCT(FLumenRadiosityTexelTraceParameters, )
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<uint>, CardTileAllocator)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<uint>, CardTileData)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, TraceRadianceAtlas)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D<float>, TraceHitDistanceAtlas)
SHADER_PARAMETER(FIntPoint, RadiosityAtlasSize)
SHADER_PARAMETER(uint32, ProbeSpacingInRadiosityTexels)
SHADER_PARAMETER(uint32, ProbeSpacingInRadiosityTexelsDivideShift)
SHADER_PARAMETER(uint32, RadiosityTileSize)
SHADER_PARAMETER(uint32, HemisphereProbeResolution)
SHADER_PARAMETER(uint32, NumTracesPerProbe)
SHADER_PARAMETER(float, ProbeOcclusionStrength)
SHADER_PARAMETER(int32, FixedJitterIndex)
SHADER_PARAMETER(uint32, MaxFramesAccumulated)
SHADER_PARAMETER(uint32, NumViews)
SHADER_PARAMETER(uint32, ViewIndex)
SHADER_PARAMETER(uint32, MaxCardTiles)
SHADER_PARAMETER(FVector3f, TargetFormatQuantizationError)
SHADER_PARAMETER_STRUCT_REF(FBlueNoise, BlueNoise)
END_SHADER_PARAMETER_STRUCT()
class FLumenRadiosityIndirectArgsCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FLumenRadiosityIndirectArgsCS)
SHADER_USE_PARAMETER_STRUCT(FLumenRadiosityIndirectArgsCS, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FLumenRadiosityCommonParameters, RadiosityCommonParameters)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, RWIndirectArgs)
SHADER_PARAMETER_STRUCT_INCLUDE(FLumenRadiosityTexelTraceParameters, RadiosityTexelTraceParameters)
SHADER_PARAMETER(uint32, HardwareRayTracingThreadGroupSize)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return DoesPlatformSupportLumenGI(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize());
}
static int32 GetGroupSize()
{
return 64;
}
};
IMPLEMENT_GLOBAL_SHADER(FLumenRadiosityIndirectArgsCS, "/Engine/Private/Lumen/Radiosity/LumenRadiosity.usf", "LumenRadiosityIndirectArgsCS", SF_Compute);
class FLumenRadiosityDistanceFieldTracingCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FLumenRadiosityDistanceFieldTracingCS)
SHADER_USE_PARAMETER_STRUCT(FLumenRadiosityDistanceFieldTracingCS, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
RDG_BUFFER_ACCESS(IndirectArgs, ERHIAccess::IndirectArgs)
SHADER_PARAMETER_STRUCT_INCLUDE(FLumenRadiosityTexelTraceParameters, RadiosityTexelTraceParameters)
SHADER_PARAMETER_STRUCT_INCLUDE(FLumenCardTracingParameters, TracingParameters)
SHADER_PARAMETER_STRUCT_INCLUDE(FLumenIndirectTracingParameters, IndirectTracingParameters)
SHADER_PARAMETER(float, MaxRayIntensity)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWTraceRadianceAtlas)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWTraceHitDistanceAtlas)
END_SHADER_PARAMETER_STRUCT()
class FThreadGroupSize32 : SHADER_PERMUTATION_BOOL("THREADGROUP_SIZE_32");
class FTraceGlobalSDF : SHADER_PERMUTATION_BOOL("TRACE_GLOBAL_SDF");
class FSimpleCoverageBasedExpand : SHADER_PERMUTATION_BOOL("GLOBALSDF_SIMPLE_COVERAGE_BASED_EXPAND");
class FProbeOcclusion : SHADER_PERMUTATION_BOOL("PROBE_OCCLUSION");
using FPermutationDomain = TShaderPermutationDomain<FThreadGroupSize32, FTraceGlobalSDF, FSimpleCoverageBasedExpand, FProbeOcclusion>;
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
const FPermutationDomain PermutationVector(Parameters.PermutationId);
if (!PermutationVector.Get<FTraceGlobalSDF>() && PermutationVector.Get<FSimpleCoverageBasedExpand>())
{
return false;
}
return DoesPlatformSupportLumenGI(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("ENABLE_DYNAMIC_SKY_LIGHT"), 1);
OutEnvironment.CompilerFlags.Add(CFLAG_Wave32);
}
};
IMPLEMENT_GLOBAL_SHADER(FLumenRadiosityDistanceFieldTracingCS, "/Engine/Private/Lumen/Radiosity/LumenRadiosity.usf", "LumenRadiosityDistanceFieldTracingCS", SF_Compute);
#if RHI_RAYTRACING
class FLumenRadiosityHardwareRayTracing : public FLumenHardwareRayTracingShaderBase
{
DECLARE_LUMEN_RAYTRACING_SHADER(FLumenRadiosityHardwareRayTracing)
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FLumenHardwareRayTracingShaderBase::FSharedParameters, SharedParameters)
RDG_BUFFER_ACCESS(HardwareRayTracingIndirectArgs, ERHIAccess::IndirectArgs | ERHIAccess::SRVCompute)
SHADER_PARAMETER_STRUCT_INCLUDE(FLumenRadiosityTexelTraceParameters, RadiosityTexelTraceParameters)
SHADER_PARAMETER(uint32, NumThreadsToDispatch)
SHADER_PARAMETER(float, MinTraceDistance)
SHADER_PARAMETER(float, MaxTraceDistance)
SHADER_PARAMETER(float, SurfaceBias)
SHADER_PARAMETER(float, HeightfieldSurfaceBias)
SHADER_PARAMETER(float, MaxRayIntensity)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWTraceRadianceAtlas)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWTraceHitDistanceAtlas)
END_SHADER_PARAMETER_STRUCT()
class FAvoidSelfIntersectionsMode : SHADER_PERMUTATION_ENUM_CLASS("AVOID_SELF_INTERSECTIONS_MODE", LumenHardwareRayTracing::EAvoidSelfIntersectionsMode);
class FSurfaceCacheAlphaMasking : SHADER_PERMUTATION_BOOL("SURFACE_CACHE_ALPHA_MASKING");
class FProbeOcclusion : SHADER_PERMUTATION_BOOL("PROBE_OCCLUSION");
using FPermutationDomain = TShaderPermutationDomain<FLumenHardwareRayTracingShaderBase::FBasePermutationDomain, FAvoidSelfIntersectionsMode, FSurfaceCacheAlphaMasking, FProbeOcclusion>;
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, Lumen::ERayTracingShaderDispatchType ShaderDispatchType, FShaderCompilerEnvironment& OutEnvironment)
{
FLumenHardwareRayTracingShaderBase::ModifyCompilationEnvironment(Parameters, ShaderDispatchType, Lumen::ESurfaceCacheSampling::HighResPages, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize());
OutEnvironment.SetDefine(TEXT("ENABLE_DYNAMIC_SKY_LIGHT"), 1);
}
static ERayTracingPayloadType GetRayTracingPayloadType(const int32 PermutationId)
{
return ERayTracingPayloadType::LumenMinimal;
}
static int32 GetGroupSize()
{
return 64;
}
};
IMPLEMENT_LUMEN_RAYGEN_AND_COMPUTE_RAYTRACING_SHADERS(FLumenRadiosityHardwareRayTracing)
IMPLEMENT_GLOBAL_SHADER(FLumenRadiosityHardwareRayTracingRGS, "/Engine/Private/Lumen/Radiosity/LumenRadiosityHardwareRayTracing.usf", "LumenRadiosityHardwareRayTracingRGS", SF_RayGen);
IMPLEMENT_GLOBAL_SHADER(FLumenRadiosityHardwareRayTracingCS, "/Engine/Private/Lumen/Radiosity/LumenRadiosityHardwareRayTracing.usf", "LumenRadiosityHardwareRayTracingCS", SF_Compute);
void FDeferredShadingSceneRenderer::PrepareLumenHardwareRayTracingRadiosityLumenMaterial(const FViewInfo& View, TArray<FRHIRayTracingShader*>& OutRayGenShaders)
{
if (Lumen::ShouldRenderRadiosityHardwareRayTracing(*View.Family) && !Lumen::UseHardwareInlineRayTracing(*View.Family))
{
FLumenRadiosityHardwareRayTracingRGS::FPermutationDomain PermutationVector;
PermutationVector.Set<FLumenRadiosityHardwareRayTracingRGS::FAvoidSelfIntersectionsMode>(LumenHardwareRayTracing::GetAvoidSelfIntersectionsMode());
PermutationVector.Set<FLumenRadiosityHardwareRayTracingRGS::FSurfaceCacheAlphaMasking>(LumenHardwareRayTracing::UseSurfaceCacheAlphaMasking());
PermutationVector.Set<FLumenRadiosityHardwareRayTracingRGS::FProbeOcclusion>(LumenRadiosity::UseProbeOcclusion());
TShaderRef<FLumenRadiosityHardwareRayTracingRGS> RayGenerationShader = View.ShaderMap->GetShader<FLumenRadiosityHardwareRayTracingRGS>(PermutationVector);
OutRayGenShaders.Add(RayGenerationShader.GetRayTracingShader());
}
}
#endif // #if RHI_RAYTRACING
class FLumenRadiositySpatialFilterProbeRadiance : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FLumenRadiositySpatialFilterProbeRadiance)
SHADER_USE_PARAMETER_STRUCT(FLumenRadiositySpatialFilterProbeRadiance, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
RDG_BUFFER_ACCESS(IndirectArgs, ERHIAccess::IndirectArgs)
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenCardScene, LumenCardScene)
SHADER_PARAMETER_STRUCT_INCLUDE(FLumenRadiosityTexelTraceParameters, RadiosityTexelTraceParameters)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWFilteredTraceRadianceAtlas)
SHADER_PARAMETER(float, ProbePlaneWeightingDepthScale)
END_SHADER_PARAMETER_STRUCT()
class FPlaneWeighting : SHADER_PERMUTATION_BOOL("FILTERING_PLANE_WEIGHTING");
class FProbeOcclusion : SHADER_PERMUTATION_BOOL("FILTERING_PROBE_OCCLUSION");
class FKernelSize : SHADER_PERMUTATION_INT("FILTERING_KERNEL_SIZE", 3);
using FPermutationDomain = TShaderPermutationDomain<FPlaneWeighting, FProbeOcclusion, FKernelSize>;
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return DoesPlatformSupportLumenGI(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize());
}
static int32 GetGroupSize()
{
return 64;
}
};
IMPLEMENT_GLOBAL_SHADER(FLumenRadiositySpatialFilterProbeRadiance, "/Engine/Private/Lumen/Radiosity/LumenRadiosity.usf", "LumenRadiositySpatialFilterProbeRadiance", SF_Compute);
class FLumenRadiosityConvertToSH : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FLumenRadiosityConvertToSH)
SHADER_USE_PARAMETER_STRUCT(FLumenRadiosityConvertToSH, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWRadiosityProbeSHRedAtlas)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWRadiosityProbeSHGreenAtlas)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWRadiosityProbeSHBlueAtlas)
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenCardScene, LumenCardScene)
SHADER_PARAMETER_STRUCT_INCLUDE(FLumenRadiosityTexelTraceParameters, RadiosityTexelTraceParameters)
RDG_BUFFER_ACCESS(IndirectArgs, ERHIAccess::IndirectArgs)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return DoesPlatformSupportLumenGI(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize());
}
static int32 GetGroupSize()
{
return 64;
}
};
IMPLEMENT_GLOBAL_SHADER(FLumenRadiosityConvertToSH, "/Engine/Private/Lumen/Radiosity/LumenRadiosity.usf", "LumenRadiosityConvertToSH", SF_Compute);
class FLumenRadiosityIntegrateCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FLumenRadiosityIntegrateCS)
SHADER_USE_PARAMETER_STRUCT(FLumenRadiosityIntegrateCS, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
RDG_BUFFER_ACCESS(IndirectArgs, ERHIAccess::IndirectArgs)
SHADER_PARAMETER_STRUCT_INCLUDE(FLumenRadiosityCommonParameters, RadiosityCommonParameters)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenCardScene, LumenCardScene)
SHADER_PARAMETER_STRUCT_INCLUDE(FLumenRadiosityTexelTraceParameters, RadiosityTexelTraceParameters)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWRadiosityAtlas)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWRadiosityNumFramesAccumulatedAtlas)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, RadiosityProbeSHRedAtlas)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, RadiosityProbeSHGreenAtlas)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, RadiosityProbeSHBlueAtlas)
SHADER_PARAMETER(float, ProbePlaneWeightingDepthScale)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSubstrateGlobalUniformParameters, Substrate)
END_SHADER_PARAMETER_STRUCT()
class FPlaneWeighting : SHADER_PERMUTATION_BOOL("INTERPOLATION_PLANE_WEIGHTING");
class FProbeOcclusion : SHADER_PERMUTATION_BOOL("INTERPOLATION_PROBE_OCCLUSION");
class FTemporalAccumulation : SHADER_PERMUTATION_BOOL("TEMPORAL_ACCUMULATION");
using FPermutationDomain = TShaderPermutationDomain<FPlaneWeighting, FProbeOcclusion, FTemporalAccumulation>;
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return DoesPlatformSupportLumenGI(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize());
OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads);
}
static int32 GetGroupSize()
{
return 64;
}
};
IMPLEMENT_GLOBAL_SHADER(FLumenRadiosityIntegrateCS, "/Engine/Private/Lumen/Radiosity/LumenRadiosity.usf", "LumenRadiosityIntegrateCS", SF_Compute);
FRDGTextureRef RegisterOrCreateRadiosityAtlas(
FRDGBuilder& GraphBuilder,
const TRefCountPtr<IPooledRenderTarget>& AtlasRT,
const TCHAR* AtlasName,
FIntPoint AtlasSize,
EPixelFormat AtlasFormat,
bool& bIndirectLightingHistoryValid)
{
FRDGTextureRef AtlasTexture = AtlasRT ? GraphBuilder.RegisterExternalTexture(AtlasRT) : nullptr;
if (!AtlasTexture || AtlasTexture->Desc.Extent != AtlasSize || AtlasTexture->Desc.Format != AtlasFormat)
{
AtlasTexture = GraphBuilder.CreateTexture(
FRDGTextureDesc::Create2D(AtlasSize, AtlasFormat, FClearValueBinding::Black, TexCreate_ShaderResource | TexCreate_UAV),
AtlasName);
bIndirectLightingHistoryValid = false;
}
return AtlasTexture;
}
void LumenRadiosity::InitFrameTemporaries(FRDGBuilder& GraphBuilder, const FLumenSceneData& LumenSceneData, const FSceneViewFamily& ViewFamily, const TArray<FViewInfo>& Views, LumenRadiosity::FFrameTemporaries& RadiosityFrameTemporaries)
{
if (LumenRadiosity::IsEnabled(ViewFamily) && LumenSceneData.bFinalLightingAtlasContentsValid)
{
const FViewInfo& FirstView = Views[0];
RadiosityFrameTemporaries.bIndirectLightingHistoryValid = true;
RadiosityFrameTemporaries.ProbeSpacing = LumenRadiosity::GetRadiosityProbeSpacing(FirstView);
RadiosityFrameTemporaries.HemisphereProbeResolution = LumenRadiosity::GetHemisphereProbeResolution(FirstView);
RadiosityFrameTemporaries.ProbeAtlasSize = FIntPoint::DivideAndRoundUp(LumenSceneData.GetPhysicalAtlasSize(), RadiosityFrameTemporaries.ProbeSpacing);
RadiosityFrameTemporaries.ProbeTracingAtlasSize = RadiosityFrameTemporaries.ProbeAtlasSize * FIntPoint(RadiosityFrameTemporaries.HemisphereProbeResolution, RadiosityFrameTemporaries.HemisphereProbeResolution);
RadiosityFrameTemporaries.TraceRadianceAtlas = RegisterOrCreateRadiosityAtlas(
GraphBuilder,
LumenSceneData.RadiosityTraceRadianceAtlas,
TEXT("Lumen.Radiosity.TraceRadianceAtlas"),
RadiosityFrameTemporaries.ProbeTracingAtlasSize,
Lumen::GetLightingDataFormat(),
RadiosityFrameTemporaries.bIndirectLightingHistoryValid);
RadiosityFrameTemporaries.bUseProbeOcclusion = LumenRadiosity::UseProbeOcclusion()
// Self intersection from grazing angle traces causes noise that breaks probe occlusion
&& Lumen::UseHardwareRayTracedRadiosity(*FirstView.Family);
if (RadiosityFrameTemporaries.bUseProbeOcclusion)
{
RadiosityFrameTemporaries.TraceHitDistanceAtlas = RegisterOrCreateRadiosityAtlas(
GraphBuilder,
LumenSceneData.RadiosityTraceHitDistanceAtlas,
TEXT("Lumen.Radiosity.TraceHitDistanceAtlas"),
RadiosityFrameTemporaries.ProbeTracingAtlasSize,
PF_R16F,
RadiosityFrameTemporaries.bIndirectLightingHistoryValid);
}
else
{
RadiosityFrameTemporaries.TraceHitDistanceAtlas = nullptr;
}
RadiosityFrameTemporaries.ProbeSHRedAtlas = RegisterOrCreateRadiosityAtlas(
GraphBuilder,
LumenSceneData.RadiosityProbeSHRedAtlas,
TEXT("Lumen.Radiosity.ProbeSHRedAtlas"),
RadiosityFrameTemporaries.ProbeAtlasSize,
PF_FloatRGBA,
RadiosityFrameTemporaries.bIndirectLightingHistoryValid);
RadiosityFrameTemporaries.ProbeSHGreenAtlas = RegisterOrCreateRadiosityAtlas(
GraphBuilder,
LumenSceneData.RadiosityProbeSHGreenAtlas,
TEXT("Lumen.Radiosity.ProbeSHGreenAtlas"),
RadiosityFrameTemporaries.ProbeAtlasSize,
PF_FloatRGBA,
RadiosityFrameTemporaries.bIndirectLightingHistoryValid);
RadiosityFrameTemporaries.ProbeSHBlueAtlas = RegisterOrCreateRadiosityAtlas(
GraphBuilder,
LumenSceneData.RadiosityProbeSHBlueAtlas,
TEXT("Lumen.Radiosity.ProbeSHBlueAtlas"),
RadiosityFrameTemporaries.ProbeAtlasSize,
PF_FloatRGBA,
RadiosityFrameTemporaries.bIndirectLightingHistoryValid);
}
}
void LumenRadiosity::AddRadiosityPass(
FRDGBuilder& GraphBuilder,
const FScene* Scene,
const TArray<FViewInfo>& Views,
bool bRenderSkylight,
FLumenSceneData& LumenSceneData,
const FLumenSceneFrameTemporaries& FrameTemporaries,
const LumenRadiosity::FFrameTemporaries& RadiosityFrameTemporaries,
const FLumenCardUpdateContext& CardUpdateContext,
ERDGPassFlags ComputePassFlags)
{
int32 NumViewOrigins = FrameTemporaries.ViewOrigins.Num();
const uint32 MaxCardTiles = CardUpdateContext.MaxUpdateTiles;
FRDGBufferRef CardTileAllocator = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), NumViewOrigins), TEXT("Lumen.Radiosity.CardTileAllocator"));
FRDGBufferRef CardTiles = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), MaxCardTiles * NumViewOrigins), TEXT("Lumen.Radiosity.CardTiles"));
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(CardTileAllocator), 0, ComputePassFlags);
FLumenRadiosityCommonParameters RadiosityCommonParameters;
RadiosityCommonParameters.View = Views[0].ViewUniformBuffer;
if (CVarLumenSceneRadiosityDebug.GetValueOnRenderThread())
{
ShaderPrint::SetEnabled(true);
ShaderPrint::RequestSpaceForLines(256 * 1024u);
ShaderPrint::SetParameters(GraphBuilder, Views[0].ShaderPrintData, RadiosityCommonParameters.ShaderPrintUniformBuffer);
}
const uint32 RadiosityTileSize = Lumen::CardTileSize / RadiosityFrameTemporaries.ProbeSpacing;
// Setup common radiosity tracing parameters
FLumenRadiosityTexelTraceParameters RadiosityTexelTraceParameters;
{
RadiosityTexelTraceParameters.CardTileAllocator = GraphBuilder.CreateSRV(CardTileAllocator);
RadiosityTexelTraceParameters.CardTileData = GraphBuilder.CreateSRV(CardTiles);
RadiosityTexelTraceParameters.TraceRadianceAtlas = RadiosityFrameTemporaries.TraceRadianceAtlas;
RadiosityTexelTraceParameters.TraceHitDistanceAtlas = RadiosityFrameTemporaries.TraceHitDistanceAtlas;
RadiosityTexelTraceParameters.RadiosityAtlasSize = LumenSceneData.GetRadiosityAtlasSize();
RadiosityTexelTraceParameters.ProbeSpacingInRadiosityTexels = RadiosityFrameTemporaries.ProbeSpacing;
RadiosityTexelTraceParameters.ProbeSpacingInRadiosityTexelsDivideShift = FMath::FloorLog2(RadiosityFrameTemporaries.ProbeSpacing);
RadiosityTexelTraceParameters.RadiosityTileSize = RadiosityTileSize;
RadiosityTexelTraceParameters.HemisphereProbeResolution = RadiosityFrameTemporaries.HemisphereProbeResolution;
RadiosityTexelTraceParameters.NumTracesPerProbe = RadiosityFrameTemporaries.HemisphereProbeResolution * RadiosityFrameTemporaries.HemisphereProbeResolution;
RadiosityTexelTraceParameters.ProbeOcclusionStrength = RadiosityFrameTemporaries.bUseProbeOcclusion ? FMath::Clamp<float>(GRadiosityFilteringProbeOcclusionStrength, 0.0f, 1.0f) : 0;
RadiosityTexelTraceParameters.FixedJitterIndex = GLumenRadiosityFixedJitterIndex;
RadiosityTexelTraceParameters.MaxFramesAccumulated = LumenRadiosity::UseTemporalAccumulation() ? GLumenRadiosityTemporalMaxFramesAccumulated : 1;
RadiosityTexelTraceParameters.NumViews = NumViewOrigins;
// Needs to be set to valid value inside view loop
RadiosityTexelTraceParameters.ViewIndex = RadiosityTexelTraceParameters.NumViews;
RadiosityTexelTraceParameters.MaxCardTiles = MaxCardTiles;
RadiosityTexelTraceParameters.TargetFormatQuantizationError = Lumen::GetLightingQuantizationError();
FBlueNoise BlueNoise = GetBlueNoiseGlobalParameters();
RadiosityTexelTraceParameters.BlueNoise = CreateUniformBufferImmediate(BlueNoise, EUniformBufferUsage::UniformBuffer_SingleDraw);
}
const FViewInfo& FirstView = Views[0];
const FGlobalShaderMap* GlobalShaderMap = FirstView.ShaderMap;
// Build a list of radiosity tiles for future processing
{
FBuildRadiosityTilesCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FBuildRadiosityTilesCS::FParameters>();
PassParameters->IndirectArgBuffer = CardUpdateContext.DispatchCardPageIndicesIndirectArgs;
PassParameters->LumenCardScene = FrameTemporaries.LumenCardSceneUniformBuffer;
PassParameters->RWCardTileAllocator = GraphBuilder.CreateUAV(CardTileAllocator);
PassParameters->RWCardTileData = GraphBuilder.CreateUAV(CardTiles);
PassParameters->CardPageIndexAllocator = GraphBuilder.CreateSRV(CardUpdateContext.CardPageIndexAllocator);
PassParameters->CardPageIndexData = GraphBuilder.CreateSRV(CardUpdateContext.CardPageIndexData);
PassParameters->NumViews = NumViewOrigins;
PassParameters->MaxCardTiles = MaxCardTiles;
check(NumViewOrigins <= PassParameters->FrustumTranslatedWorldToClip.Num());
for (int32 OriginIndex = 0; OriginIndex < NumViewOrigins; OriginIndex++)
{
const FLumenViewOrigin& ViewOrigin = FrameTemporaries.ViewOrigins[OriginIndex];
PassParameters->FrustumTranslatedWorldToClip[OriginIndex] = ViewOrigin.FrustumTranslatedWorldToClip;
PassParameters->PreViewTranslationHigh[OriginIndex] = ViewOrigin.PreViewTranslationDF.High;
PassParameters->PreViewTranslationLow[OriginIndex] = ViewOrigin.PreViewTranslationDF.Low;
}
auto ComputeShader = GlobalShaderMap->GetShader<FBuildRadiosityTilesCS>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("BuildRadiosityTiles"),
ComputePassFlags,
ComputeShader,
PassParameters,
CardUpdateContext.DispatchCardPageIndicesIndirectArgs,
FLumenCardUpdateContext::EIndirectArgOffset::ThreadPerTile);
}
FRDGBufferRef RadiosityIndirectArgs = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc<FRHIDispatchIndirectParameters>((uint32)ERadiosityIndirectArgs::MAX * NumViewOrigins), TEXT("Lumen.RadiosityIndirectArgs"));
// Setup indirect args for future passes
{
FLumenRadiosityIndirectArgsCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FLumenRadiosityIndirectArgsCS::FParameters>();
PassParameters->RadiosityCommonParameters = RadiosityCommonParameters;
PassParameters->RWIndirectArgs = GraphBuilder.CreateUAV(RadiosityIndirectArgs);
PassParameters->RadiosityTexelTraceParameters = RadiosityTexelTraceParameters;
#if RHI_RAYTRACING
PassParameters->HardwareRayTracingThreadGroupSize = Lumen::UseHardwareInlineRayTracing(*FirstView.Family) ?
FLumenRadiosityHardwareRayTracingCS::GetThreadGroupSize(FirstView.GetShaderPlatform()).X :
FLumenRadiosityHardwareRayTracingRGS::GetThreadGroupSize().X;
#else
PassParameters->HardwareRayTracingThreadGroupSize = 1;
#endif
auto ComputeShader = GlobalShaderMap->GetShader<FLumenRadiosityIndirectArgsCS>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("IndirectArgs"),
ComputePassFlags,
ComputeShader,
PassParameters,
FIntVector(1, 1, 1));
}
FLumenCardTracingParameters TracingParameters;
GetLumenCardTracingParameters(GraphBuilder, FirstView, *Scene->GetLumenSceneData(FirstView), FrameTemporaries, /*bSurfaceCacheFeedback*/ false, TracingParameters);
// Trace rays from surface cache texels
if (Lumen::UseHardwareRayTracedRadiosity(*FirstView.Family))
{
#if RHI_RAYTRACING
const bool bUseMinimalPayload = true;
const bool bInlineRayTracing = Lumen::UseHardwareInlineRayTracing(*FirstView.Family);
checkf((Views.Num() == 1 || IStereoRendering::IsStereoEyeView(FirstView)), TEXT("Radiosity HW tracing needs to be updated for splitscreen support"));
uint32 ViewIndex = 0;
const FViewInfo& View = Views[ViewIndex];
FLumenRadiosityHardwareRayTracingRGS::FParameters* PassParameters = GraphBuilder.AllocParameters<FLumenRadiosityHardwareRayTracingRGS::FParameters>();
SetLumenHardwareRayTracingSharedParameters(
GraphBuilder,
GetSceneTextureParameters(GraphBuilder, View),
View,
TracingParameters,
&PassParameters->SharedParameters
);
PassParameters->HardwareRayTracingIndirectArgs = RadiosityIndirectArgs;
PassParameters->RadiosityTexelTraceParameters = RadiosityTexelTraceParameters;
PassParameters->RadiosityTexelTraceParameters.ViewIndex = 0;
PassParameters->RWTraceRadianceAtlas = GraphBuilder.CreateUAV(RadiosityFrameTemporaries.TraceRadianceAtlas);
PassParameters->RWTraceHitDistanceAtlas = RadiosityFrameTemporaries.TraceHitDistanceAtlas ? GraphBuilder.CreateUAV(RadiosityFrameTemporaries.TraceHitDistanceAtlas) : nullptr;
const uint32 NumThreadsToDispatch = GRHIPersistentThreadGroupCount * FLumenRadiosityHardwareRayTracingRGS::GetGroupSize();
PassParameters->NumThreadsToDispatch = NumThreadsToDispatch;
PassParameters->SurfaceBias = FMath::Clamp(GLumenRadiosityHardwareRayTracingSurfaceSlopeBias, 0.0f, 1000.0f);
PassParameters->HeightfieldSurfaceBias = Lumen::GetHeightfieldReceiverBias();
PassParameters->MaxRayIntensity = FMath::Clamp(GLumenRadiosityMaxRayIntensity, 0.0f, 1000000.0f);
PassParameters->MinTraceDistance = FMath::Clamp(GLumenRadiosityHardwareRayTracingSurfaceBias, 0.0f, 1000.0f);
PassParameters->MaxTraceDistance = Lumen::GetMaxTraceDistance(View);
FLumenRadiosityHardwareRayTracing::FPermutationDomain PermutationVector;
PermutationVector.Set<FLumenRadiosityHardwareRayTracing::FAvoidSelfIntersectionsMode>(LumenHardwareRayTracing::GetAvoidSelfIntersectionsMode());
PermutationVector.Set<FLumenRadiosityHardwareRayTracing::FSurfaceCacheAlphaMasking>(LumenHardwareRayTracing::UseSurfaceCacheAlphaMasking());
PermutationVector.Set<FLumenRadiosityHardwareRayTracing::FProbeOcclusion>(RadiosityFrameTemporaries.bUseProbeOcclusion);
const FIntPoint DispatchResolution = FIntPoint(NumThreadsToDispatch, 1);
FString Resolution = FString::Printf(TEXT("%ux%u"), DispatchResolution.X, DispatchResolution.Y);
if (bInlineRayTracing)
{
FLumenRadiosityHardwareRayTracingCS::AddLumenRayTracingDispatchIndirect(
GraphBuilder,
RDG_EVENT_NAME("HardwareRayTracingCS <indirect> %ux%u probes at %u spacing",
RadiosityFrameTemporaries.HemisphereProbeResolution,
RadiosityFrameTemporaries.HemisphereProbeResolution,
RadiosityFrameTemporaries.ProbeSpacing),
View,
PermutationVector,
PassParameters,
PassParameters->HardwareRayTracingIndirectArgs,
(uint32)ERadiosityIndirectArgs::HardwareRayTracingThreadPerTrace + ViewIndex * (uint32)ERadiosityIndirectArgs::MAX * sizeof(FRHIDispatchIndirectParameters),
ComputePassFlags);
}
else
{
FLumenRadiosityHardwareRayTracingRGS::AddLumenRayTracingDispatchIndirect(
GraphBuilder,
RDG_EVENT_NAME("HardwareRayTracingRGS %s %ux%u probes at %u spacing",
*Resolution,
RadiosityFrameTemporaries.HemisphereProbeResolution,
RadiosityFrameTemporaries.HemisphereProbeResolution,
RadiosityFrameTemporaries.ProbeSpacing),
View,
PermutationVector,
PassParameters,
PassParameters->HardwareRayTracingIndirectArgs,
(uint32)ERadiosityIndirectArgs::HardwareRayTracingThreadPerTrace + ViewIndex * (uint32)ERadiosityIndirectArgs::MAX * sizeof(FRHIDispatchIndirectParameters),
bUseMinimalPayload,
ComputePassFlags);
}
#endif
}
else
{
FRDGTextureUAVRef TraceRadianceAtlasUAV = GraphBuilder.CreateUAV(RadiosityFrameTemporaries.TraceRadianceAtlas, ERDGUnorderedAccessViewFlags::SkipBarrier);
FRDGTextureUAVRef TraceHitDistanceAtlasUAV = RadiosityFrameTemporaries.TraceHitDistanceAtlas ? GraphBuilder.CreateUAV(RadiosityFrameTemporaries.TraceHitDistanceAtlas, ERDGUnorderedAccessViewFlags::SkipBarrier) : nullptr;
for (int32 OriginIndex = 0; OriginIndex < NumViewOrigins; ++OriginIndex)
{
const FLumenViewOrigin& ViewOrigin = FrameTemporaries.ViewOrigins[OriginIndex];
FLumenRadiosityDistanceFieldTracingCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FLumenRadiosityDistanceFieldTracingCS::FParameters>();
PassParameters->IndirectArgs = RadiosityIndirectArgs;
PassParameters->RadiosityTexelTraceParameters = RadiosityTexelTraceParameters;
PassParameters->RadiosityTexelTraceParameters.ViewIndex = OriginIndex;
PassParameters->RWTraceRadianceAtlas = TraceRadianceAtlasUAV;
PassParameters->RWTraceHitDistanceAtlas = TraceHitDistanceAtlasUAV;
PassParameters->TracingParameters = TracingParameters;
SetupLumenDiffuseTracingParametersForProbe(ViewOrigin.MaxTraceDistance, ViewOrigin.OrthoMaxDimension, PassParameters->IndirectTracingParameters, 0.0f);
PassParameters->IndirectTracingParameters.SurfaceBias = FMath::Clamp(GLumenRadiosityDistanceFieldSurfaceSlopeBias, 0.0f, 1000.0f);
PassParameters->IndirectTracingParameters.MinTraceDistance = FMath::Clamp(GLumenRadiosityDistanceFieldSurfaceBias, 0.0f, 1000.0f);
PassParameters->IndirectTracingParameters.MaxTraceDistance = ViewOrigin.MaxTraceDistance;
PassParameters->MaxRayIntensity = FMath::Clamp(GLumenRadiosityMaxRayIntensity, 0.0f, 1000000.0f);
FLumenRadiosityDistanceFieldTracingCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FLumenRadiosityDistanceFieldTracingCS::FThreadGroupSize32>(Lumen::UseThreadGroupSize32());
PermutationVector.Set<FLumenRadiosityDistanceFieldTracingCS::FTraceGlobalSDF>(Lumen::UseGlobalSDFTracing(ViewOrigin.Family->EngineShowFlags));
PermutationVector.Set<FLumenRadiosityDistanceFieldTracingCS::FSimpleCoverageBasedExpand>(Lumen::UseGlobalSDFTracing(ViewOrigin.Family->EngineShowFlags) && Lumen::UseGlobalSDFSimpleCoverageBasedExpand());
PermutationVector.Set<FLumenRadiosityDistanceFieldTracingCS::FProbeOcclusion>(RadiosityFrameTemporaries.bUseProbeOcclusion);
auto ComputeShader = GlobalShaderMap->GetShader<FLumenRadiosityDistanceFieldTracingCS>(PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("DistanceFieldTracing %ux%u probes at %u spacing",
RadiosityFrameTemporaries.HemisphereProbeResolution,
RadiosityFrameTemporaries.HemisphereProbeResolution,
RadiosityFrameTemporaries.ProbeSpacing),
ComputePassFlags,
ComputeShader,
PassParameters,
RadiosityIndirectArgs,
(uint32)(Lumen::UseThreadGroupSize32() ? ERadiosityIndirectArgs::NumTracesDiv32 : ERadiosityIndirectArgs::NumTracesDiv64) + OriginIndex * (uint32)ERadiosityIndirectArgs::MAX * sizeof(FRHIDispatchIndirectParameters));
}
}
if (GLumenRadiositySpatialFilterProbes && GLumenRadiositySpatialFilterProbesKernelSize > 0)
{
//@todo - use temporary buffer based off of CardUpdateContext.UpdateAtlasSize which is smaller
FRDGTextureRef FilteredTraceRadianceAtlas = GraphBuilder.CreateTexture(
FRDGTextureDesc::Create2D(
RadiosityFrameTemporaries.ProbeTracingAtlasSize,
Lumen::GetLightingDataFormat(),
FClearValueBinding::Black,
TexCreate_ShaderResource | TexCreate_UAV),
TEXT("Lumen.Radiosity.FilteredTraceRadianceAtlas"));
FRDGTextureUAVRef FilteredTraceRadianceAtlasUAV = GraphBuilder.CreateUAV(FilteredTraceRadianceAtlas, ERDGUnorderedAccessViewFlags::SkipBarrier);
for (int32 OriginIndex = 0; OriginIndex < NumViewOrigins; ++OriginIndex)
{
FLumenRadiositySpatialFilterProbeRadiance::FParameters* PassParameters = GraphBuilder.AllocParameters<FLumenRadiositySpatialFilterProbeRadiance::FParameters>();
PassParameters->RWFilteredTraceRadianceAtlas = FilteredTraceRadianceAtlasUAV;
PassParameters->IndirectArgs = RadiosityIndirectArgs;
PassParameters->View = FrameTemporaries.ViewOrigins[OriginIndex].ReferenceView->ViewUniformBuffer;
PassParameters->LumenCardScene = FrameTemporaries.LumenCardSceneUniformBuffer;
PassParameters->RadiosityTexelTraceParameters = RadiosityTexelTraceParameters;
PassParameters->RadiosityTexelTraceParameters.ViewIndex = OriginIndex;
PassParameters->ProbePlaneWeightingDepthScale = GRadiosityProbePlaneWeightingDepthScale;
FLumenRadiositySpatialFilterProbeRadiance::FPermutationDomain PermutationVector;
PermutationVector.Set<FLumenRadiositySpatialFilterProbeRadiance::FPlaneWeighting>(GRadiosityFilteringProbePlaneWeighting != 0);
PermutationVector.Set<FLumenRadiositySpatialFilterProbeRadiance::FProbeOcclusion>(RadiosityFrameTemporaries.bUseProbeOcclusion);
PermutationVector.Set<FLumenRadiositySpatialFilterProbeRadiance::FKernelSize>(FMath::Clamp<int32>(GLumenRadiositySpatialFilterProbesKernelSize, 0, 2));
auto ComputeShader = GlobalShaderMap->GetShader<FLumenRadiositySpatialFilterProbeRadiance>(PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("SpatialFilterProbes"),
ComputePassFlags,
ComputeShader,
PassParameters,
RadiosityIndirectArgs,
(uint32)ERadiosityIndirectArgs::NumTracesDiv64 + OriginIndex * (uint32)ERadiosityIndirectArgs::MAX * sizeof(FRHIDispatchIndirectParameters));
}
RadiosityTexelTraceParameters.TraceRadianceAtlas = FilteredTraceRadianceAtlas;
}
FRDGTextureUAVRef RadiosityProbeSHRedAtlasUAV = GraphBuilder.CreateUAV(RadiosityFrameTemporaries.ProbeSHRedAtlas, ERDGUnorderedAccessViewFlags::SkipBarrier);
FRDGTextureUAVRef RadiosityProbeSHGreenAtlasUAV = GraphBuilder.CreateUAV(RadiosityFrameTemporaries.ProbeSHGreenAtlas, ERDGUnorderedAccessViewFlags::SkipBarrier);
FRDGTextureUAVRef RadiosityProbeSHBlueAtlasUAV = GraphBuilder.CreateUAV(RadiosityFrameTemporaries.ProbeSHBlueAtlas, ERDGUnorderedAccessViewFlags::SkipBarrier);
// Convert traces to SH and store in persistent SH atlas
for (int32 OriginIndex = 0; OriginIndex < NumViewOrigins; ++OriginIndex)
{
FLumenRadiosityConvertToSH::FParameters* PassParameters = GraphBuilder.AllocParameters<FLumenRadiosityConvertToSH::FParameters>();
PassParameters->RWRadiosityProbeSHRedAtlas = RadiosityProbeSHRedAtlasUAV;
PassParameters->RWRadiosityProbeSHGreenAtlas = RadiosityProbeSHGreenAtlasUAV;
PassParameters->RWRadiosityProbeSHBlueAtlas = RadiosityProbeSHBlueAtlasUAV;
PassParameters->IndirectArgs = RadiosityIndirectArgs;
PassParameters->View = FrameTemporaries.ViewOrigins[OriginIndex].ReferenceView->ViewUniformBuffer;
PassParameters->LumenCardScene = FrameTemporaries.LumenCardSceneUniformBuffer;
PassParameters->RadiosityTexelTraceParameters = RadiosityTexelTraceParameters;
PassParameters->RadiosityTexelTraceParameters.ViewIndex = OriginIndex;
auto ComputeShader = GlobalShaderMap->GetShader<FLumenRadiosityConvertToSH>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("ConvertToSH"),
ComputePassFlags,
ComputeShader,
PassParameters,
RadiosityIndirectArgs,
(uint32)ERadiosityIndirectArgs::ThreadPerProbe + OriginIndex * (uint32)ERadiosityIndirectArgs::MAX * sizeof(FRHIDispatchIndirectParameters));
}
FRDGTextureUAVRef RadiosityAtlasUAV = GraphBuilder.CreateUAV(FrameTemporaries.IndirectLightingAtlas, ERDGUnorderedAccessViewFlags::SkipBarrier);
FRDGTextureUAVRef RadiosityNumFramesAccumulatedAtlasUAV = GraphBuilder.CreateUAV(FrameTemporaries.RadiosityNumFramesAccumulatedAtlas, ERDGUnorderedAccessViewFlags::SkipBarrier);
for (int32 OriginIndex = 0; OriginIndex < NumViewOrigins; ++OriginIndex)
{
const FLumenViewOrigin& ViewOrigin = FrameTemporaries.ViewOrigins[OriginIndex];
FLumenRadiosityIntegrateCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FLumenRadiosityIntegrateCS::FParameters>();
PassParameters->IndirectArgs = RadiosityIndirectArgs;
PassParameters->RadiosityCommonParameters = RadiosityCommonParameters;
PassParameters->LumenCardScene = FrameTemporaries.LumenCardSceneUniformBuffer;
PassParameters->RadiosityTexelTraceParameters = RadiosityTexelTraceParameters;
PassParameters->RadiosityTexelTraceParameters.ViewIndex = OriginIndex;
PassParameters->RWRadiosityAtlas = RadiosityAtlasUAV;
PassParameters->RWRadiosityNumFramesAccumulatedAtlas = RadiosityNumFramesAccumulatedAtlasUAV;
PassParameters->RadiosityProbeSHRedAtlas = RadiosityFrameTemporaries.ProbeSHRedAtlas;
PassParameters->RadiosityProbeSHGreenAtlas = RadiosityFrameTemporaries.ProbeSHGreenAtlas;
PassParameters->RadiosityProbeSHBlueAtlas = RadiosityFrameTemporaries.ProbeSHBlueAtlas;
PassParameters->ProbePlaneWeightingDepthScale = GRadiosityProbePlaneWeightingDepthScale;
PassParameters->Substrate = Substrate::BindSubstrateGlobalUniformParameters(*ViewOrigin.ReferenceView);
FLumenRadiosityIntegrateCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FLumenRadiosityIntegrateCS::FPlaneWeighting>(GRadiosityFilteringProbePlaneWeighting != 0);
PermutationVector.Set<FLumenRadiosityIntegrateCS::FProbeOcclusion>(RadiosityFrameTemporaries.bUseProbeOcclusion);
PermutationVector.Set<FLumenRadiosityIntegrateCS::FTemporalAccumulation>(LumenRadiosity::UseTemporalAccumulation());
auto ComputeShader = GlobalShaderMap->GetShader<FLumenRadiosityIntegrateCS>(PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("Integrate"),
ComputePassFlags,
ComputeShader,
PassParameters,
RadiosityIndirectArgs,
(uint32)ERadiosityIndirectArgs::ThreadPerRadiosityTexel + OriginIndex * (uint32)ERadiosityIndirectArgs::MAX * sizeof(FRHIDispatchIndirectParameters));
}
// Note: extracting source TraceRadianceAtlas and not the filtered one
LumenSceneData.RadiosityTraceRadianceAtlas = GraphBuilder.ConvertToExternalTexture(RadiosityFrameTemporaries.TraceRadianceAtlas);
LumenSceneData.RadiosityTraceHitDistanceAtlas = RadiosityFrameTemporaries.bUseProbeOcclusion ? GraphBuilder.ConvertToExternalTexture(RadiosityFrameTemporaries.TraceHitDistanceAtlas) : nullptr;
LumenSceneData.RadiosityProbeSHRedAtlas = GraphBuilder.ConvertToExternalTexture(RadiosityFrameTemporaries.ProbeSHRedAtlas);
LumenSceneData.RadiosityProbeSHGreenAtlas = GraphBuilder.ConvertToExternalTexture(RadiosityFrameTemporaries.ProbeSHGreenAtlas);
LumenSceneData.RadiosityProbeSHBlueAtlas = GraphBuilder.ConvertToExternalTexture(RadiosityFrameTemporaries.ProbeSHBlueAtlas);
}
void FDeferredShadingSceneRenderer::RenderRadiosityForLumenScene(
FRDGBuilder& GraphBuilder,
const FLumenSceneFrameTemporaries& FrameTemporaries,
const LumenRadiosity::FFrameTemporaries& RadiosityFrameTemporaries,
const FLumenCardUpdateContext& CardUpdateContext,
ERDGPassFlags ComputePassFlags)
{
LLM_SCOPE_BYTAG(Lumen);
FLumenSceneData& LumenSceneData = *Scene->GetLumenSceneData(Views[0]);
if (LumenRadiosity::IsEnabled(ViewFamily)
&& LumenSceneData.bFinalLightingAtlasContentsValid
&& CardUpdateContext.MaxUpdateTiles > 0)
{
RDG_EVENT_SCOPE(GraphBuilder, "Radiosity");
FLumenCardTileUpdateContext CardTileUpdateContext;
Lumen::SpliceCardPagesIntoTiles(GraphBuilder, Views[0].ShaderMap, CardUpdateContext, FrameTemporaries.LumenCardSceneUniformBuffer, CardTileUpdateContext, ComputePassFlags);
const bool bRenderSkylight = Lumen::ShouldHandleSkyLight(Scene, ViewFamily);
LumenRadiosity::AddRadiosityPass(
GraphBuilder,
Scene,
Views,
bRenderSkylight,
LumenSceneData,
FrameTemporaries,
RadiosityFrameTemporaries,
CardUpdateContext,
ComputePassFlags);
// Update Final Lighting
Lumen::CombineLumenSceneLighting(
Scene,
Views[0],
GraphBuilder,
FrameTemporaries,
CardUpdateContext,
CardTileUpdateContext,
ComputePassFlags);
}
else
{
AddClearRenderTargetPass(GraphBuilder, FrameTemporaries.IndirectLightingAtlas);
}
}
class FBuildVisualizeProbesCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FBuildVisualizeProbesCS);
SHADER_USE_PARAMETER_STRUCT(FBuildVisualizeProbesCS, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenCardScene, LumenCardScene)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<uint>, RWVisualizeProbeAllocator)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<uint>, RWVisualizeProbeData)
SHADER_PARAMETER(float, VisualizeProbeRadius)
SHADER_PARAMETER(uint32, ProbesPerPhysicalPage)
SHADER_PARAMETER(uint32, ProbeTileSize)
SHADER_PARAMETER(uint32, MaxVisualizeProbes)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return DoesPlatformSupportLumenGI(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize());
}
static int32 GetGroupSize()
{
return 64;
}
};
IMPLEMENT_GLOBAL_SHADER(FBuildVisualizeProbesCS, "/Engine/Private/Lumen/Radiosity/LumenVisualizeRadiosityProbes.usf", "BuildVisualizeProbesCS", SF_Compute);
class FVisualizeRadiosityProbesVS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FVisualizeRadiosityProbesVS);
SHADER_USE_PARAMETER_STRUCT(FVisualizeRadiosityProbesVS, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenCardScene, LumenCardScene)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<uint>, VisualizeProbeAllocator)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<uint>, VisualizeProbeData)
SHADER_PARAMETER(float, VisualizeProbeRadius)
SHADER_PARAMETER(uint32, ProbesPerPhysicalPage)
SHADER_PARAMETER(uint32, ProbeTileSize)
SHADER_PARAMETER(uint32, MaxVisualizeProbes)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return DoesPlatformSupportLumenGI(Parameters.Platform);
}
};
IMPLEMENT_GLOBAL_SHADER(FVisualizeRadiosityProbesVS, "/Engine/Private/Lumen/Radiosity/LumenVisualizeRadiosityProbes.usf", "VisualizeRadiosityProbesVS", SF_Vertex);
class FVisualizeRadiosityProbesPS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FVisualizeRadiosityProbesPS);
SHADER_USE_PARAMETER_STRUCT(FVisualizeRadiosityProbesPS, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenCardScene, LumenCardScene)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, RadiosityProbeSHRedAtlas)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, RadiosityProbeSHGreenAtlas)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, RadiosityProbeSHBlueAtlas)
SHADER_PARAMETER(float, OneOverCachedLightingPreExposure)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return DoesPlatformSupportLumenGI(Parameters.Platform);
}
};
IMPLEMENT_GLOBAL_SHADER(FVisualizeRadiosityProbesPS, "/Engine/Private/Lumen/Radiosity/LumenVisualizeRadiosityProbes.usf", "VisualizeRadiosityProbesPS", SF_Pixel);
BEGIN_SHADER_PARAMETER_STRUCT(FVisualizeRadiosityProbeParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FVisualizeRadiosityProbesVS::FParameters, VS)
SHADER_PARAMETER_STRUCT_INCLUDE(FVisualizeRadiosityProbesPS::FParameters, PS)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
void FDeferredShadingSceneRenderer::RenderLumenRadiosityProbeVisualization(FRDGBuilder& GraphBuilder, const FMinimalSceneTextures& SceneTextures, const FLumenSceneFrameTemporaries& FrameTemporaries)
{
const FViewInfo& View = Views[0];
const FPerViewPipelineState& ViewPipelineState = GetViewPipelineState(View);
const bool bAnyLumenActive = ViewPipelineState.DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen;
FLumenSceneData& LumenSceneData = *Scene->GetLumenSceneData(View);
if (Views.Num() == 1
&& View.ViewState
&& bAnyLumenActive
&& LumenRadiosity::IsEnabled(ViewFamily)
&& LumenSceneData.bFinalLightingAtlasContentsValid
&& LumenSceneData.RadiosityProbeSHRedAtlas
&& LumenSceneData.RadiosityProbeSHGreenAtlas
&& LumenSceneData.RadiosityProbeSHBlueAtlas
&& CVarLumenSceneRadiosityVisualizeProbes.GetValueOnRenderThread() != 0)
{
RDG_EVENT_SCOPE(GraphBuilder, "VisualizeLumenRadiosityProbes");
FRDGTextureRef SceneColor = SceneTextures.Color.Resolve;
FRDGTextureRef SceneDepth = SceneTextures.Depth.Resolve;
const int32 ProbeSpacing = LumenRadiosity::GetRadiosityProbeSpacing(View);
const uint32 ProbesPerPhysicalPage = Lumen::PhysicalPageSize / ProbeSpacing;
FRDGTextureRef RadiosityProbeSHRedAtlas = GraphBuilder.RegisterExternalTexture(LumenSceneData.RadiosityProbeSHRedAtlas);
FRDGTextureRef RadiosityProbeSHGreenAtlas = GraphBuilder.RegisterExternalTexture(LumenSceneData.RadiosityProbeSHGreenAtlas);
FRDGTextureRef RadiosityProbeSHBlueAtlas = GraphBuilder.RegisterExternalTexture(LumenSceneData.RadiosityProbeSHBlueAtlas);
const uint32 MaxVisualizeProbes = (LumenSceneData.GetPhysicalAtlasSize().X / ProbeSpacing) * (LumenSceneData.GetPhysicalAtlasSize().Y / ProbeSpacing);
FRDGBufferRef VisualizeProbeAllocator = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(uint32), 1), TEXT("Lumen.RadiosityVisualizeProbeAllocator"));
FRDGBufferRef VisualizeProbeData = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(4 * sizeof(float), MaxVisualizeProbes), TEXT("Lumen.RadiosityVisualizeProbeData"));
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(VisualizeProbeAllocator), 0);
// Gather and prepare probes for visualization
{
FBuildVisualizeProbesCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FBuildVisualizeProbesCS::FParameters>();
PassParameters->View = GetShaderBinding(View.ViewUniformBuffer);
PassParameters->LumenCardScene = FrameTemporaries.LumenCardSceneUniformBuffer;
PassParameters->VisualizeProbeRadius = CVarLumenSceneRadiosityVisualizeProbeRadius.GetValueOnRenderThread();
PassParameters->ProbesPerPhysicalPage = ProbesPerPhysicalPage;
PassParameters->ProbeTileSize = ProbeSpacing;
PassParameters->MaxVisualizeProbes = MaxVisualizeProbes;
PassParameters->MaxVisualizeProbes = MaxVisualizeProbes;
PassParameters->RWVisualizeProbeAllocator = GraphBuilder.CreateUAV(VisualizeProbeAllocator);
PassParameters->RWVisualizeProbeData = GraphBuilder.CreateUAV(VisualizeProbeData);
auto ComputeShader = View.ShaderMap->GetShader<FBuildVisualizeProbesCS>();
const FIntVector GroupSize = FComputeShaderUtils::GetGroupCountWrapped(LumenSceneData.GetNumCardPages() * ProbesPerPhysicalPage * ProbesPerPhysicalPage, FBuildVisualizeProbesCS::GetGroupSize());
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("BuildVisualizeProbes"),
ComputeShader,
PassParameters,
GroupSize);
}
FVisualizeRadiosityProbeParameters* PassParameters = GraphBuilder.AllocParameters<FVisualizeRadiosityProbeParameters>();
PassParameters->VS.View = GetShaderBinding(View.ViewUniformBuffer);
PassParameters->VS.LumenCardScene = FrameTemporaries.LumenCardSceneUniformBuffer;
PassParameters->VS.VisualizeProbeRadius = CVarLumenSceneRadiosityVisualizeProbeRadius.GetValueOnRenderThread();
PassParameters->VS.ProbesPerPhysicalPage = ProbesPerPhysicalPage;
PassParameters->VS.ProbeTileSize = ProbeSpacing;
PassParameters->VS.MaxVisualizeProbes = MaxVisualizeProbes;
PassParameters->VS.VisualizeProbeAllocator = GraphBuilder.CreateSRV(VisualizeProbeAllocator);
PassParameters->VS.VisualizeProbeData = GraphBuilder.CreateSRV(VisualizeProbeData);
PassParameters->PS.View = GetShaderBinding(View.ViewUniformBuffer);
PassParameters->PS.LumenCardScene = FrameTemporaries.LumenCardSceneUniformBuffer;
PassParameters->PS.RadiosityProbeSHRedAtlas = RadiosityProbeSHRedAtlas;
PassParameters->PS.RadiosityProbeSHGreenAtlas = RadiosityProbeSHGreenAtlas;
PassParameters->PS.RadiosityProbeSHBlueAtlas = RadiosityProbeSHBlueAtlas;
PassParameters->PS.OneOverCachedLightingPreExposure = 1.0f / Lumen::GetCachedLightingPreExposure();
PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(
SceneDepth,
ERenderTargetLoadAction::ENoAction,
ERenderTargetLoadAction::ELoad,
FExclusiveDepthStencil::DepthWrite_StencilWrite);
PassParameters->RenderTargets[0] = FRenderTargetBinding(SceneColor, ERenderTargetLoadAction::ELoad);
GraphBuilder.AddPass(
RDG_EVENT_NAME("Visualize Radiosity Probes"),
PassParameters,
ERDGPassFlags::Raster,
[PassParameters, &View, MaxVisualizeProbes](FRDGAsyncTask, FRHICommandList& RHICmdList)
{
TShaderMapRef<FVisualizeRadiosityProbesVS> VertexShader(View.ShaderMap);
TShaderMapRef<FVisualizeRadiosityProbesPS> PixelShader(View.ShaderMap);
RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f);
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGB>::GetRHI();
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<true, CF_DepthNear>::GetRHI();
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GEmptyVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
SetShaderParameters(RHICmdList, VertexShader, VertexShader.GetVertexShader(), PassParameters->VS);
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), PassParameters->PS);
RHICmdList.SetStreamSource(0, NULL, 0);
RHICmdList.DrawIndexedPrimitive(GCubeIndexBuffer.IndexBufferRHI, 0, 0, 8, 0, 2 * 6, MaxVisualizeProbes);
});
}
}