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

621 lines
25 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "RayTracingDecals.h"
#include "RHIDefinitions.h"
#if RHI_RAYTRACING
#include "RendererPrivate.h"
#include "RayTracingMaterialHitShaders.h"
#include "DecalRenderingShared.h"
#include "RayTracingTypes.h"
#include "RayTracingDefinitions.h"
#include "SceneProxies/DeferredDecalProxy.h"
#include <limits>
TAutoConsoleVariable<int32> CVarDecalGridResolution(
TEXT("r.RayTracing.DecalGrid.Resolution"),
256,
TEXT("Controls the resolution of the 2D decal grid used to cull irrelevant decal from calculations (default = 256)\n"),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarDecalGridMaxCount(
TEXT("r.RayTracing.DecalGrid.MaxCount"),
128,
TEXT("Controls the maximum number of decals per cell in the 2D decal grid. The minimum of this value and the number of decal in the scene is used. (default = 128)\n"),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarDecalGridAxis(
TEXT("r.RayTracing.DecalGrid.Axis"),
-1,
TEXT("Choose the coordinate axis along which to project the decal grid (default = -1, automatic)\n"),
ECVF_RenderThreadSafe
);
enum class EDecalsWriteFlags : uint32
{
None = 0,
BaseColor = (1 << 0),
Normal = (1 << 1),
RoughnessSpecularMetallic = (1 << 2),
Emissive = (1 << 3),
AmbientOcclusion = (1 << 4),
};
static uint32 CalculateDecalWriteFlags(FDecalBlendDesc DecalBlendDesc)
{
uint32 Flags = (uint32)EDecalsWriteFlags::None;
#define SET_FLAG(BOOL_VAL, WRITE_FLAG) Flags |= uint32(DecalBlendDesc.BOOL_VAL ? EDecalsWriteFlags::WRITE_FLAG : EDecalsWriteFlags::None)
SET_FLAG(bWriteBaseColor, BaseColor);
SET_FLAG(bWriteNormal, Normal);
SET_FLAG(bWriteRoughnessSpecularMetallic, RoughnessSpecularMetallic);
SET_FLAG(bWriteEmissive, Emissive);
SET_FLAG(bWriteAmbientOcclusion, AmbientOcclusion);
#undef SET_FLAG
return Flags;
}
IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FRayTracingDecals, "RayTracingDecals");
BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FDecalParametersRayTracing, )
SHADER_PARAMETER(FMatrix44f, WorldToDecal)
SHADER_PARAMETER(FMatrix44f, DecalToWorld)
SHADER_PARAMETER(FVector3f, DecalPositionHigh)
SHADER_PARAMETER(FVector3f, DecalToWorldInvScale)
SHADER_PARAMETER(FVector3f, DecalOrientation)
SHADER_PARAMETER(FVector2f, DecalParams)
SHADER_PARAMETER(FVector4f, DecalColorParam)
SHADER_PARAMETER(uint32, DecalWriteFlags)
END_GLOBAL_SHADER_PARAMETER_STRUCT()
IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FDecalParametersRayTracing, "RayTracingDecalParameters");
static TUniformBufferRef<FDecalParametersRayTracing> CreateDecalParametersBuffer(
const FViewInfo& View,
EShaderPlatform Platform,
const FVisibleDecal& VisibleDecal,
EUniformBufferUsage Usage)
{
FDecalParametersRayTracing Parameters;
const FMatrix DecalToWorldMatrix = VisibleDecal.ComponentTrans.ToMatrixWithScale();
const FMatrix WorldToDecalMatrix = VisibleDecal.ComponentTrans.ToInverseMatrixWithScale();
const FDFVector3 AbsoluteOrigin(DecalToWorldMatrix.GetOrigin());
const FVector3f PositionHigh = AbsoluteOrigin.High;
const FMatrix44f RelativeDecalToWorldMatrix = FDFMatrix::MakeToRelativeWorldMatrix(PositionHigh, DecalToWorldMatrix).M;
const FVector3f OrientationVector = (FVector3f)VisibleDecal.ComponentTrans.GetUnitAxis(EAxis::X);
Parameters.DecalPositionHigh = PositionHigh;
Parameters.WorldToDecal = FMatrix44f(FTranslationMatrix(-View.ViewMatrices.GetPreViewTranslation()) * WorldToDecalMatrix);
Parameters.DecalToWorld = RelativeDecalToWorldMatrix;
Parameters.DecalToWorldInvScale = static_cast<FVector3f>(DecalToWorldMatrix.GetScaleVector().Reciprocal());
Parameters.DecalOrientation = OrientationVector;
Parameters.DecalColorParam = VisibleDecal.DecalColor;
float LifetimeAlpha = 1.0f;
// Certain engine captures (e.g. environment reflection) don't have a tick. Default to fully opaque.
if (View.Family->Time.GetWorldTimeSeconds())
{
LifetimeAlpha = FMath::Clamp(FMath::Min(View.Family->Time.GetWorldTimeSeconds() * -VisibleDecal.InvFadeDuration + VisibleDecal.FadeStartDelayNormalized, View.Family->Time.GetWorldTimeSeconds() * VisibleDecal.InvFadeInDuration + VisibleDecal.FadeInStartDelayNormalized), 0.0f, 1.0f);
}
Parameters.DecalParams = FVector2f(VisibleDecal.FadeAlpha, LifetimeAlpha);
Parameters.DecalColorParam = VisibleDecal.DecalColor;
Parameters.DecalWriteFlags = CalculateDecalWriteFlags(VisibleDecal.BlendDesc);
return CreateUniformBufferImmediate(Parameters, Usage);
}
class FRayTracingBuildDecalGridCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FRayTracingBuildDecalGridCS)
SHADER_USE_PARAMETER_STRUCT(FRayTracingBuildDecalGridCS, 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<FRayTracingDecal>, SceneDecals)
SHADER_PARAMETER(uint32, SceneDecalCount)
SHADER_PARAMETER(FVector3f, SceneDecalsTranslatedBoundMin)
SHADER_PARAMETER(FVector3f, SceneDecalsTranslatedBoundMax)
SHADER_PARAMETER(unsigned, DecalGridResolution)
SHADER_PARAMETER(unsigned, DecalGridMaxCount)
SHADER_PARAMETER(int, DecalGridAxis)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2DArray<uint>, RWDecalGrid)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, RWDecalGridData)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_SHADER_TYPE(, FRayTracingBuildDecalGridCS, TEXT("/Engine/Private/RayTracing/RayTracingBuildDecalGrid.usf"), TEXT("BuildDecalGridCS"), SF_Compute);
class FRayTracingDecalMaterialShader : public FMaterialShader
{
DECLARE_SHADER_TYPE(FRayTracingDecalMaterialShader, Material);
public:
FRayTracingDecalMaterialShader() = default;
FRayTracingDecalMaterialShader(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FMaterialShader(Initializer)
{
DecalParameter.Bind(Initializer.ParameterMap, TEXT("RaytracingDecalParameters"));
}
static bool ShouldCompilePermutation(const FMaterialShaderPermutationParameters& Parameters)
{
if (!ShouldCompileRayTracingCallableShadersForProject(Parameters.Platform))
{
// is raytracing enabled at all?
return false;
}
if (!FDataDrivenShaderPlatformInfo::GetSupportsPathTracing(Parameters.Platform))
{
// this shader is currently only used by the path tracer
return false;
}
if (Parameters.MaterialParameters.MaterialDomain != MD_DeferredDecal)
{
// only compile callable shader permutation of deferred decal material
return false;
}
return true;
}
static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
// DECAL_PRIMITIVE informs material templates which functions to expose when rendering decals.
OutEnvironment.SetDefine(TEXT("DECAL_PRIMITIVE"), 1);
}
static bool ValidateCompiledResult(EShaderPlatform Platform, const FShaderParameterMap& ParameterMap, TArray<FString>& OutError)
{
if (ParameterMap.ContainsParameterAllocation(FSceneTextureUniformParameters::FTypeInfo::GetStructMetadata()->GetShaderVariableName()))
{
OutError.Add(TEXT("Ray tracing callable shaders cannot read from the SceneTexturesStruct."));
return false;
}
for (const auto& It : ParameterMap.GetParameterMap())
{
const FParameterAllocation& ParamAllocation = It.Value;
if (ParamAllocation.Type != EShaderParameterType::UniformBuffer)
{
OutError.Add(FString::Printf(TEXT("Invalid ray tracing callable shader parameter '%s'. Only uniform buffers and loose data parameters are supported."), *(It.Key)));
return false;
}
}
return true;
}
static ERayTracingPayloadType GetRayTracingPayloadType(const int32 PermutationId)
{
return ERayTracingPayloadType::Decals;
}
static const FShaderBindingLayout* GetShaderBindingLayout(const FShaderPermutationParameters& Parameters)
{
return RayTracing::GetShaderBindingLayout(Parameters.Platform);
}
void GetShaderBindings(
const FScene* Scene,
ERHIFeatureLevel::Type FeatureLevel,
const FMaterialRenderProxy& MaterialRenderProxy,
const FMaterial& Material,
const FViewInfo& View,
const TUniformBufferRef<FDecalParametersRayTracing>& DecalParameters,
FMeshDrawSingleShaderBindings& ShaderBindings) const
{
FMaterialShader::GetShaderBindings(Scene, FeatureLevel, MaterialRenderProxy, Material, ShaderBindings);
ShaderBindings.Add(GetUniformBufferParameter<FViewUniformShaderParameters>(), View.ViewUniformBuffer);
// Similar to DecalRendering::SetShader(...) uses GIdentityPrimitiveUniformBuffer
// We could potentially bind the actual primitive uniform buffer
ShaderBindings.Add(GetUniformBufferParameter<FPrimitiveUniformShaderParameters>(), GIdentityPrimitiveUniformBuffer);
ShaderBindings.Add(DecalParameter, DecalParameters);
}
private:
LAYOUT_FIELD(FShaderUniformBufferParameter, DecalParameter);
};
IMPLEMENT_RT_PAYLOAD_TYPE(ERayTracingPayloadType::Decals, 48);
IMPLEMENT_SHADER_TYPE(, FRayTracingDecalMaterialShader, TEXT("/Engine/Private/RayTracing/RayTracingDecalMaterialShader.usf"), TEXT("RayTracingDecalMaterialShader"), SF_RayCallable);
static bool DecalNeedsAnyHitShader(EBlendMode BlendMode)
{
return BlendMode != BLEND_Opaque;
}
template<bool bUseAnyHitShader>
class TRayTracingDecalMaterial : public FMeshMaterialShader
{
DECLARE_SHADER_TYPE(TRayTracingDecalMaterial, MeshMaterial);
public:
TRayTracingDecalMaterial() = default;
TRayTracingDecalMaterial(const FMeshMaterialShaderType::CompiledShaderInitializerType& Initializer)
: FMeshMaterialShader(Initializer)
{}
static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters)
{
if (!ShouldCompileRayTracingShadersForProject(Parameters.Platform))
{
// is raytracing enabled at all?
return false;
}
if (!FDataDrivenShaderPlatformInfo::GetSupportsPathTracing(Parameters.Platform))
{
// this shader is currently only used by the path tracer
return false;
}
if (!Parameters.VertexFactoryType->SupportsRayTracing())
{
// does the VF support ray tracing at all?
return false;
}
if (Parameters.MaterialParameters.MaterialDomain != MD_DeferredDecal)
{
return false;
}
if (DecalNeedsAnyHitShader(Parameters.MaterialParameters.BlendMode) != bUseAnyHitShader)
{
// the anyhit permutation is only required if the material is masked or has a non-opaque blend mode
return false;
}
return true;
}
static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FMeshMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
check(Parameters.MaterialParameters.MaterialDomain == MD_DeferredDecal);
FDecalBlendDesc DecalBlendDesc = DecalRendering::ComputeDecalBlendDesc(Parameters.Platform, Parameters.MaterialParameters);
OutEnvironment.SetDefine(TEXT("DECAL_PAYLOAD_FLAGS"), CalculateDecalWriteFlags(DecalBlendDesc));
OutEnvironment.SetDefine(TEXT("USE_MATERIAL_ANY_HIT_SHADER"), bUseAnyHitShader ? 1 : 0);
}
static bool ValidateCompiledResult(EShaderPlatform Platform, const FShaderParameterMap& ParameterMap, TArray<FString>& OutError)
{
if (ParameterMap.ContainsParameterAllocation(FSceneTextureUniformParameters::FTypeInfo::GetStructMetadata()->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;
}
static ERayTracingPayloadType GetRayTracingPayloadType(const int32 PermutationId)
{
return ERayTracingPayloadType::Decals;
}
static const FShaderBindingLayout* GetShaderBindingLayout(const FShaderPermutationParameters& Parameters)
{
return RayTracing::GetShaderBindingLayout(Parameters.Platform);
}
};
using FRayTracingDecalMaterialCHS = TRayTracingDecalMaterial<false>;
using FRayTracingDecalMaterialCHS_AHS = TRayTracingDecalMaterial<true>;
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>, FRayTracingDecalMaterialCHS, TEXT("/Engine/Private/RayTracing/RayTracingDecalMaterialShader.usf"), TEXT("closesthit=RayTracingDecalMaterialCHS"), SF_RayHitGroup);
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>, FRayTracingDecalMaterialCHS_AHS, TEXT("/Engine/Private/RayTracing/RayTracingDecalMaterialShader.usf"), TEXT("closesthit=RayTracingDecalMaterialCHS anyhit=RayTracingDecalMaterialAHS"), SF_RayHitGroup);
FShaderType* GetRayTracingDecalMaterialShaderType(EBlendMode BlendMode)
{
if (DecalNeedsAnyHitShader(BlendMode))
{
return &FRayTracingDecalMaterialCHS_AHS::GetStaticType();
}
else
{
return &FRayTracingDecalMaterialCHS::GetStaticType();
}
}
class FDefaultOpaqueMeshDecalHitGroup : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FDefaultOpaqueMeshDecalHitGroup)
SHADER_USE_ROOT_PARAMETER_STRUCT(FDefaultOpaqueMeshDecalHitGroup, FGlobalShader)
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return ShouldCompileRayTracingShadersForProject(Parameters.Platform);
}
static ERayTracingPayloadType GetRayTracingPayloadType(const int32 PermutationId)
{
return ERayTracingPayloadType::Decals;
}
static const FShaderBindingLayout* GetShaderBindingLayout(const FShaderPermutationParameters& Parameters)
{
return RayTracing::GetShaderBindingLayout(Parameters.Platform);
}
using FParameters = FEmptyShaderParameters;
};
IMPLEMENT_SHADER_TYPE(, FDefaultOpaqueMeshDecalHitGroup, TEXT("/Engine/Private/RayTracing/RayTracingDefaultDecalHitShader.usf"), TEXT("closesthit=RayTracingDefaultOpaqueDecalMaterialCHS"), SF_RayHitGroup);
class FDefaultHiddenMeshDecalHitGroup : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FDefaultHiddenMeshDecalHitGroup)
SHADER_USE_ROOT_PARAMETER_STRUCT(FDefaultHiddenMeshDecalHitGroup, FGlobalShader)
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return ShouldCompileRayTracingShadersForProject(Parameters.Platform);
}
static ERayTracingPayloadType GetRayTracingPayloadType(const int32 PermutationId)
{
return ERayTracingPayloadType::Decals;
}
static const FShaderBindingLayout* GetShaderBindingLayout(const FShaderPermutationParameters& Parameters)
{
return RayTracing::GetShaderBindingLayout(Parameters.Platform);
}
using FParameters = FEmptyShaderParameters;
};
IMPLEMENT_SHADER_TYPE(, FDefaultHiddenMeshDecalHitGroup, TEXT("/Engine/Private/RayTracing/RayTracingDefaultDecalHitShader.usf"), TEXT("closesthit=RayTracingDefaultHiddenDecalMaterialCHS anyhit=RayTracingDefaultHiddenDecalMaterialAHS"), SF_RayHitGroup);
FRHIRayTracingShader* GetDefaultOpaqueMeshDecalHitShader(const FGlobalShaderMap* ShaderMap)
{
return ShaderMap->GetShader<FDefaultOpaqueMeshDecalHitGroup>().GetRayTracingShader();
}
FRHIRayTracingShader* GetDefaultHiddenMeshDecalHitShader(const FGlobalShaderMap* ShaderMap)
{
return ShaderMap->GetShader<FDefaultHiddenMeshDecalHitGroup>().GetRayTracingShader();
}
void BuildDecalGrid(FRDGBuilder& GraphBuilder, uint32 NumDecals, FRDGBufferSRVRef DecalsSRV, FRayTracingDecals& OutParameters)
{
// if we have some decals -- build a decal grid
if (NumDecals > 0)
{
const uint32 Resolution = FMath::Clamp(CVarDecalGridResolution.GetValueOnRenderThread(), 1, 2048);
const uint32 MaxCount = FMath::Clamp(CVarDecalGridMaxCount.GetValueOnRenderThread(), 1, FMath::Min((int32)NumDecals, RAY_TRACING_DECAL_COUNT_MAXIMUM));
OutParameters.GridResolution = Resolution;
OutParameters.GridMaxCount = MaxCount;
OutParameters.GridAxis = CVarDecalGridAxis.GetValueOnRenderThread();
FRDGTextureDesc DecalGridDesc = FRDGTextureDesc::Create2DArray(
FIntPoint(Resolution, Resolution),
PF_R32_UINT,
FClearValueBinding::None,
TexCreate_ShaderResource | TexCreate_UAV, 3);
FRDGTexture* DecalGridTexture = GraphBuilder.CreateTexture(DecalGridDesc, TEXT("PathTracer.DecalGrid"), ERDGTextureFlags::None);
EPixelFormat DecalGridDataFormat = PF_R32_UINT;
size_t DecalGridDataNumBytes = sizeof(uint32);
if (NumDecals <= (MAX_uint8 + 1))
{
DecalGridDataFormat = PF_R8_UINT;
DecalGridDataNumBytes = sizeof(uint8);
}
else if (NumDecals <= (MAX_uint16 + 1))
{
DecalGridDataFormat = PF_R16_UINT;
DecalGridDataNumBytes = sizeof(uint16);
}
FRDGBufferDesc DecalGridDataDesc = FRDGBufferDesc::CreateBufferDesc(DecalGridDataNumBytes, 3 * MaxCount * Resolution * Resolution);
FRDGBuffer* DecalGridData = GraphBuilder.CreateBuffer(DecalGridDataDesc, TEXT("PathTracer.DecalGridData"));
auto* PassParameters = GraphBuilder.AllocParameters<FRayTracingBuildDecalGridCS::FParameters>();
PassParameters->RWDecalGrid = GraphBuilder.CreateUAV(DecalGridTexture);
PassParameters->RWDecalGridData = GraphBuilder.CreateUAV(DecalGridData, DecalGridDataFormat);
PassParameters->SceneDecals = DecalsSRV;
PassParameters->SceneDecalCount = OutParameters.Count;
PassParameters->SceneDecalsTranslatedBoundMin = OutParameters.TranslatedBoundMin;
PassParameters->SceneDecalsTranslatedBoundMax = OutParameters.TranslatedBoundMax;
PassParameters->DecalGridResolution = OutParameters.GridResolution;
PassParameters->DecalGridMaxCount = OutParameters.GridMaxCount;
PassParameters->DecalGridAxis = OutParameters.GridAxis;
TShaderMapRef<FRayTracingBuildDecalGridCS> ComputeShader(GetGlobalShaderMap(GMaxRHIFeatureLevel));
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("Decal Grid Create (%u decals)", NumDecals),
ComputeShader,
PassParameters,
FComputeShaderUtils::GetGroupCount(FIntVector(Resolution, Resolution, 3), FIntVector(FComputeShaderUtils::kGolden2DGroupSize, FComputeShaderUtils::kGolden2DGroupSize, 1)));
// hookup to the actual rendering pass
OutParameters.Grid = DecalGridTexture;
OutParameters.GridData = GraphBuilder.CreateSRV(DecalGridData, DecalGridDataFormat);
}
else
{
// light grid is not needed - just hookup dummy data
FRDGBufferDesc DecalGridDataDesc = FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), 1);
FRDGBuffer* DecalGridData = GraphBuilder.CreateBuffer(DecalGridDataDesc, TEXT("PathTracer.DecalGridData"));
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(DecalGridData, PF_R32_UINT), 0);
OutParameters.GridResolution = 0;
OutParameters.GridMaxCount = 0;
OutParameters.GridAxis = 0;
OutParameters.Grid = GSystemTextures.GetBlackDummy(GraphBuilder);
OutParameters.GridData = GraphBuilder.CreateSRV(DecalGridData, PF_R32_UINT);
}
}
FRelevantDecalList GetSortedDecals(FVisibleDecalList& VisibleDecals, TConstArrayView<FDeferredDecalProxy*> Decals, FScene& Scene, const FViewInfo& View)
{
const float FadeMultiplier = DecalRendering::GetDecalFadeScreenSizeMultiplier();
const bool bIsPerspectiveProjection = View.IsPerspectiveProjection();
FRelevantDecalList SortedDecals;
// NOTE: Reserve is critical here to avoid reallocation of the underlying array, since the sorted decals array holds a pointer!
SortedDecals.Reserve(Decals.Num());
VisibleDecals.Reserve(Decals.Num());
for (auto DecalProxy : Decals)
{
if (!DecalProxy->DecalMaterial || !DecalProxy->DecalMaterial->IsValidLowLevelFast())
{
continue;
}
bool bIsShown = true;
if (!DecalProxy->IsShown(&View))
{
bIsShown = false;
}
if (bIsShown)
{
float FadeAlpha = 1.0f;
if (bIsPerspectiveProjection && DecalProxy->FadeScreenSize != 0.0f)
{
const FMatrix ComponentToWorldMatrix = DecalProxy->ComponentTrans.ToMatrixWithScale();
FadeAlpha = DecalRendering::CalculateDecalFadeAlpha(DecalProxy->FadeScreenSize, ComponentToWorldMatrix, View, FadeMultiplier);
}
const bool bShouldRender = FadeAlpha > 0.0f;
if (bShouldRender)
{
VisibleDecals.Emplace(*DecalProxy, 0.0f, FadeAlpha, Scene.GetShaderPlatform(), Scene.GetFeatureLevel());
SortedDecals.Emplace(&VisibleDecals.Last());
}
}
}
DecalRendering::SortDecalList(SortedDecals);
return SortedDecals;
}
TRDGUniformBufferRef<FRayTracingDecals> CreateNullRayTracingDecalsUniformBuffer(FRDGBuilder& GraphBuilder)
{
auto* OutParameters = GraphBuilder.AllocParameters<FRayTracingDecals>();
OutParameters->Count = 0;
OutParameters->TranslatedBoundMin = FVector3f::ZeroVector;
OutParameters->TranslatedBoundMax = FVector3f::ZeroVector;
BuildDecalGrid(GraphBuilder, 0, nullptr, *OutParameters);
return GraphBuilder.CreateUniformBuffer(OutParameters);
}
TRDGUniformBufferRef<FRayTracingDecals> CreateRayTracingDecalData(FRDGBuilder& GraphBuilder, FScene& Scene, const FViewInfo& View, uint32 BaseCallableSlotIndex)
{
const EShaderPlatform ShaderPlatform = View.Family->GetShaderPlatform();
FVisibleDecalList VisibleDecals;
FRelevantDecalList SortedDecals = GetSortedDecals(VisibleDecals, Scene.Decals, Scene, View);
// Allocate from the graph builder so that we don't need to copy the data again when queuing the upload
auto& RayTracingDecals = GraphBuilder.AllocArray<FRayTracingDecal>();
RayTracingDecals.Reserve(SortedDecals.Num());
const float Inf = std::numeric_limits<float>::infinity();
FVector3f TranslatedBoundMin(+Inf, +Inf, +Inf);
FVector3f TranslatedBoundMax(-Inf, -Inf, -Inf);
for (int32 Index = 0; Index < SortedDecals.Num(); ++Index)
{
const FVisibleDecal& VisibleDecal = *SortedDecals[Index];
const FMaterialRenderProxy* MaterialProxy = VisibleDecal.MaterialProxy;
const FMaterial* MaterialResource = &MaterialProxy->GetMaterialWithFallback(View.GetFeatureLevel(), MaterialProxy);
const FMaterialShaderMap* MaterialShaderMap = MaterialResource->GetRenderingThreadShaderMap();
auto CallableShader = MaterialShaderMap->GetShader<FRayTracingDecalMaterialShader>();
if (!CallableShader.IsValid())
{
continue;
}
TUniformBufferRef<FDecalParametersRayTracing> DecalParameters = CreateDecalParametersBuffer(View, ShaderPlatform, VisibleDecal, EUniformBufferUsage::UniformBuffer_SingleFrame);
Scene.RayTracingSBT.TransientUniformBuffers.Add(DecalParameters); // Hold uniform buffer ref in RayTracingSBT since FMeshDrawSingleShaderBindings doesn't
FRayTracingShaderCommand& Command = Scene.RayTracingSBT.CallableCommands.AddDefaulted_GetRef();
Command.SetShader(CallableShader);
Command.SlotInScene = BaseCallableSlotIndex + Index;
FMeshDrawSingleShaderBindings SingleShaderBindings = Command.ShaderBindings.GetSingleShaderBindings(SF_RayCallable);
CallableShader->GetShaderBindings(&Scene, Scene.GetFeatureLevel(), *MaterialProxy, *MaterialResource, View, DecalParameters, SingleShaderBindings);
const FBox BoxBounds = VisibleDecal.BoxBounds;
FRayTracingDecal& DestDecal = RayTracingDecals.AddDefaulted_GetRef();
DestDecal.TranslatedBoundMin = FVector3f(BoxBounds.Min + View.ViewMatrices.GetPreViewTranslation());
DestDecal.TranslatedBoundMax = FVector3f(BoxBounds.Max + View.ViewMatrices.GetPreViewTranslation());
DestDecal.CallableSlotIndex = BaseCallableSlotIndex + Index;
{
const FMatrix WorldToDecalMatrix = VisibleDecal.ComponentTrans.ToInverseMatrixWithScale();
DestDecal.TranslatedWorldToDecal = FMatrix44f(FTranslationMatrix(-View.ViewMatrices.GetPreViewTranslation()) * WorldToDecalMatrix);
}
TranslatedBoundMin = FVector3f::Min(TranslatedBoundMin, DestDecal.TranslatedBoundMin);
TranslatedBoundMax = FVector3f::Max(TranslatedBoundMax, DestDecal.TranslatedBoundMax);
}
// Upload the buffer of decals to the GPU
const uint32 NumCopyDecals = FMath::Max(1u, (uint32)RayTracingDecals.Num()); // need at least one since zero-sized buffers are not allowed
const size_t DataSize = sizeof(FRayTracingDecal) * RayTracingDecals.Num();
FRDGBufferSRVRef DecalsSRV = GraphBuilder.CreateSRV(FRDGBufferSRVDesc(CreateStructuredBuffer(GraphBuilder, TEXT("PathTracer.DecalsBuffer"), sizeof(FRayTracingDecal), NumCopyDecals, RayTracingDecals.GetData(), DataSize, ERDGInitialDataFlags::NoCopy)));
auto* OutParameters = GraphBuilder.AllocParameters<FRayTracingDecals>();
OutParameters->Count = RayTracingDecals.Num();
OutParameters->TranslatedBoundMin = TranslatedBoundMin;
OutParameters->TranslatedBoundMax = TranslatedBoundMax;
BuildDecalGrid(GraphBuilder, RayTracingDecals.Num(), DecalsSRV, *OutParameters);
return GraphBuilder.CreateUniformBuffer(OutParameters);
}
#endif // RHI_RAYTRACING