You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
* Existing calls to CreateSceneTextureShaderParameters and similar functions use "GetSceneTexturesChecked", which allows for the possibility that they are reached in a code path where scene textures haven't been initialized, and nullptr is returned instead of asserting. The shader parameter setup functions then fill in dummy defaults for that case. The goal was to precisely match the original behavior, which queried the RDG blackboard, and gracefully handled null if scene textures weren't there. This definitely appears to occur in FNiagaraGpuComputeDispatch::ProcessPendingTicksFlush, which can be called with a dummy scene with no scene textures. In the future, I may change this so dummy defaults are filled in for FSceneTextures at construction time, so the structure is never in an uninitialized state, but I would like to set up a test case for the Niagara code path before doing that, and the checks aren't harmful in the meantime. * I marked as deprecated global functions which query values from FSceneTexturesConfig, but they'll still work with the caveat that if you use multi-view-family rendering, the results will be indeterminate (whatever view family rendered last). There was only one case outside the scene renderer that accessed the globals (depth clear value), which I removed, noting that there is nowhere in the code where we modify the depth clear value from its global default. I would like to permanently deprecate or remove these at some point. Display Cluster is the only code that's currently using the multi-view-family code path, and as a new (still incomplete) feature, third party code can't be using it, and won't be affected. #jira NONE #rb chris.kulla zach.bethel mihnea.balta #preflight 6261aca76119a1a496bd2644 [CL 19873983 by jason hoerner in ue5-main branch]
692 lines
27 KiB
C++
692 lines
27 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
VolumetricFogVoxelization.cpp
|
|
=============================================================================*/
|
|
|
|
#include "VolumetricFog.h"
|
|
#include "RendererPrivate.h"
|
|
#include "ScenePrivate.h"
|
|
#include "SceneUtils.h"
|
|
#include "VolumetricFogShared.h"
|
|
#include "LocalVertexFactory.h"
|
|
#include "DynamicMeshBuilder.h"
|
|
#include "SpriteIndexBuffer.h"
|
|
#include "StaticMeshResources.h"
|
|
#include "MeshPassProcessor.inl"
|
|
#include "VolumetricCloudRendering.h"
|
|
|
|
int32 GVolumetricFogVoxelizationSlicesPerGSPass = 8;
|
|
FAutoConsoleVariableRef CVarVolumetricFogVoxelizationSlicesPerPass(
|
|
TEXT("r.VolumetricFog.VoxelizationSlicesPerGSPass"),
|
|
GVolumetricFogVoxelizationSlicesPerGSPass,
|
|
TEXT("How many depth slices to render in a single voxelization pass (max geometry shader expansion). Must recompile voxelization shaders to propagate changes."),
|
|
ECVF_ReadOnly
|
|
);
|
|
|
|
int32 GVolumetricFogVoxelizationShowOnlyPassIndex = -1;
|
|
FAutoConsoleVariableRef CVarVolumetricFogVoxelizationShowOnlyPassIndex(
|
|
TEXT("r.VolumetricFog.VoxelizationShowOnlyPassIndex"),
|
|
GVolumetricFogVoxelizationShowOnlyPassIndex,
|
|
TEXT("When >= 0, indicates a single voxelization pass to render for debugging."),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static FORCEINLINE int32 GetVoxelizationSlicesPerPass(EShaderPlatform Platform)
|
|
{
|
|
return RHISupportsGeometryShaders(Platform) ? GVolumetricFogVoxelizationSlicesPerGSPass : 1;
|
|
}
|
|
|
|
IMPLEMENT_STATIC_UNIFORM_BUFFER_STRUCT(FVoxelizeVolumePassUniformParameters, "VoxelizeVolumePass", SceneTextures);
|
|
|
|
TRDGUniformBufferRef<FVoxelizeVolumePassUniformParameters> CreateVoxelizeVolumePassUniformBuffer(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FViewInfo& View,
|
|
const FVolumetricFogIntegrationParameterData& IntegrationData,
|
|
FVector2D Jitter,
|
|
const FVolumetricCloudRenderSceneInfo* CloudInfo)
|
|
{
|
|
auto* Parameters = GraphBuilder.AllocParameters<FVoxelizeVolumePassUniformParameters>();
|
|
SetupSceneTextureUniformParameters(GraphBuilder, &View.GetSceneTextures(), View.FeatureLevel, ESceneTextureSetupMode::None, Parameters->SceneTextures);
|
|
|
|
Parameters->ViewToVolumeClip = FMatrix44f(View.ViewMatrices.ComputeProjectionNoAAMatrix()); // LWC_TODO: Precision loss?
|
|
Parameters->ViewToVolumeClip.M[2][0] += Jitter.X;
|
|
Parameters->ViewToVolumeClip.M[2][1] += Jitter.Y;
|
|
|
|
Parameters->FrameJitterOffset0 = IntegrationData.FrameJitterOffsetValues[0];
|
|
|
|
SetupVolumetricFogGlobalData(View, Parameters->VolumetricFog);
|
|
|
|
if (CloudInfo)
|
|
{
|
|
const FVolumetricCloudCommonShaderParameters& CloudGlobalShaderParams = CloudInfo->GetVolumetricCloudCommonShaderParameters();
|
|
Parameters->RenderVolumetricCloudParametersCloudLayerCenterKm = CloudGlobalShaderParams.CloudLayerCenterKm;
|
|
Parameters->RenderVolumetricCloudParametersPlanetRadiusKm = CloudGlobalShaderParams.PlanetRadiusKm;
|
|
Parameters->RenderVolumetricCloudParametersBottomRadiusKm = CloudGlobalShaderParams.BottomRadiusKm;
|
|
Parameters->RenderVolumetricCloudParametersTopRadiusKm = CloudGlobalShaderParams.TopRadiusKm;
|
|
}
|
|
else
|
|
{
|
|
Parameters->RenderVolumetricCloudParametersCloudLayerCenterKm = FVector3f::ZeroVector;
|
|
Parameters->RenderVolumetricCloudParametersPlanetRadiusKm = 0.001f;
|
|
Parameters->RenderVolumetricCloudParametersBottomRadiusKm = 0.5f;
|
|
Parameters->RenderVolumetricCloudParametersTopRadiusKm = 1.0f;
|
|
}
|
|
|
|
return GraphBuilder.CreateUniformBuffer(Parameters);
|
|
}
|
|
|
|
class FQuadMeshVertexBuffer : public FRenderResource
|
|
{
|
|
public:
|
|
FStaticMeshVertexBuffers Buffers;
|
|
|
|
FQuadMeshVertexBuffer()
|
|
{
|
|
TArray<FDynamicMeshVertex> Vertices;
|
|
|
|
// Vertex position constructed in the shader
|
|
Vertices.Add(FDynamicMeshVertex(FVector3f(0.0f, 0.0f, 0.0f)));
|
|
Vertices.Add(FDynamicMeshVertex(FVector3f(0.0f, 0.0f, 0.0f)));
|
|
Vertices.Add(FDynamicMeshVertex(FVector3f(0.0f, 0.0f, 0.0f)));
|
|
Vertices.Add(FDynamicMeshVertex(FVector3f(0.0f, 0.0f, 0.0f)));
|
|
|
|
Buffers.PositionVertexBuffer.Init(Vertices.Num());
|
|
Buffers.StaticMeshVertexBuffer.Init(Vertices.Num(), 1);
|
|
|
|
for (int32 i = 0; i < Vertices.Num(); i++)
|
|
{
|
|
const FDynamicMeshVertex& Vertex = Vertices[i];
|
|
|
|
Buffers.PositionVertexBuffer.VertexPosition(i) = Vertex.Position;
|
|
Buffers.StaticMeshVertexBuffer.SetVertexTangents(i, Vertex.TangentX.ToFVector3f(), Vertex.GetTangentY(), Vertex.TangentZ.ToFVector3f());
|
|
Buffers.StaticMeshVertexBuffer.SetVertexUV(i, 0, Vertex.TextureCoordinate[0]);
|
|
}
|
|
}
|
|
|
|
virtual void InitRHI() override
|
|
{
|
|
Buffers.PositionVertexBuffer.InitResource();
|
|
Buffers.StaticMeshVertexBuffer.InitResource();
|
|
}
|
|
|
|
virtual void ReleaseRHI() override
|
|
{
|
|
Buffers.PositionVertexBuffer.ReleaseRHI();
|
|
Buffers.PositionVertexBuffer.ReleaseResource();
|
|
Buffers.StaticMeshVertexBuffer.ReleaseRHI();
|
|
Buffers.StaticMeshVertexBuffer.ReleaseResource();
|
|
}
|
|
};
|
|
|
|
TGlobalResource<FQuadMeshVertexBuffer> GQuadMeshVertexBuffer;
|
|
|
|
TGlobalResource<FSpriteIndexBuffer<1>> GQuadMeshIndexBuffer;
|
|
|
|
class FQuadMeshVertexFactory final : public FLocalVertexFactory
|
|
{
|
|
public:
|
|
FQuadMeshVertexFactory(ERHIFeatureLevel::Type InFeatureLevel)
|
|
: FLocalVertexFactory(InFeatureLevel, "FQuadMeshVertexFactory")
|
|
{}
|
|
|
|
~FQuadMeshVertexFactory()
|
|
{
|
|
ReleaseResource();
|
|
}
|
|
|
|
virtual void InitRHI() override
|
|
{
|
|
FQuadMeshVertexBuffer* VertexBuffer = &GQuadMeshVertexBuffer;
|
|
FLocalVertexFactory::FDataType NewData;
|
|
VertexBuffer->Buffers.PositionVertexBuffer.BindPositionVertexBuffer(this, NewData);
|
|
VertexBuffer->Buffers.StaticMeshVertexBuffer.BindTangentVertexBuffer(this, NewData);
|
|
VertexBuffer->Buffers.StaticMeshVertexBuffer.BindPackedTexCoordVertexBuffer(this, NewData);
|
|
VertexBuffer->Buffers.StaticMeshVertexBuffer.BindLightMapVertexBuffer(this, NewData, 0);
|
|
FColorVertexBuffer::BindDefaultColorVertexBuffer(this, NewData, FColorVertexBuffer::NullBindStride::ZeroForDefaultBufferBind);
|
|
// Don't call SetData(), because that ends up calling UpdateRHI(), and if the resource has already been initialized
|
|
// (e.g. when switching the feature level in the editor), that calls InitRHI(), resulting in an infinite loop.
|
|
Data = NewData;
|
|
FLocalVertexFactory::InitRHI();
|
|
}
|
|
|
|
bool HasIncompatibleFeatureLevel(ERHIFeatureLevel::Type InFeatureLevel)
|
|
{
|
|
return InFeatureLevel != GetFeatureLevel();
|
|
}
|
|
};
|
|
|
|
FQuadMeshVertexFactory* GQuadMeshVertexFactory = NULL;
|
|
|
|
class FVoxelizeVolumeShaderElementData : public FMeshMaterialShaderElementData
|
|
{
|
|
public:
|
|
FVoxelizeVolumeShaderElementData(int32 InVoxelizationPassIndex)
|
|
: VoxelizationPassIndex(InVoxelizationPassIndex)
|
|
{}
|
|
|
|
int32 VoxelizationPassIndex;
|
|
};
|
|
|
|
class FVoxelizeVolumeVS : public FMeshMaterialShader
|
|
{
|
|
public:
|
|
DECLARE_INLINE_TYPE_LAYOUT(FVoxelizeVolumeVS, NonVirtual);
|
|
|
|
FVoxelizeVolumeVS() = default;
|
|
FVoxelizeVolumeVS(const FMeshMaterialShaderType::CompiledShaderInitializerType& Initializer)
|
|
: FMeshMaterialShader(Initializer)
|
|
{
|
|
VoxelizationPassIndex.Bind(Initializer.ParameterMap, TEXT("VoxelizationPassIndex"));
|
|
}
|
|
|
|
static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters)
|
|
{
|
|
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5)
|
|
&& DoesPlatformSupportVolumetricFogVoxelization(Parameters.Platform)
|
|
&& Parameters.MaterialParameters.MaterialDomain == MD_Volume;
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FMeshMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
if (RHISupportsGeometryShaders(Parameters.Platform))
|
|
{
|
|
OutEnvironment.CompilerFlags.Add( CFLAG_VertexToGeometryShader );
|
|
}
|
|
}
|
|
|
|
void GetShaderBindings(
|
|
const FScene* Scene,
|
|
ERHIFeatureLevel::Type FeatureLevel,
|
|
const FPrimitiveSceneProxy* PrimitiveSceneProxy,
|
|
const FMaterialRenderProxy& MaterialRenderProxy,
|
|
const FMaterial& Material,
|
|
const FMeshPassProcessorRenderState& DrawRenderState,
|
|
const FVoxelizeVolumeShaderElementData& ShaderElementData,
|
|
FMeshDrawSingleShaderBindings& ShaderBindings) const
|
|
{
|
|
FMeshMaterialShader::GetShaderBindings(Scene, FeatureLevel, PrimitiveSceneProxy, MaterialRenderProxy, Material, DrawRenderState, ShaderElementData, ShaderBindings);
|
|
if (!RHISupportsGeometryShaders(Scene->GetShaderPlatform()))
|
|
{
|
|
ShaderBindings.Add(VoxelizationPassIndex, ShaderElementData.VoxelizationPassIndex);
|
|
}
|
|
}
|
|
|
|
protected:
|
|
LAYOUT_FIELD(FShaderParameter, VoxelizationPassIndex);
|
|
};
|
|
|
|
enum EVoxelizeShapeMode
|
|
{
|
|
VMode_Primitive_Sphere,
|
|
VMode_Object_Box
|
|
};
|
|
|
|
template<EVoxelizeShapeMode Mode>
|
|
class TVoxelizeVolumeVS : public FVoxelizeVolumeVS
|
|
{
|
|
public:
|
|
DECLARE_SHADER_TYPE(TVoxelizeVolumeVS,MeshMaterial);
|
|
typedef FVoxelizeVolumeVS Super;
|
|
|
|
TVoxelizeVolumeVS() = default;
|
|
TVoxelizeVolumeVS(const FMeshMaterialShaderType::CompiledShaderInitializerType& Initializer)
|
|
: Super(Initializer)
|
|
{}
|
|
|
|
static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
Super::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
|
|
if (Mode == VMode_Primitive_Sphere)
|
|
{
|
|
OutEnvironment.SetDefine(TEXT("VOXELIZE_SHAPE_MODE"), TEXT("PRIMITIVE_SPHERE_MODE"));
|
|
}
|
|
else if (Mode == VMode_Object_Box)
|
|
{
|
|
OutEnvironment.SetDefine(TEXT("VOXELIZE_SHAPE_MODE"), TEXT("OBJECT_BOX_MODE"));
|
|
}
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TVoxelizeVolumeVS<VMode_Primitive_Sphere>,TEXT("/Engine/Private/VolumetricFogVoxelization.usf"),TEXT("VoxelizeVS"),SF_Vertex);
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TVoxelizeVolumeVS<VMode_Object_Box>,TEXT("/Engine/Private/VolumetricFogVoxelization.usf"),TEXT("VoxelizeVS"),SF_Vertex);
|
|
|
|
class FVoxelizeVolumeGS : public FMeshMaterialShader
|
|
{
|
|
public:
|
|
DECLARE_INLINE_TYPE_LAYOUT(FVoxelizeVolumeGS, NonVirtual);
|
|
|
|
FVoxelizeVolumeGS() = default;
|
|
FVoxelizeVolumeGS(const FMeshMaterialShaderType::CompiledShaderInitializerType& Initializer)
|
|
: FMeshMaterialShader(Initializer)
|
|
{
|
|
VoxelizationPassIndex.Bind(Initializer.ParameterMap, TEXT("VoxelizationPassIndex"));
|
|
}
|
|
|
|
static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters)
|
|
{
|
|
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5)
|
|
&& RHISupportsGeometryShaders(Parameters.Platform)
|
|
&& DoesPlatformSupportVolumetricFogVoxelization(Parameters.Platform)
|
|
&& Parameters.MaterialParameters.MaterialDomain == MD_Volume;
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FMeshMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
OutEnvironment.SetDefine(TEXT("MAX_SLICES_PER_VOXELIZATION_PASS"), GetVoxelizationSlicesPerPass(Parameters.Platform));
|
|
}
|
|
|
|
void GetShaderBindings(
|
|
const FScene* Scene,
|
|
ERHIFeatureLevel::Type FeatureLevel,
|
|
const FPrimitiveSceneProxy* PrimitiveSceneProxy,
|
|
const FMaterialRenderProxy& MaterialRenderProxy,
|
|
const FMaterial& Material,
|
|
const FMeshPassProcessorRenderState& DrawRenderState,
|
|
const FVoxelizeVolumeShaderElementData& ShaderElementData,
|
|
FMeshDrawSingleShaderBindings& ShaderBindings) const
|
|
{
|
|
FMeshMaterialShader::GetShaderBindings(Scene, FeatureLevel, PrimitiveSceneProxy, MaterialRenderProxy, Material, DrawRenderState, ShaderElementData, ShaderBindings);
|
|
|
|
ShaderBindings.Add(VoxelizationPassIndex, ShaderElementData.VoxelizationPassIndex);
|
|
}
|
|
|
|
protected:
|
|
LAYOUT_FIELD(FShaderParameter, VoxelizationPassIndex);
|
|
};
|
|
|
|
template<EVoxelizeShapeMode Mode>
|
|
class TVoxelizeVolumeGS : public FVoxelizeVolumeGS
|
|
{
|
|
public:
|
|
DECLARE_SHADER_TYPE(TVoxelizeVolumeGS,MeshMaterial);
|
|
typedef FVoxelizeVolumeGS Super;
|
|
|
|
TVoxelizeVolumeGS() = default;
|
|
TVoxelizeVolumeGS(const FMeshMaterialShaderType::CompiledShaderInitializerType& Initializer)
|
|
: Super(Initializer)
|
|
{}
|
|
|
|
static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
Super::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
|
|
if (Mode == VMode_Primitive_Sphere)
|
|
{
|
|
OutEnvironment.SetDefine(TEXT("VOXELIZE_SHAPE_MODE"), TEXT("PRIMITIVE_SPHERE_MODE"));
|
|
}
|
|
else if (Mode == VMode_Object_Box)
|
|
{
|
|
OutEnvironment.SetDefine(TEXT("VOXELIZE_SHAPE_MODE"), TEXT("OBJECT_BOX_MODE"));
|
|
}
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TVoxelizeVolumeGS<VMode_Primitive_Sphere>,TEXT("/Engine/Private/VolumetricFogVoxelization.usf"),TEXT("VoxelizeGS"),SF_Geometry);
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TVoxelizeVolumeGS<VMode_Object_Box>,TEXT("/Engine/Private/VolumetricFogVoxelization.usf"),TEXT("VoxelizeGS"),SF_Geometry);
|
|
|
|
class FVoxelizeVolumePS : public FMeshMaterialShader
|
|
{
|
|
public:
|
|
DECLARE_INLINE_TYPE_LAYOUT(FVoxelizeVolumePS, NonVirtual);
|
|
|
|
FVoxelizeVolumePS() = default;
|
|
FVoxelizeVolumePS(const FMeshMaterialShaderType::CompiledShaderInitializerType& Initializer)
|
|
: FMeshMaterialShader(Initializer)
|
|
{}
|
|
|
|
static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters)
|
|
{
|
|
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5)
|
|
&& DoesPlatformSupportVolumetricFogVoxelization(Parameters.Platform)
|
|
&& Parameters.MaterialParameters.MaterialDomain == MD_Volume;
|
|
}
|
|
};
|
|
|
|
template<EVoxelizeShapeMode Mode>
|
|
class TVoxelizeVolumePS : public FVoxelizeVolumePS
|
|
{
|
|
DECLARE_SHADER_TYPE(TVoxelizeVolumePS,MeshMaterial);
|
|
typedef FVoxelizeVolumePS Super;
|
|
|
|
protected:
|
|
|
|
TVoxelizeVolumePS() = default;
|
|
TVoxelizeVolumePS(const FMeshMaterialShaderType::CompiledShaderInitializerType& Initializer):
|
|
Super(Initializer)
|
|
{}
|
|
|
|
public:
|
|
|
|
static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
Super::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
|
|
if (Mode == VMode_Primitive_Sphere)
|
|
{
|
|
OutEnvironment.SetDefine(TEXT("VOXELIZE_SHAPE_MODE"), TEXT("PRIMITIVE_SPHERE_MODE"));
|
|
}
|
|
else if (Mode == VMode_Object_Box)
|
|
{
|
|
OutEnvironment.SetDefine(TEXT("VOXELIZE_SHAPE_MODE"), TEXT("OBJECT_BOX_MODE"));
|
|
}
|
|
OutEnvironment.SetDefine(TEXT("CLOUD_LAYER_PIXEL_SHADER"), TEXT("1")); // This is to enable cloud data on the MaterialParameter structure.
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TVoxelizeVolumePS<VMode_Primitive_Sphere>,TEXT("/Engine/Private/VolumetricFogVoxelization.usf"),TEXT("VoxelizePS"),SF_Pixel);
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TVoxelizeVolumePS<VMode_Object_Box>,TEXT("/Engine/Private/VolumetricFogVoxelization.usf"),TEXT("VoxelizePS"),SF_Pixel);
|
|
|
|
class FVoxelizeVolumeMeshProcessor : public FMeshPassProcessor
|
|
{
|
|
public:
|
|
FVoxelizeVolumeMeshProcessor(const FScene* Scene, const FViewInfo* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext);
|
|
|
|
void AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, int32 NumVoxelizationPasses, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy);
|
|
|
|
virtual void AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId = -1) override final
|
|
{
|
|
checkf(false, TEXT("Default AddMeshBatch can't be used as rendering requires extra parameters per pass."));
|
|
}
|
|
|
|
private:
|
|
bool TryAddMeshBatch(
|
|
const FMeshBatch& RESTRICT MeshBatch,
|
|
uint64 BatchElementMask,
|
|
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
|
|
const FMaterialRenderProxy& MaterialRenderProxy,
|
|
const FMaterial& Material,
|
|
int32 NumVoxelizationPasses);
|
|
|
|
bool Process(
|
|
const FMeshBatch& MeshBatch,
|
|
uint64 BatchElementMask,
|
|
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
|
|
const FMaterialRenderProxy& RESTRICT MaterialRenderProxy,
|
|
const FMaterial& RESTRICT MaterialResource,
|
|
int32 NumVoxelizationPasses,
|
|
ERasterizerFillMode MeshFillMode,
|
|
ERasterizerCullMode MeshCullMode);
|
|
|
|
FMeshPassProcessorRenderState PassDrawRenderState;
|
|
};
|
|
|
|
FVoxelizeVolumeMeshProcessor::FVoxelizeVolumeMeshProcessor(const FScene* Scene, const FViewInfo* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext)
|
|
: FMeshPassProcessor(Scene, Scene->GetFeatureLevel(), InViewIfDynamicMeshCommand, InDrawListContext)
|
|
{
|
|
PassDrawRenderState.SetBlendState(TStaticBlendState<
|
|
CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_One, BF_One,
|
|
CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_One, BF_One>::GetRHI());
|
|
PassDrawRenderState.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
|
|
}
|
|
|
|
void FVoxelizeVolumeMeshProcessor::AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, int32 NumVoxelizationPasses, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy)
|
|
{
|
|
const FMaterialRenderProxy* MaterialRenderProxy = MeshBatch.MaterialRenderProxy;
|
|
while (MaterialRenderProxy)
|
|
{
|
|
const FMaterial* Material = MaterialRenderProxy->GetMaterialNoFallback(FeatureLevel);
|
|
if (Material)
|
|
{
|
|
if (TryAddMeshBatch(MeshBatch, BatchElementMask, PrimitiveSceneProxy, *MaterialRenderProxy, *Material, NumVoxelizationPasses))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
MaterialRenderProxy = MaterialRenderProxy->GetFallback(FeatureLevel);
|
|
}
|
|
}
|
|
|
|
bool FVoxelizeVolumeMeshProcessor::TryAddMeshBatch(
|
|
const FMeshBatch& RESTRICT MeshBatch,
|
|
uint64 BatchElementMask,
|
|
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
|
|
const FMaterialRenderProxy& MaterialRenderProxy,
|
|
const FMaterial& Material,
|
|
int32 NumVoxelizationPasses)
|
|
{
|
|
// Determine the mesh's material and blend mode.
|
|
const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(MeshBatch);
|
|
const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(MeshBatch, Material, OverrideSettings);
|
|
const ERasterizerCullMode MeshCullMode = CM_None;
|
|
|
|
return Process(MeshBatch, BatchElementMask, PrimitiveSceneProxy, MaterialRenderProxy, Material, NumVoxelizationPasses, MeshFillMode, MeshCullMode);
|
|
}
|
|
|
|
bool FVoxelizeVolumeMeshProcessor::Process(
|
|
const FMeshBatch& MeshBatch,
|
|
uint64 BatchElementMask,
|
|
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
|
|
const FMaterialRenderProxy& RESTRICT MaterialRenderProxy,
|
|
const FMaterial& RESTRICT MaterialResource,
|
|
int32 NumVoxelizationPasses,
|
|
ERasterizerFillMode MeshFillMode,
|
|
ERasterizerCullMode MeshCullMode)
|
|
{
|
|
const FVertexFactory* VertexFactory = MeshBatch.VertexFactory;
|
|
|
|
TMeshProcessorShaders<
|
|
FVoxelizeVolumeVS,
|
|
FVoxelizeVolumePS,
|
|
FVoxelizeVolumeGS> PassShaders;
|
|
|
|
const bool bUsePrimitiveSphere = VertexFactory != GQuadMeshVertexFactory;
|
|
|
|
FMaterialShaderTypes ShaderTypes;
|
|
if (bUsePrimitiveSphere)
|
|
{
|
|
ShaderTypes.AddShaderType<TVoxelizeVolumeVS<VMode_Primitive_Sphere>>();
|
|
if (RHISupportsGeometryShaders(GShaderPlatformForFeatureLevel[FeatureLevel]))
|
|
{
|
|
ShaderTypes.AddShaderType<TVoxelizeVolumeGS<VMode_Primitive_Sphere>>();
|
|
}
|
|
ShaderTypes.AddShaderType<TVoxelizeVolumePS<VMode_Primitive_Sphere>>();
|
|
}
|
|
else
|
|
{
|
|
ShaderTypes.AddShaderType<TVoxelizeVolumeVS<VMode_Object_Box>>();
|
|
if (RHISupportsGeometryShaders(GShaderPlatformForFeatureLevel[FeatureLevel]))
|
|
{
|
|
ShaderTypes.AddShaderType<TVoxelizeVolumeGS<VMode_Object_Box>>();
|
|
}
|
|
ShaderTypes.AddShaderType<TVoxelizeVolumePS<VMode_Object_Box>>();
|
|
}
|
|
|
|
FMaterialShaders Shaders;
|
|
if (!MaterialResource.TryGetShaders(ShaderTypes, VertexFactory->GetType(), Shaders))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Shaders.TryGetVertexShader(PassShaders.VertexShader);
|
|
if (RHISupportsGeometryShaders(GShaderPlatformForFeatureLevel[FeatureLevel]))
|
|
{
|
|
Shaders.TryGetGeometryShader(PassShaders.GeometryShader);
|
|
}
|
|
Shaders.TryGetPixelShader(PassShaders.PixelShader);
|
|
|
|
const FMeshDrawCommandSortKey SortKey = CalculateMeshStaticSortKey(PassShaders.VertexShader, PassShaders.PixelShader);
|
|
|
|
for (int32 VoxelizationPassIndex = 0; VoxelizationPassIndex < NumVoxelizationPasses; VoxelizationPassIndex++)
|
|
{
|
|
if (GVolumetricFogVoxelizationShowOnlyPassIndex < 0 || GVolumetricFogVoxelizationShowOnlyPassIndex == VoxelizationPassIndex)
|
|
{
|
|
FVoxelizeVolumeShaderElementData ShaderElementData(VoxelizationPassIndex);
|
|
ShaderElementData.InitializeMeshMaterialData(ViewIfDynamicMeshCommand, PrimitiveSceneProxy, MeshBatch, -1, true);
|
|
|
|
BuildMeshDrawCommands(
|
|
MeshBatch,
|
|
BatchElementMask,
|
|
PrimitiveSceneProxy,
|
|
MaterialRenderProxy,
|
|
MaterialResource,
|
|
PassDrawRenderState,
|
|
PassShaders,
|
|
MeshFillMode,
|
|
MeshCullMode,
|
|
SortKey,
|
|
EMeshPassFeatures::Default,
|
|
ShaderElementData);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void VoxelizeVolumePrimitive(FVoxelizeVolumeMeshProcessor& PassMeshProcessor,
|
|
FRHICommandListImmediate& RHICmdList,
|
|
const FViewInfo& View,
|
|
FIntVector VolumetricFogGridSize,
|
|
FVector GridZParams,
|
|
const FPrimitiveSceneProxy* PrimitiveSceneProxy,
|
|
const FMeshBatch& OriginalMesh)
|
|
{
|
|
const FMaterialRenderProxy* MaterialProxy = OriginalMesh.MaterialRenderProxy;
|
|
const FMaterial& Material = OriginalMesh.MaterialRenderProxy->GetMaterialWithFallback(View.GetFeatureLevel(), MaterialProxy);
|
|
|
|
if (Material.GetMaterialDomain() == MD_Volume)
|
|
{
|
|
FMeshBatch LocalQuadMesh;
|
|
|
|
// The voxelization shaders require camera facing quads as input
|
|
// Vertex factories like particle sprites can work as-is, everything else needs to override with a camera facing quad
|
|
const bool bOverrideWithQuadMesh = !OriginalMesh.VertexFactory->RendersPrimitivesAsCameraFacingSprites();
|
|
|
|
if (bOverrideWithQuadMesh)
|
|
{
|
|
LocalQuadMesh.VertexFactory = GQuadMeshVertexFactory;
|
|
LocalQuadMesh.MaterialRenderProxy = MaterialProxy;
|
|
LocalQuadMesh.Elements[0].IndexBuffer = &GQuadMeshIndexBuffer;
|
|
LocalQuadMesh.Elements[0].PrimitiveUniformBuffer = OriginalMesh.Elements[0].PrimitiveUniformBuffer;
|
|
LocalQuadMesh.Elements[0].FirstIndex = 0;
|
|
LocalQuadMesh.Elements[0].NumPrimitives = 2;
|
|
LocalQuadMesh.Elements[0].MinVertexIndex = 0;
|
|
LocalQuadMesh.Elements[0].MaxVertexIndex = 3;
|
|
}
|
|
|
|
const FMeshBatch& Mesh = bOverrideWithQuadMesh ? LocalQuadMesh : OriginalMesh;
|
|
|
|
FBoxSphereBounds Bounds = PrimitiveSceneProxy->GetBounds();
|
|
//@todo - compute NumSlices based on the largest particle size. Bounds is overly conservative in most cases.
|
|
float BoundsCenterDepth = View.ViewMatrices.GetViewMatrix().TransformPosition(Bounds.Origin).Z;
|
|
int32 NearSlice = ComputeZSliceFromDepth(BoundsCenterDepth - Bounds.SphereRadius, GridZParams);
|
|
int32 FarSlice = ComputeZSliceFromDepth(BoundsCenterDepth + Bounds.SphereRadius, GridZParams);
|
|
|
|
NearSlice = FMath::Clamp(NearSlice, 0, VolumetricFogGridSize.Z - 1);
|
|
FarSlice = FMath::Clamp(FarSlice, 0, VolumetricFogGridSize.Z - 1);
|
|
|
|
const int32 NumSlices = FarSlice - NearSlice + 1;
|
|
const int32 NumVoxelizationPasses = FMath::DivideAndRoundUp(NumSlices, GetVoxelizationSlicesPerPass(View.GetShaderPlatform()));
|
|
|
|
const uint64 DefaultBatchElementMask = ~0ull;
|
|
PassMeshProcessor.AddMeshBatch(Mesh, DefaultBatchElementMask, NumVoxelizationPasses, PrimitiveSceneProxy);
|
|
}
|
|
}
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FVoxelizeVolumePassParameters, )
|
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
|
|
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVoxelizeVolumePassUniformParameters, Pass)
|
|
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FInstanceCullingGlobalUniforms, InstanceCulling)
|
|
RENDER_TARGET_BINDING_SLOTS()
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
void FDeferredShadingSceneRenderer::VoxelizeFogVolumePrimitives(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FViewInfo& View,
|
|
const FVolumetricFogIntegrationParameterData& IntegrationData,
|
|
FIntVector VolumetricFogGridSize,
|
|
FVector GridZParams,
|
|
float VolumetricFogDistance,
|
|
bool bVoxelizeEmissive)
|
|
{
|
|
if (View.VolumetricMeshBatches.Num() > 0 && DoesPlatformSupportVolumetricFogVoxelization(View.GetShaderPlatform()))
|
|
{
|
|
const FVector2D Jitter(
|
|
IntegrationData.FrameJitterOffsetValues[0].X / VolumetricFogGridSize.X,
|
|
IntegrationData.FrameJitterOffsetValues[0].Y / VolumetricFogGridSize.Y);
|
|
|
|
auto* PassParameters = GraphBuilder.AllocParameters<FVoxelizeVolumePassParameters>();
|
|
PassParameters->Pass = CreateVoxelizeVolumePassUniformBuffer(GraphBuilder, View, IntegrationData, Jitter, Scene->GetVolumetricCloudSceneInfo());
|
|
PassParameters->InstanceCulling = FInstanceCullingContext::CreateDummyInstanceCullingUniformBuffer(GraphBuilder);
|
|
PassParameters->RenderTargets[0] = FRenderTargetBinding(IntegrationData.VBufferA, ERenderTargetLoadAction::ELoad);
|
|
if (bVoxelizeEmissive)
|
|
{
|
|
PassParameters->RenderTargets[1] = FRenderTargetBinding(IntegrationData.VBufferB, ERenderTargetLoadAction::ELoad);
|
|
}
|
|
|
|
{
|
|
FViewUniformShaderParameters ViewVoxelizeParameters = *View.CachedViewUniformShaderParameters;
|
|
|
|
// Update the parts of VoxelizeParameters which are dependent on the buffer size and view rect
|
|
View.SetupViewRectUniformBufferParameters(
|
|
ViewVoxelizeParameters,
|
|
FIntPoint(VolumetricFogGridSize.X, VolumetricFogGridSize.Y),
|
|
FIntRect(0, 0, VolumetricFogGridSize.X, VolumetricFogGridSize.Y),
|
|
View.ViewMatrices,
|
|
View.PrevViewInfo.ViewMatrices
|
|
);
|
|
|
|
PassParameters->View = TUniformBufferRef<FViewUniformShaderParameters>::CreateUniformBufferImmediate(ViewVoxelizeParameters, UniformBuffer_SingleFrame);
|
|
}
|
|
|
|
if (!GQuadMeshVertexFactory || GQuadMeshVertexFactory->HasIncompatibleFeatureLevel(View.GetFeatureLevel()))
|
|
{
|
|
if (GQuadMeshVertexFactory)
|
|
{
|
|
GQuadMeshVertexFactory->ReleaseResource();
|
|
delete GQuadMeshVertexFactory;
|
|
}
|
|
GQuadMeshVertexFactory = new FQuadMeshVertexFactory(View.GetFeatureLevel());
|
|
GQuadMeshVertexBuffer.UpdateRHI();
|
|
GQuadMeshVertexFactory->InitResource();
|
|
}
|
|
|
|
GraphBuilder.AddPass(
|
|
RDG_EVENT_NAME("VoxelizeVolumePrimitives"),
|
|
PassParameters,
|
|
ERDGPassFlags::Raster,
|
|
[PassParameters, Scene = Scene, &View, VolumetricFogGridSize, IntegrationData, VolumetricFogDistance, GridZParams](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
FMeshPassProcessorRenderState DrawRenderState;
|
|
|
|
DrawDynamicMeshPass(View, RHICmdList,
|
|
[&View, VolumetricFogDistance, &RHICmdList, &VolumetricFogGridSize, &GridZParams](FDynamicPassMeshDrawListContext* DynamicMeshPassContext)
|
|
{
|
|
FVoxelizeVolumeMeshProcessor PassMeshProcessor(
|
|
View.Family->Scene->GetRenderScene(),
|
|
&View,
|
|
DynamicMeshPassContext);
|
|
|
|
for (int32 MeshBatchIndex = 0; MeshBatchIndex < View.VolumetricMeshBatches.Num(); ++MeshBatchIndex)
|
|
{
|
|
const FMeshBatch* Mesh = View.VolumetricMeshBatches[MeshBatchIndex].Mesh;
|
|
const FPrimitiveSceneProxy* PrimitiveSceneProxy = View.VolumetricMeshBatches[MeshBatchIndex].Proxy;
|
|
const FPrimitiveSceneInfo* PrimitiveSceneInfo = PrimitiveSceneProxy->GetPrimitiveSceneInfo();
|
|
const FBoxSphereBounds Bounds = PrimitiveSceneProxy->GetBounds();
|
|
|
|
if ((View.ViewMatrices.GetViewOrigin() - Bounds.Origin).SizeSquared() < (VolumetricFogDistance + Bounds.SphereRadius) * (VolumetricFogDistance + Bounds.SphereRadius))
|
|
{
|
|
VoxelizeVolumePrimitive(PassMeshProcessor, RHICmdList, View, VolumetricFogGridSize, GridZParams, PrimitiveSceneProxy, *Mesh);
|
|
}
|
|
}
|
|
},
|
|
|
|
// Force off instanced stereo.
|
|
// With instanced stereo on, primitives were being drawn to the left eye twice, thickening the fog more in the
|
|
// left eye. It seemed better to force off instanced stereo anyway because of cache coherency in the 3d grids,
|
|
// which are per-eye (far away in cache). The engine is already instancing across slices, which should be nearby
|
|
// in cache).
|
|
//
|
|
// It may be a tradeoff where small primitives do better with instancing (their texture lookups stay cached)
|
|
// and large ones covering lots of the voxel grid do worse (grid writes use up too much cache?), and could be
|
|
// decided based on bounds? GPUs presumably may have separate caches for read-only data in a way that changes
|
|
// this tradeoff as well.
|
|
true /*bForceInstanceStereoOff*/);
|
|
});
|
|
}
|
|
}
|