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

4234 lines
176 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "HeterogeneousVolumes.h"
#include "HeterogeneousVolumeInterface.h"
#include "LocalVertexFactory.h"
#include "MeshPassUtils.h"
#include "PixelShaderUtils.h"
#include "RayTracingDefinitions.h"
#include "RayTracingInstance.h"
#include "RayTracingInstanceBufferUtil.h"
#include "RendererPrivate.h"
#include "ScenePrivate.h"
#include "PrimitiveDrawingUtils.h"
#include "BlueNoise.h"
#include "VolumeLighting.h"
#include "VolumetricFog.h"
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesEnableFrustumVoxelGrid(
TEXT("r.HeterogeneousVolumes.FrustumGrid"),
1,
TEXT("Enables a frustum voxel grid (Default = 1)"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<float> CVarHeterogeneousVolumesFrustumGridShadingRate(
TEXT("r.HeterogeneousVolumes.FrustumGrid.ShadingRate"),
4.0,
TEXT("The voxel tessellation rate, in pixel-space (Default = 4.0)"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<float> CVarHeterogeneousVolumesFrustumGridNearPlaneDistance(
TEXT("r.HeterogeneousVolumes.FrustumGrid.NearPlaneDistance"),
1.0,
TEXT("Sets near-plane distance for the frustum grid (Default = 1.0)\n"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<float> CVarHeterogeneousVolumesFrustumGridFarPlaneDistance(
TEXT("r.HeterogeneousVolumes.FrustumGrid.FarPlaneDistance"),
-1.0,
TEXT("Sets far-plane distance for the frustum grid (Default = -1.0)\n"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesFrustumGridDepthSliceCount(
TEXT("r.HeterogeneousVolumes.FrustumGrid.DepthSliceCount"),
512,
TEXT("The number of depth slices (Default = 512)"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesFrustumGridMaxBottomLevelMemoryInMegabytes(
TEXT("r.HeterogeneousVolumes.FrustumGrid.MaxBottomLevelMemoryInMegabytes"),
128,
TEXT("The minimum voxel size (Default = 128)"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesEnableOrthoVoxelGrid(
TEXT("r.HeterogeneousVolumes.OrthoGrid"),
1,
TEXT("Enables an ortho voxel grid (Default = 1)"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<float> CVarHeterogeneousVolumesOrthoGridShadingRate(
TEXT("r.HeterogeneousVolumes.OrthoGrid.ShadingRate"),
4.0,
TEXT("The voxelization rate (Default = 4.0)"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesOrthoGridVoxelizationMode(
TEXT("r.HeterogeneousVolumes.OrthoGrid.VoxelizationMode"),
1,
TEXT("Voxelization mode (Default = 1)\n")
TEXT("0: Screen-space voxel size (legacy behavior)\n")
TEXT("1: World-space voxel size\n"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesOrthoGridMaxBottomLevelMemoryInMegabytes(
TEXT("r.HeterogeneousVolumes.OrthoGrid.MaxBottomLevelMemoryInMegabytes"),
128,
TEXT("The minimum voxel size (Default = 128)"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesMarchingMode(
TEXT("r.HeterogeneousVolumes.Debug.MarchingMode"),
1,
TEXT("The marching mode (Default = 0)\n")
TEXT("0: Ray Marching (dt=StepSize)\n")
TEXT("1: Naive DDA\n")
TEXT("2: Optimized DDA\n")
TEXT("3: Optimized DDA w/ bitmask\n"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesTessellationJitter(
TEXT("r.HeterogeneousVolumes.Tessellation.Jitter"),
1,
TEXT("Enables jittering when tessellating the acceleration grids (Default = 1)"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesBottomLevelGridResolution(
TEXT("r.HeterogeneousVolumes.Tessellation.BottomLevelGrid.Resolution"),
4,
TEXT("Determines intra-tile bottom-level grid resolution (Default = 4)"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesBottomLevelGridVoxelHashing(
TEXT("r.HeterogeneousVolumes.Tessellation.BottomLevelGrid.VoxelHashing"),
0,
TEXT("Enables bottom-level voxel hashing for deduplication (Default = 0)\n"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<float> CVarHeterogeneousVolumesMinimumVoxelSizeInFrustum(
TEXT("r.HeterogeneousVolumes.Tessellation.MinimumVoxelSizeInFrustum"),
0.1,
TEXT("The minimum voxel size (Default = 0.1)"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<float> CVarHeterogeneousVolumesMinimumVoxelSizeOutsideFrustum(
TEXT("r.HeterogeneousVolumes.Tessellation.MinimumVoxelSizeOutsideFrustum"),
100.0,
TEXT("The minimum voxel size (Default = 100.0)"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesBottomLevelGridVoxelHashingMemoryInMegabytes(
TEXT("r.HeterogeneousVolumes.Tessellation.BottomLevelGrid.VoxelHashingMemoryInMegabytes"),
64,
TEXT("Enables bottom-level voxel hashing for deduplication (Default = 64)\n"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesBottomLevelGridHomogeneousAggregation(
TEXT("r.HeterogeneousVolumes.Tessellation.BottomLevelGrid.HomogeneousAggregation"),
1,
TEXT("Enables bottom-level voxel homogeneous aggregation (Default = 1)\n"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<float> CVarHeterogeneousVolumesBottomLevelGridHomogeneousAggregationThreshold(
TEXT("r.HeterogeneousVolumes.Tessellation.BottomLevelGrid.HomogeneousAggregationThreshold"),
1.0e-3,
TEXT("Threshold for bottom-level voxel homogeneous aggregation (Default = 1.0e-3)\n"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesIndirectionGrid(
TEXT("r.HeterogeneousVolumes.Tessellation.IndirectionGrid"),
1,
TEXT("Enables lazy allocation of bottom-level memory (Default = 1)\n"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesIndirectionGridResolution(
TEXT("r.HeterogeneousVolumes.Tessellation.IndirectionGrid.Resolution"),
4,
TEXT("Determines intra-tile indirection grid resolution (Default = 4)"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesFarPlaneAutoTransition(
TEXT("r.HeterogeneousVolumes.Tessellation.FarPlaneAutoTransition"),
1,
TEXT("Enables auto transitioning of far-plane distance, based on projected minimum voxel size (Default = 1)\n"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesEnableTopLevelBitmask(
TEXT("r.HeterogeneousVolumes.Tessellation.TopLevelBitmask"),
0,
TEXT("Enables top-level bitmask to accelerate grid traversal (Default = 0)\n"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesEnableMajorantGrid(
TEXT("r.HeterogeneousVolumes.Tessellation.MajorantGrid"),
1,
TEXT("Enables building majorant grids to accelerate volume tracking (Default = 0)\n"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesEnableMajorantGridMax(
TEXT("r.HeterogeneousVolumes.Tessellation.MajorantGrid.Max"),
0,
TEXT("Enables building majorant grids to accelerate volume tracking (Default = 0)\n"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesShadowCameraDownsampleFactor(
TEXT("r.HeterogeneousVolumes.Shadows.CameraDownsampleFactor"),
2,
TEXT("Controls downsample factor for camera volumetric shadow map (default = 2)"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesShadowResolution(
TEXT("r.HeterogeneousVolumes.Shadows.Resolution"),
512,
TEXT("Resolution when building volumetric shadow map (Default = 512)\n"),
ECVF_Scalability | ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<float> CVarHeterogeneousVolumesStepSizeForShadows(
TEXT("r.HeterogeneousVolumes.Shadows.StepSize"),
2.0,
TEXT("Ray marching step size when building volumetric shadow map (Default = 2.0)\n"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesShadowMaxSampleCount(
TEXT("r.HeterogeneousVolumes.Shadows.MaxSampleCount"),
8,
TEXT("Maximum sample count when building volumetric shadow map (Default = 8)\n"),
ECVF_Scalability | ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<float> CVarHeterogeneousVolumesShadowAbsoluteErrorThreshold(
TEXT("r.HeterogeneousVolumes.Shadows.AbsoluteErrorThreshold"),
0.0,
TEXT("Absolute error threshold for volume shadow compression (Default = 0.0)\n"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<float> CVarHeterogeneousVolumesShadowRelativeErrorThreshold(
TEXT("r.HeterogeneousVolumes.Shadows.RelativeErrorThreshold"),
0.05,
TEXT("Relative error threshold for volume shadow compression (Default = 0.05)\n"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesShadowUseAVSMCompression(
TEXT("r.HeterogeneousVolumes.Shadows.UseAVSMCompression"),
1,
TEXT("Enables AVSM compression (Default = 1)\n"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesShadowDebugTweak(
TEXT("r.HeterogeneousVolumes.Shadows.DebugTweak"),
0,
TEXT("Debug tweak value (Default = 0)\n"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<float> CVarHeterogeneousVolumesShadowShadingRate(
TEXT("r.HeterogeneousVolumes.Shadows.ShadingRate"),
1.0f,
TEXT("Shading rate (in-frustum) for computing shadows (Default = 1.0)\n"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesShadowOutOfFrustumShadingRate(
TEXT("r.HeterogeneousVolumes.Shadows.OutOfFrustumShadingRate"),
2,
TEXT("Shading rate (out-of-frustum) for computing shadows (Default = 2.0)\n"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesShadowJitter(
TEXT("r.HeterogeneousVolumes.Shadows.Jitter"),
0,
TEXT("Enables jittering when constructing shadows (Default = 0)\n"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<bool> CVarHeterogeneousShadowsForLightTypeDirectional(
TEXT("r.HeterogeneousVolumes.Shadows.LightType.Directional"),
1,
TEXT("Enables shadows from the directional light (Default = 1)"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<bool> CVarHeterogeneousShadowsForLightTypePoint(
TEXT("r.HeterogeneousVolumes.Shadows.LightType.Point"),
1,
TEXT("Enables shadows from point lights (Default = 1)"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<bool> CVarHeterogeneousShadowsForLightTypeSpot(
TEXT("r.HeterogeneousVolumes.Shadows.LightType.Spot"),
1,
TEXT("Enables shadows from spot lights (Default = 1)"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<bool> CVarHeterogeneousShadowsForLightTypeRect(
TEXT("r.HeterogeneousVolumes.Shadows.LightType.Rect"),
1,
TEXT("Enables shadows from rect lights (Default = 1)"),
ECVF_RenderThreadSafe
);
IMPLEMENT_UNIFORM_BUFFER_STRUCT(FOrthoVoxelGridUniformBufferParameters, "OrthoGridUniformBuffer");
IMPLEMENT_UNIFORM_BUFFER_STRUCT(FFrustumVoxelGridUniformBufferParameters, "FrustumGridUniformBuffer");
IMPLEMENT_UNIFORM_BUFFER_STRUCT(FAdaptiveVolumetricShadowMapUniformBufferParameters, "AVSM");
struct FRasterTileData
{
uint32 TopLevelGridLinearIndex;
uint32 BottomLevelGridLinearOffset;
};
namespace HeterogeneousVolumes
{
bool EnableFrustumVoxelGrid()
{
return CVarHeterogeneousVolumesEnableFrustumVoxelGrid.GetValueOnRenderThread() != 0;
}
float GetShadingRateForFrustumGrid()
{
return FMath::Max(CVarHeterogeneousVolumesFrustumGridShadingRate.GetValueOnRenderThread(), 0.1);
}
float GetNearPlaneDistanceForFrustumGrid()
{
return CVarHeterogeneousVolumesFrustumGridNearPlaneDistance.GetValueOnRenderThread();
}
float GetFarPlaneDistanceForFrustumGrid()
{
return CVarHeterogeneousVolumesFrustumGridFarPlaneDistance.GetValueOnRenderThread();
}
int32 GetDepthSliceCountForFrustumGrid()
{
return FMath::Max(CVarHeterogeneousVolumesFrustumGridDepthSliceCount.GetValueOnRenderThread(), 1);
}
int32 GetMaxBottomLevelMemoryInMegabytesForFrustumGrid()
{
return FMath::Max(CVarHeterogeneousVolumesFrustumGridMaxBottomLevelMemoryInMegabytes.GetValueOnRenderThread(), 1);
}
bool EnableOrthoVoxelGrid()
{
return CVarHeterogeneousVolumesEnableOrthoVoxelGrid.GetValueOnRenderThread() != 0;
}
float GetShadingRateForOrthoGrid()
{
return FMath::Max(CVarHeterogeneousVolumesOrthoGridShadingRate.GetValueOnRenderThread(), 0.1);
}
int32 GetMaxBottomLevelMemoryInMegabytesForOrthoGrid()
{
return FMath::Max(CVarHeterogeneousVolumesOrthoGridMaxBottomLevelMemoryInMegabytes.GetValueOnRenderThread(), 1);
}
bool EnableFarPlaneAutoTransition()
{
return CVarHeterogeneousVolumesFarPlaneAutoTransition.GetValueOnRenderThread() != 0;
}
float GetMinimumVoxelSizeInFrustum()
{
return FMath::Max(CVarHeterogeneousVolumesMinimumVoxelSizeInFrustum.GetValueOnRenderThread(), 0.01);
}
float GetMinimumVoxelSizeOutsideFrustum()
{
return FMath::Max(CVarHeterogeneousVolumesMinimumVoxelSizeOutsideFrustum.GetValueOnRenderThread(), 0.01);
}
int32 GetMarchingMode()
{
return FMath::Clamp(CVarHeterogeneousVolumesMarchingMode.GetValueOnRenderThread(), 0, 3);
}
bool EnableVoxelHashing()
{
//return CVarHeterogeneousVolumesBottomLevelGridVoxelHashing.GetValueOnRenderThread() != 0;
return false;
}
uint32 GetVoxelHashingMemoryInMegabytes()
{
return CVarHeterogeneousVolumesBottomLevelGridVoxelHashingMemoryInMegabytes.GetValueOnRenderThread();
}
bool EnableHomogeneousAggregation()
{
return CVarHeterogeneousVolumesBottomLevelGridHomogeneousAggregation.GetValueOnRenderThread() != 0;
}
float GetHomogeneousAggregationThreshold()
{
return CVarHeterogeneousVolumesBottomLevelGridHomogeneousAggregationThreshold.GetValueOnRenderThread();
}
bool EnableIndirectionGrid()
{
return CVarHeterogeneousVolumesIndirectionGrid.GetValueOnRenderThread() != 0;
}
bool EnableJitter()
{
return CVarHeterogeneousVolumesTessellationJitter.GetValueOnRenderThread() != 0;
}
int32 GetBottomLevelGridResolution()
{
return FMath::Clamp(CVarHeterogeneousVolumesBottomLevelGridResolution.GetValueOnRenderThread(), 1, 4);
}
int32 GetIndirectionGridResolution()
{
return FMath::Clamp(CVarHeterogeneousVolumesIndirectionGridResolution.GetValueOnRenderThread(), 1, 4);
}
bool EnableLinearInterpolation()
{
return false;
}
bool EnableTopLevelBitmask()
{
return CVarHeterogeneousVolumesEnableTopLevelBitmask.GetValueOnRenderThread() != 0;
}
bool EnableMajorantGrid()
{
return CVarHeterogeneousVolumesEnableMajorantGrid.GetValueOnRenderThread() != 0;
}
float CalcTanHalfFOV(float FOVInDegrees)
{
return FMath::Tan(FMath::DegreesToRadians(FOVInDegrees * 0.5));
}
FIntPoint GetShadowMapResolution()
{
return FIntPoint(FMath::Clamp(CVarHeterogeneousVolumesShadowResolution.GetValueOnRenderThread(), 1, 2048));
}
float GetStepSizeForShadows()
{
return FMath::Max(CVarHeterogeneousVolumesStepSizeForShadows.GetValueOnRenderThread(), 0.01f);
}
uint32 GetShadowMaxSampleCount()
{
return FMath::Clamp(CVarHeterogeneousVolumesShadowMaxSampleCount.GetValueOnRenderThread(), 2, 64);
}
float GetShadowAbsoluteErrorThreshold()
{
return FMath::Max(CVarHeterogeneousVolumesShadowAbsoluteErrorThreshold.GetValueOnRenderThread(), 0.0);
}
float GetShadowRelativeErrorThreshold()
{
return FMath::Max(CVarHeterogeneousVolumesShadowRelativeErrorThreshold.GetValueOnRenderThread(), 0.0);
}
bool UseAVSMCompression()
{
return CVarHeterogeneousVolumesShadowUseAVSMCompression.GetValueOnRenderThread() != 0;
}
float GetCameraDownsampleFactor()
{
return FMath::Max(CVarHeterogeneousVolumesShadowCameraDownsampleFactor.GetValueOnRenderThread(), 1);
}
float GetShadingRateForShadows()
{
return FMath::Max(CVarHeterogeneousVolumesShadowShadingRate.GetValueOnRenderThread(), 0.1);
}
float GetOutOfFrustumShadingRateForShadows()
{
return FMath::Max(CVarHeterogeneousVolumesShadowOutOfFrustumShadingRate.GetValueOnRenderThread(), 0.1);
}
bool EnableJitterForShadows()
{
return CVarHeterogeneousVolumesShadowJitter.GetValueOnRenderThread() != 0;
}
bool SupportsShadowForLightType(uint32 LightType)
{
if (SupportsLightType(LightType))
{
switch (LightType)
{
case LightType_Directional:
return CVarHeterogeneousShadowsForLightTypeDirectional.GetValueOnRenderThread();
case LightType_Point:
return CVarHeterogeneousShadowsForLightTypePoint.GetValueOnRenderThread();
case LightType_Spot:
return CVarHeterogeneousShadowsForLightTypeSpot.GetValueOnRenderThread();
case LightType_Rect:
return CVarHeterogeneousShadowsForLightTypeRect.GetValueOnRenderThread();
}
}
return false;
}
bool IsDynamicShadow(const FVisibleLightInfo* VisibleLightInfo)
{
check(VisibleLightInfo != nullptr);
if (VisibleLightInfo)
{
return !VisibleLightInfo->ShadowsToProject.IsEmpty();
}
return false;
}
const FProjectedShadowInfo* GetProjectedShadowInfo(const FVisibleLightInfo* VisibleLightInfo, int32 ShadowIndex)
{
check(VisibleLightInfo != nullptr);
if (VisibleLightInfo && ShadowIndex < VisibleLightInfo->ShadowsToProject.Num())
{
return VisibleLightInfo->ShadowsToProject[ShadowIndex];
}
return nullptr;
}
}
uint32 GetTypeHash(const FVolumetricMeshBatch& MeshBatch)
{
return HashCombineFast(GetTypeHash(MeshBatch.Mesh), GetTypeHash(MeshBatch.Proxy));
}
class FMarkTopLevelGridVoxelsForFrustumGrid : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FMarkTopLevelGridVoxelsForFrustumGrid);
SHADER_USE_PARAMETER_STRUCT(FMarkTopLevelGridVoxelsForFrustumGrid, FGlobalShader);
using FPermutationDomain = TShaderPermutationDomain<>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER(FIntVector, TopLevelGridResolution)
SHADER_PARAMETER(FVector3f, PrimitiveWorldBoundsMin)
SHADER_PARAMETER(FVector3f, PrimitiveWorldBoundsMax)
SHADER_PARAMETER(FMatrix44f, ViewToWorld)
SHADER_PARAMETER(float, TanHalfFOV)
SHADER_PARAMETER(float, NearPlaneDepth)
SHADER_PARAMETER(float, FarPlaneDepth)
SHADER_PARAMETER(FIntVector, VoxelDimensions)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FTopLevelGridData>, RWTopLevelGridBuffer)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(
const FGlobalShaderPermutationParameters& Parameters
)
{
return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform);
}
static void ModifyCompilationEnvironment(
const FGlobalShaderPermutationParameters& Parameters,
FShaderCompilerEnvironment& OutEnvironment
)
{
FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_1D"), GetThreadGroupSize1D());
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_3D"), GetThreadGroupSize3D());
}
static int32 GetThreadGroupSize1D() { return GetThreadGroupSize3D() * GetThreadGroupSize3D() * GetThreadGroupSize3D(); }
static int32 GetThreadGroupSize3D() { return 4; }
};
IMPLEMENT_GLOBAL_SHADER(FMarkTopLevelGridVoxelsForFrustumGrid, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesFrustumVoxelGrid.usf", "MarkTopLevelGridVoxelsForFrustumGridCS", SF_Compute);
class FRasterizeBottomLevelFrustumGridCS : public FMeshMaterialShader
{
DECLARE_SHADER_TYPE(FRasterizeBottomLevelFrustumGridCS, MeshMaterial);
using FPermutationDomain = TShaderPermutationDomain<>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
// Scene data
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneUniformParameters, Scene)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<float4>, EyeAdaptationBuffer)
// Primitive data
SHADER_PARAMETER(FVector3f, PrimitiveWorldBoundsMin)
SHADER_PARAMETER(FVector3f, PrimitiveWorldBoundsMax)
SHADER_PARAMETER(FMatrix44f, LocalToWorld)
SHADER_PARAMETER(FMatrix44f, WorldToLocal)
SHADER_PARAMETER(FVector3f, LocalBoundsOrigin)
SHADER_PARAMETER(FVector3f, LocalBoundsExtent)
SHADER_PARAMETER(int32, PrimitiveId)
// Volume data
SHADER_PARAMETER(FIntVector, TopLevelGridResolution)
SHADER_PARAMETER(FIntVector, VoxelDimensions)
SHADER_PARAMETER(FMatrix44f, ViewToWorld)
SHADER_PARAMETER(float, TanHalfFOV)
SHADER_PARAMETER(float, NearPlaneDepth)
SHADER_PARAMETER(float, FarPlaneDepth)
SHADER_PARAMETER(int, BottomLevelGridBufferSize)
// Sampling data
SHADER_PARAMETER(int, bJitter)
SHADER_PARAMETER_STRUCT_REF(FBlueNoise, BlueNoise)
// Raster tile data
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<uint>, RasterTileAllocatorBuffer)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<FRasterTileData>, RasterTileBuffer)
// Indirect args
RDG_BUFFER_ACCESS(IndirectArgs, ERHIAccess::IndirectArgs)
// Grid data
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FTopLevelGridData>, RWTopLevelGridBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, RWBottomLevelGridAllocatorBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FScalarGridData>, RWExtinctionGridBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FVectorGridData>, RWEmissionGridBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FVectorGridData>, RWScatteringGridBuffer)
END_SHADER_PARAMETER_STRUCT()
FRasterizeBottomLevelFrustumGridCS() = default;
FRasterizeBottomLevelFrustumGridCS(
const FMeshMaterialShaderType::CompiledShaderInitializerType& Initializer
)
: FMeshMaterialShader(Initializer)
{
Bindings.BindForLegacyShaderParameters(
this,
Initializer.PermutationId,
Initializer.ParameterMap,
*FParameters::FTypeInfo::GetStructMetadata(),
// Don't require full bindings, we use FMaterialShader::SetParameters
false
);
}
static bool ShouldCompilePermutation(
const FMaterialShaderPermutationParameters& Parameters
)
{
return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform)
&& DoesMaterialShaderSupportHeterogeneousVolumes(Parameters.MaterialParameters);
}
static FPermutationDomain RemapPermutation(FPermutationDomain PermutationVector)
{
return PermutationVector;
}
static void ModifyCompilationEnvironment(
const FMaterialShaderPermutationParameters& Parameters,
FShaderCompilerEnvironment& OutEnvironment
)
{
FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_1D"), GetThreadGroupSize1D());
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_3D"), GetThreadGroupSize3D());
// This shader takes a very long time to compile with FXC, so we pre-compile it with DXC first and then forward the optimized HLSL to FXC.
OutEnvironment.CompilerFlags.Add(CFLAG_PrecompileWithDXC);
OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads);
OutEnvironment.SetDefine(TEXT("GET_PRIMITIVE_DATA_OVERRIDE"), 1);
}
void SetParameters(
FRHIComputeCommandList& RHICmdList,
FRHIComputeShader* ShaderRHI,
const FViewInfo& View,
const FMaterialRenderProxy* MaterialProxy,
const FMaterial& Material
)
{
FMaterialShader::SetViewParameters(RHICmdList, ShaderRHI, View, View.ViewUniformBuffer);
FMaterialShader::SetParameters(RHICmdList, ShaderRHI, MaterialProxy, Material, View);
}
static int32 GetThreadGroupSize1D() { return GetThreadGroupSize3D() * GetThreadGroupSize3D() * GetThreadGroupSize3D(); }
static int32 GetThreadGroupSize3D() { return 4; }
};
IMPLEMENT_MATERIAL_SHADER_TYPE(, FRasterizeBottomLevelFrustumGridCS, TEXT("/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesMaterialBakingPipeline.usf"), TEXT("RasterizeBottomLevelFrustumGridCS"), SF_Compute);
class FTopLevelGridCalculateVoxelSize : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FTopLevelGridCalculateVoxelSize);
SHADER_USE_PARAMETER_STRUCT(FTopLevelGridCalculateVoxelSize, FGlobalShader);
using FPermutationDomain = TShaderPermutationDomain<>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER(FIntVector, TopLevelGridResolution)
SHADER_PARAMETER(FVector3f, TopLevelGridWorldBoundsMin)
SHADER_PARAMETER(FVector3f, TopLevelGridWorldBoundsMax)
SHADER_PARAMETER(FVector3f, PrimitiveWorldBoundsMin)
SHADER_PARAMETER(FVector3f, PrimitiveWorldBoundsMax)
SHADER_PARAMETER(float, ShadingRateInFrustum)
SHADER_PARAMETER(float, ShadingRateOutOfFrustum)
SHADER_PARAMETER(float, MinVoxelSizeInFrustum)
SHADER_PARAMETER(float, MinVoxelSizeOutOfFrustum)
SHADER_PARAMETER(float, DownsampleFactor)
SHADER_PARAMETER(int, bUseProjectedPixelSize)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FTopLevelGridData>, RWTopLevelGridBuffer)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(
const FGlobalShaderPermutationParameters& Parameters
)
{
return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform);
}
static void ModifyCompilationEnvironment(
const FGlobalShaderPermutationParameters& Parameters,
FShaderCompilerEnvironment& OutEnvironment
)
{
FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_1D"), GetThreadGroupSize1D());
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_3D"), GetThreadGroupSize3D());
}
static int32 GetThreadGroupSize1D() { return GetThreadGroupSize3D() * GetThreadGroupSize3D() * GetThreadGroupSize3D(); }
static int32 GetThreadGroupSize3D() { return 4; }
};
IMPLEMENT_GLOBAL_SHADER(FTopLevelGridCalculateVoxelSize, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesVoxelGridPipeline.usf", "TopLevelGridCalculateVoxelSize", SF_Compute);
class FAllocateBottomLevelGrid : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FAllocateBottomLevelGrid);
SHADER_USE_PARAMETER_STRUCT(FAllocateBottomLevelGrid, FGlobalShader);
class FEnableIndirectionGrid : SHADER_PERMUTATION_BOOL("DIM_ENABLE_INDIRECTION_GRID");
using FPermutationDomain = TShaderPermutationDomain<FEnableIndirectionGrid>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER(FIntVector, TopLevelGridResolution)
SHADER_PARAMETER(FVector3f, TopLevelGridWorldBoundsMin)
SHADER_PARAMETER(FVector3f, TopLevelGridWorldBoundsMax)
SHADER_PARAMETER(int, MaxVoxelResolution)
SHADER_PARAMETER(int, bSampleAtVertices)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FTopLevelGridData>, RWTopLevelGridBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, RWBottomLevelGridAllocatorBuffer)
// Indirection Grid
SHADER_PARAMETER(int, IndirectionGridBufferSize)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FTopLevelGridData>, RWIndirectionGridBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, RWIndirectionGridAllocatorBuffer)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(
const FGlobalShaderPermutationParameters& Parameters
)
{
return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform);
}
static void ModifyCompilationEnvironment(
const FGlobalShaderPermutationParameters& Parameters,
FShaderCompilerEnvironment& OutEnvironment
)
{
FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_1D"), GetThreadGroupSize1D());
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_3D"), GetThreadGroupSize3D());
}
static int32 GetThreadGroupSize1D() { return GetThreadGroupSize3D() * GetThreadGroupSize3D() * GetThreadGroupSize3D(); }
static int32 GetThreadGroupSize3D() { return 4; }
};
IMPLEMENT_GLOBAL_SHADER(FAllocateBottomLevelGrid, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesVoxelGridPipeline.usf", "AllocateBottomLevelGrid", SF_Compute);
class FGenerateRasterTiles : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FGenerateRasterTiles);
SHADER_USE_PARAMETER_STRUCT(FGenerateRasterTiles, FGlobalShader);
class FEnableIndirectionGrid : SHADER_PERMUTATION_BOOL("DIM_ENABLE_INDIRECTION_GRID");
using FPermutationDomain = TShaderPermutationDomain<FEnableIndirectionGrid>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
//SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER(FIntVector, TopLevelGridResolution)
SHADER_PARAMETER(int, RasterTileVoxelResolution)
SHADER_PARAMETER(int, MaxNumRasterTiles)
//SHADER_PARAMETER(FVector3f, TopLevelGridWorldBoundsMin)
//SHADER_PARAMETER(FVector3f, TopLevelGridWorldBoundsMax)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<FTopLevelGridData>, TopLevelGridBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, RWRasterTileAllocatorBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FRasterTileData>, RWRasterTileBuffer)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(
const FGlobalShaderPermutationParameters& Parameters
)
{
return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform);
}
static void ModifyCompilationEnvironment(
const FGlobalShaderPermutationParameters& Parameters,
FShaderCompilerEnvironment& OutEnvironment
)
{
FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_1D"), GetThreadGroupSize1D());
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_3D"), GetThreadGroupSize3D());
}
static int32 GetThreadGroupSize1D() { return GetThreadGroupSize3D() * GetThreadGroupSize3D() * GetThreadGroupSize3D(); }
static int32 GetThreadGroupSize3D() { return 4; }
};
IMPLEMENT_GLOBAL_SHADER(FGenerateRasterTiles, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesVoxelGridPipeline.usf", "GenerateRasterTiles", SF_Compute);
class FSetRasterizeBottomLevelGridIndirectArgs : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FSetRasterizeBottomLevelGridIndirectArgs);
SHADER_USE_PARAMETER_STRUCT(FSetRasterizeBottomLevelGridIndirectArgs, FGlobalShader);
using FPermutationDomain = TShaderPermutationDomain<>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER(FIntVector, MaxDispatchThreadGroupsPerDimension)
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<uint>, RasterTileAllocatorBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, RWRasterizeBottomLevelGridIndirectArgsBuffer)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(
const FGlobalShaderPermutationParameters& Parameters
)
{
return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform);
}
static void ModifyCompilationEnvironment(
const FGlobalShaderPermutationParameters& Parameters,
FShaderCompilerEnvironment& OutEnvironment
)
{
FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_1D"), GetThreadGroupSize1D());
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_3D"), GetThreadGroupSize3D());
}
static int32 GetThreadGroupSize1D() { return GetThreadGroupSize3D() * GetThreadGroupSize3D() * GetThreadGroupSize3D(); }
static int32 GetThreadGroupSize3D() { return 4; }
};
IMPLEMENT_GLOBAL_SHADER(FSetRasterizeBottomLevelGridIndirectArgs, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesVoxelGridPipeline.usf", "SetRasterizeBottomLevelGridIndirectArgs", SF_Compute);
class FRasterizeBottomLevelOrthoGridCS : public FMeshMaterialShader
{
DECLARE_SHADER_TYPE(FRasterizeBottomLevelOrthoGridCS, MeshMaterial);
class FEnableIndirectionGrid : SHADER_PERMUTATION_BOOL("DIM_ENABLE_INDIRECTION_GRID");
//class FEnableVoxelHashing : SHADER_PERMUTATION_BOOL("DIM_ENABLE_VOXEL_HASHING");
class FEnableHomogeneousAggregation : SHADER_PERMUTATION_BOOL("DIM_ENABLE_HOMOGENEOUS_AGGREGATION");
using FPermutationDomain = TShaderPermutationDomain<FEnableIndirectionGrid, FEnableHomogeneousAggregation>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
// Scene data
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneUniformParameters, Scene)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<float4>, EyeAdaptationBuffer)
// Primitive data
SHADER_PARAMETER(FVector3f, PrimitiveWorldBoundsMin)
SHADER_PARAMETER(FVector3f, PrimitiveWorldBoundsMax)
SHADER_PARAMETER(FMatrix44f, LocalToWorld)
SHADER_PARAMETER(FMatrix44f, WorldToLocal)
SHADER_PARAMETER(FVector3f, LocalBoundsOrigin)
SHADER_PARAMETER(FVector3f, LocalBoundsExtent)
SHADER_PARAMETER(int32, PrimitiveId)
// Volume data
SHADER_PARAMETER(FIntVector, TopLevelGridResolution)
SHADER_PARAMETER(FVector3f, TopLevelGridWorldBoundsMin)
SHADER_PARAMETER(FVector3f, TopLevelGridWorldBoundsMax)
SHADER_PARAMETER(int, BottomLevelGridBufferSize)
// Sampling data
SHADER_PARAMETER_STRUCT_REF(FBlueNoise, BlueNoise)
// Volume sample mode
SHADER_PARAMETER(int, bJitter)
SHADER_PARAMETER(int, bSampleAtVertices)
// Raster tile data
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<uint>, RasterTileAllocatorBuffer)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<FRasterTileData>, RasterTileBuffer)
// Indirect args
RDG_BUFFER_ACCESS(IndirectArgs, ERHIAccess::IndirectArgs)
// Grid data
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<FTopLevelGridData>, TopLevelGridBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, RWBottomLevelGridAllocatorBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FTopLevelGridData>, RWTopLevelGridBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FScalarGridData>, RWExtinctionGridBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FVectorGridData>, RWEmissionGridBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FVectorGridData>, RWScatteringGridBuffer)
// Indirection Grid
SHADER_PARAMETER(int, IndirectionGridBufferSize)
SHADER_PARAMETER(int, FixedBottomLevelResolution)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FTopLevelGridData>, RWIndirectionGridBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, RWIndirectionGridAllocatorBuffer)
// Hash Table
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, RWHashTable)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, RWHashToVoxelBuffer)
SHADER_PARAMETER(int, HashTableSize)
// Homogeneous aggregation
SHADER_PARAMETER(float, HomogeneousThreshold)
END_SHADER_PARAMETER_STRUCT()
FRasterizeBottomLevelOrthoGridCS() = default;
FRasterizeBottomLevelOrthoGridCS(
const FMeshMaterialShaderType::CompiledShaderInitializerType & Initializer
)
: FMeshMaterialShader(Initializer)
{
Bindings.BindForLegacyShaderParameters(
this,
Initializer.PermutationId,
Initializer.ParameterMap,
*FParameters::FTypeInfo::GetStructMetadata(),
// Don't require full bindings, we use FMaterialShader::SetParameters
false
);
}
static bool ShouldCompilePermutation(
const FMaterialShaderPermutationParameters& Parameters
)
{
return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform)
&& DoesMaterialShaderSupportHeterogeneousVolumes(Parameters.MaterialParameters);
}
static FPermutationDomain RemapPermutation(FPermutationDomain PermutationVector)
{
return PermutationVector;
}
static void ModifyCompilationEnvironment(
const FMaterialShaderPermutationParameters& Parameters,
FShaderCompilerEnvironment& OutEnvironment
)
{
FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_1D"), GetThreadGroupSize1D());
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_3D"), GetThreadGroupSize3D());
// This shader takes a very long time to compile with FXC, so we pre-compile it with DXC first and then forward the optimized HLSL to FXC.
OutEnvironment.CompilerFlags.Add(CFLAG_PrecompileWithDXC);
OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads);
OutEnvironment.SetDefine(TEXT("GET_PRIMITIVE_DATA_OVERRIDE"), 1);
}
void SetParameters(
FRHIComputeCommandList& RHICmdList,
FRHIComputeShader* ShaderRHI,
const FViewInfo& View,
const FMaterialRenderProxy* MaterialProxy,
const FMaterial& Material
)
{
FMaterialShader::SetViewParameters(RHICmdList, ShaderRHI, View, View.ViewUniformBuffer);
FMaterialShader::SetParameters(RHICmdList, ShaderRHI, MaterialProxy, Material, View);
}
static int32 GetThreadGroupSize1D() { return GetThreadGroupSize3D() * GetThreadGroupSize3D() * GetThreadGroupSize3D(); }
static int32 GetThreadGroupSize3D() { return 4; }
};
IMPLEMENT_MATERIAL_SHADER_TYPE(, FRasterizeBottomLevelOrthoGridCS, TEXT("/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesMaterialBakingPipeline.usf"), TEXT("RasterizeBottomLevelOrthoGridCS"), SF_Compute);
class FTopLevelCreateBitmaskCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FTopLevelCreateBitmaskCS);
SHADER_USE_PARAMETER_STRUCT(FTopLevelCreateBitmaskCS, FGlobalShader);
using FPermutationDomain = TShaderPermutationDomain<>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER(FIntVector, TopLevelGridResolution)
// Grid data
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<FTopLevelGridData>, TopLevelGridBuffer)
// Output
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FTopLevelGridBitmaskData>, RWTopLevelGridBitmaskBuffer)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(
const FGlobalShaderPermutationParameters& Parameters
)
{
return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform);
}
static void ModifyCompilationEnvironment(
const FGlobalShaderPermutationParameters& Parameters,
FShaderCompilerEnvironment& OutEnvironment
)
{
FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_1D"), GetThreadGroupSize1D());
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_3D"), GetThreadGroupSize3D());
}
static int32 GetThreadGroupSize1D() { return GetThreadGroupSize3D() * GetThreadGroupSize3D() * GetThreadGroupSize3D(); }
static int32 GetThreadGroupSize3D() { return 4; }
};
IMPLEMENT_GLOBAL_SHADER(FTopLevelCreateBitmaskCS, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesVoxelGridPipeline.usf", "TopLevelCreateBitmaskCS", SF_Compute);
void CreateTopLevelBitmask(
FRDGBuilder& GraphBuilder,
TArray<FViewInfo>& Views,
FIntVector TopLevelGridResolution,
FRDGBufferRef TopLevelGridBuffer,
FRDGBufferRef& TopLevelGridBitmaskBuffer
)
{
if (!HeterogeneousVolumes::EnableTopLevelBitmask())
{
TopLevelGridBitmaskBuffer = GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FTopLevelGridBitmaskData));
}
else
{
FIntVector BitmaskGridResolution = FIntVector(
FMath::DivideAndRoundUp(TopLevelGridResolution.X, 4),
FMath::DivideAndRoundUp(TopLevelGridResolution.Y, 4),
FMath::DivideAndRoundUp(TopLevelGridResolution.Z, 4)
);
uint32 TopLevelGridBitmaskBufferSize = BitmaskGridResolution.X * BitmaskGridResolution.Y * BitmaskGridResolution.Z;
TopLevelGridBitmaskBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(FTopLevelGridBitmaskData), TopLevelGridBitmaskBufferSize),
TEXT("HeterogeneousVolumes.BottomLevelGridBuffer")
);
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(TopLevelGridBitmaskBuffer), 0x0);
FTopLevelCreateBitmaskCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FTopLevelCreateBitmaskCS::FParameters>();
{
PassParameters->TopLevelGridResolution = TopLevelGridResolution;
PassParameters->TopLevelGridBuffer = GraphBuilder.CreateSRV(TopLevelGridBuffer);
PassParameters->RWTopLevelGridBitmaskBuffer = GraphBuilder.CreateUAV(TopLevelGridBitmaskBuffer);
}
//FTopLevelCreateBitmaskCS::FPermutationDomain PermutationVector;
const FGlobalShaderMap* GlobalShaderMap = Views[0].ShaderMap;
TShaderRef<FTopLevelCreateBitmaskCS> ComputeShader = GlobalShaderMap->GetShader<FTopLevelCreateBitmaskCS>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("RenderTransmittanceTopLevelGridCS"),
ERDGPassFlags::Compute | ERDGPassFlags::NeverCull,
ComputeShader,
PassParameters,
BitmaskGridResolution
);
}
}
class FBuildMajorantVoxelGridCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FBuildMajorantVoxelGridCS);
SHADER_USE_PARAMETER_STRUCT(FBuildMajorantVoxelGridCS, FGlobalShader);
class FEnableIndirectionGrid : SHADER_PERMUTATION_BOOL("DIM_ENABLE_INDIRECTION_GRID");
using FPermutationDomain = TShaderPermutationDomain<FEnableIndirectionGrid>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER(FIntVector, TopLevelGridResolution)
// Grid data
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<FTopLevelGridData>, TopLevelGridBuffer)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<FTopLevelGridData>, IndirectionGridBuffer)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<FScalarGridData>, ExtinctionGridBuffer)
// Output
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FScalarGridData>, RWMajorantVoxelGridBuffer)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(
const FGlobalShaderPermutationParameters& Parameters
)
{
return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform);
}
static void ModifyCompilationEnvironment(
const FGlobalShaderPermutationParameters& Parameters,
FShaderCompilerEnvironment& OutEnvironment
)
{
FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_1D"), GetThreadGroupSize1D());
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_3D"), GetThreadGroupSize3D());
}
static int32 GetThreadGroupSize1D() { return GetThreadGroupSize3D() * GetThreadGroupSize3D() * GetThreadGroupSize3D(); }
static int32 GetThreadGroupSize3D() { return 4; }
};
IMPLEMENT_GLOBAL_SHADER(FBuildMajorantVoxelGridCS, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesVoxelGridPipeline.usf", "BuildMajorantVoxelGridCS", SF_Compute);
class FDownsampleMajorantVoxelGridCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FDownsampleMajorantVoxelGridCS);
SHADER_USE_PARAMETER_STRUCT(FDownsampleMajorantVoxelGridCS, FGlobalShader);
using FPermutationDomain = TShaderPermutationDomain<>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER(FIntVector, InputDimensions)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<FScalarGridData>, InputBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FScalarGridData>, RWOutputBuffer)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(
const FGlobalShaderPermutationParameters& Parameters
)
{
return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform);
}
static void ModifyCompilationEnvironment(
const FGlobalShaderPermutationParameters& Parameters,
FShaderCompilerEnvironment& OutEnvironment
)
{
FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_1D"), GetThreadGroupSize1D());
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_3D"), GetThreadGroupSize3D());
}
static int32 GetThreadGroupSize1D() { return GetThreadGroupSize3D() * GetThreadGroupSize3D() * GetThreadGroupSize3D(); }
static int32 GetThreadGroupSize3D() { return 4; }
};
IMPLEMENT_GLOBAL_SHADER(FDownsampleMajorantVoxelGridCS, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesVoxelGridPipeline.usf", "DownsampleMajorantVoxelGridCS", SF_Compute);
class FCopyMaxIntoMajorantVoxelGridCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FCopyMaxIntoMajorantVoxelGridCS);
SHADER_USE_PARAMETER_STRUCT(FCopyMaxIntoMajorantVoxelGridCS, FGlobalShader);
using FPermutationDomain = TShaderPermutationDomain<>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<FScalarGridData>, InputBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FScalarGridData>, RWMajorantVoxelGridBuffer)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(
const FGlobalShaderPermutationParameters& Parameters
)
{
return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform);
}
static void ModifyCompilationEnvironment(
const FGlobalShaderPermutationParameters& Parameters,
FShaderCompilerEnvironment& OutEnvironment
)
{
FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_1D"), GetThreadGroupSize1D());
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_3D"), GetThreadGroupSize3D());
}
static int32 GetThreadGroupSize1D() { return GetThreadGroupSize3D() * GetThreadGroupSize3D() * GetThreadGroupSize3D(); }
static int32 GetThreadGroupSize3D() { return 4; }
};
IMPLEMENT_GLOBAL_SHADER(FCopyMaxIntoMajorantVoxelGridCS, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesVoxelGridPipeline.usf", "CopyMaximumIntoMajorantVoxelGridCS", SF_Compute);
void BuildMajorantVoxelGrid(
FRDGBuilder& GraphBuilder,
const FScene* Scene,
const FIntVector& TopLevelGridResolution,
FRDGBufferRef TopLevelGridBuffer,
FRDGBufferRef IndirectionGridBuffer,
FRDGBufferRef ExtinctionGridBuffer,
FRDGBufferRef& MajorantVoxelGridBuffer
)
{
if (!HeterogeneousVolumes::EnableMajorantGrid())
{
MajorantVoxelGridBuffer = GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FScalarGridData));
}
else
{
uint32 MajorantVoxelGridBufferSize = TopLevelGridResolution.X * TopLevelGridResolution.Y * TopLevelGridResolution.Z;
MajorantVoxelGridBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(FScalarGridData), MajorantVoxelGridBufferSize),
TEXT("HeterogeneousVolumes.MajorantVoxelGridBuffer")
);
const FGlobalShaderMap* GlobalShaderMap = GetGlobalShaderMap(Scene->GetFeatureLevel());
// Build Majorant Grid
{
FBuildMajorantVoxelGridCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FBuildMajorantVoxelGridCS::FParameters>();
{
PassParameters->TopLevelGridResolution = TopLevelGridResolution;
PassParameters->TopLevelGridBuffer = GraphBuilder.CreateSRV(TopLevelGridBuffer);
PassParameters->IndirectionGridBuffer = GraphBuilder.CreateSRV(IndirectionGridBuffer);
PassParameters->ExtinctionGridBuffer = GraphBuilder.CreateSRV(ExtinctionGridBuffer);
PassParameters->RWMajorantVoxelGridBuffer = GraphBuilder.CreateUAV(MajorantVoxelGridBuffer);
}
FBuildMajorantVoxelGridCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FBuildMajorantVoxelGridCS::FEnableIndirectionGrid>(HeterogeneousVolumes::EnableIndirectionGrid());
TShaderRef<FBuildMajorantVoxelGridCS> ComputeShader = GlobalShaderMap->GetShader<FBuildMajorantVoxelGridCS>(PermutationVector);
FIntVector GroupCount = FIntVector(
FMath::DivideAndRoundUp(TopLevelGridResolution.X, FBuildMajorantVoxelGridCS::GetThreadGroupSize3D()),
FMath::DivideAndRoundUp(TopLevelGridResolution.Y, FBuildMajorantVoxelGridCS::GetThreadGroupSize3D()),
FMath::DivideAndRoundUp(TopLevelGridResolution.Z, FBuildMajorantVoxelGridCS::GetThreadGroupSize3D())
);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("BuildMajorantVoxelGridCS"),
ERDGPassFlags::Compute,
ComputeShader,
PassParameters,
GroupCount
);
}
bool bUseMax = CVarHeterogeneousVolumesEnableMajorantGridMax.GetValueOnRenderThread() != 0;
if (bUseMax)
{
// Downsample majorant grid
FIntVector InputDimensions = TopLevelGridResolution;
FRDGBufferRef InputBuffer = MajorantVoxelGridBuffer;
FRDGBufferRef OutputBuffer = nullptr;
while (InputDimensions.X > 1 && InputDimensions.Y && InputDimensions.Z)
{
FIntVector OutputDimensions = FIntVector(
FMath::DivideAndRoundUp(InputDimensions.X, FDownsampleMajorantVoxelGridCS::GetThreadGroupSize3D()),
FMath::DivideAndRoundUp(InputDimensions.Y, FDownsampleMajorantVoxelGridCS::GetThreadGroupSize3D()),
FMath::DivideAndRoundUp(InputDimensions.Z, FDownsampleMajorantVoxelGridCS::GetThreadGroupSize3D())
);
uint32 OutputBufferSize = OutputDimensions.X * OutputDimensions.Y * OutputDimensions.Z;
OutputBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(FScalarGridData), OutputBufferSize),
TEXT("HeterogeneousVolumes.DownsampledBuffer")
);
FDownsampleMajorantVoxelGridCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FDownsampleMajorantVoxelGridCS::FParameters>();
{
PassParameters->InputDimensions = InputDimensions;
PassParameters->InputBuffer = GraphBuilder.CreateSRV(InputBuffer);
PassParameters->RWOutputBuffer = GraphBuilder.CreateUAV(OutputBuffer);
}
TShaderRef<FDownsampleMajorantVoxelGridCS> ComputeShader = GlobalShaderMap->GetShader<FDownsampleMajorantVoxelGridCS>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("FDownsampleMajorantVoxelGridCS"),
ERDGPassFlags::Compute | ERDGPassFlags::NeverCull,
ComputeShader,
PassParameters,
TopLevelGridResolution
);
InputBuffer = OutputBuffer;
InputDimensions = OutputDimensions;
}
// Copy maximum value into the first index of the majorant grid
if (OutputBuffer)
{
FCopyMaxIntoMajorantVoxelGridCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FCopyMaxIntoMajorantVoxelGridCS::FParameters>();
{
PassParameters->InputBuffer = GraphBuilder.CreateSRV(OutputBuffer);
PassParameters->RWMajorantVoxelGridBuffer = GraphBuilder.CreateUAV(MajorantVoxelGridBuffer);
}
TShaderRef<FCopyMaxIntoMajorantVoxelGridCS> ComputeShader = GlobalShaderMap->GetShader<FCopyMaxIntoMajorantVoxelGridCS>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("FCopyMaxIntoMajorantVoxelGridCS"),
ERDGPassFlags::Compute | ERDGPassFlags::NeverCull,
ComputeShader,
PassParameters,
FIntVector(1)
);
}
}
}
}
struct FRenderDebugData
{
FVector4f RayOrigin;
FVector4f RayDirection;
float TMin;
float TMax;
float Distance;
FVector4f EstimateAndPdf;
FMatrix44f ScreenToTranslatedWorld;
FMatrix44f ClipToTranslatedWorld;
};
class FRenderTransmittanceWithVoxelGridCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FRenderTransmittanceWithVoxelGridCS);
SHADER_USE_PARAMETER_STRUCT(FRenderTransmittanceWithVoxelGridCS, FGlobalShader);
class FDebugMode : SHADER_PERMUTATION_INT("DEBUG_MODE", 9);
using FPermutationDomain = TShaderPermutationDomain<FDebugMode>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
// Scene data
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureParameters, SceneTextures)
// Ray data
SHADER_PARAMETER(int, bJitter)
SHADER_PARAMETER(float, MaxTraceDistance)
SHADER_PARAMETER(float, StepSize)
SHADER_PARAMETER(int, MaxStepCount)
// Voxel grids
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FOrthoVoxelGridUniformBufferParameters, OrthoGridUniformBuffer)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FFrustumVoxelGridUniformBufferParameters, FrustumGridUniformBuffer)
// Dispatch data
SHADER_PARAMETER(FIntVector, GroupCount)
// Output
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, RWLightingTexture)
// Debug
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FRenderDebugData>, RWDebugBuffer)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(
const FGlobalShaderPermutationParameters& Parameters
)
{
return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform);
}
static void ModifyCompilationEnvironment(
const FGlobalShaderPermutationParameters& Parameters,
FShaderCompilerEnvironment& OutEnvironment
)
{
FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_1D"), GetThreadGroupSize1D());
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_2D"), GetThreadGroupSize2D());
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_3D"), GetThreadGroupSize3D());
OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads);
}
static int32 GetThreadGroupSize1D() { return GetThreadGroupSize3D() * GetThreadGroupSize3D() * GetThreadGroupSize3D(); }
static int32 GetThreadGroupSize2D() { return 8; }
static int32 GetThreadGroupSize3D() { return 4; }
};
IMPLEMENT_GLOBAL_SHADER(FRenderTransmittanceWithVoxelGridCS, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesVoxelGridRendering.usf", "RenderTransmittanceWithVoxelGridCS", SF_Compute);
void CalcViewBoundsAndMinimumVoxelSize(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
const FVoxelGridBuildOptions& BuildOptions,
FBoxSphereBounds::Builder& TopLevelGridBoundsBuilder,
float& MinimumVoxelSize
)
{
MinimumVoxelSize = HeterogeneousVolumes::GetMinimumVoxelSizeOutsideFrustum();
// Build view bounds
FVector WorldCameraOrigin = View.ViewMatrices.GetViewOrigin();
FBoxSphereBounds WorldCameraBounds(FSphere(WorldCameraOrigin, HeterogeneousVolumes::GetMaxTraceDistance()));
float TanHalfFOV = HeterogeneousVolumes::CalcTanHalfFOV(View.FOV);
float HalfWidth = View.ViewRect.Width() * 0.5 / HeterogeneousVolumes::GetDownsampleFactor();
float PixelWidth = TanHalfFOV / HalfWidth;
for (int32 MeshBatchIndex = 0; MeshBatchIndex < View.HeterogeneousVolumesMeshBatches.Num(); ++MeshBatchIndex)
{
// Only Niagara mesh particles bound to volume materials
const FMeshBatch* Mesh = View.HeterogeneousVolumesMeshBatches[MeshBatchIndex].Mesh;
const FPrimitiveSceneProxy* PrimitiveSceneProxy = View.HeterogeneousVolumesMeshBatches[MeshBatchIndex].Proxy;
if (!ShouldRenderMeshBatchWithHeterogeneousVolumes(Mesh, PrimitiveSceneProxy, View.GetFeatureLevel()))
{
continue;
}
for (int32 VolumeIndex = 0; VolumeIndex < Mesh->Elements.Num(); ++VolumeIndex)
{
IHeterogeneousVolumeInterface* HeterogeneousVolume = (IHeterogeneousVolumeInterface*)Mesh->Elements[VolumeIndex].UserData;
//check(HeterogeneousVolume != nullptr);
if (HeterogeneousVolume == nullptr)
{
continue;
}
// Only incorporate the primitive if it intersects with the camera bounding sphere where radius=MaxTraceDistance
const FBoxSphereBounds& PrimitiveBounds = HeterogeneousVolume->GetBounds();
if (View.ViewFrustum.IntersectBox(PrimitiveBounds.Origin, PrimitiveBounds.BoxExtent))
{
TopLevelGridBoundsBuilder += PrimitiveBounds;
// Bandlimit minimum voxel size request with projected voxel size, based on shading rate
FVector VoxelCenter = PrimitiveBounds.Origin;
float Distance = FMath::Max(FVector(PrimitiveBounds.Origin - WorldCameraOrigin).Length() - PrimitiveBounds.BoxExtent.Length(), 0.0);
float VoxelWidth = Distance * PixelWidth * BuildOptions.ShadingRateInFrustum;
float PerVolumeMinimumVoxelSize = FMath::Max(VoxelWidth, HeterogeneousVolume->GetMinimumVoxelSize());
MinimumVoxelSize = FMath::Min(PerVolumeMinimumVoxelSize, MinimumVoxelSize);
}
}
}
}
void CalcGlobalBoundsAndMinimumVoxelSize(
FRDGBuilder& GraphBuilder,
const TArray<FViewInfo>& Views,
const TSet<FVolumetricMeshBatch>& HeterogeneousVolumesMeshBatches,
const FVoxelGridBuildOptions& BuildOptions,
FBoxSphereBounds::Builder& TopLevelGridBoundsBuilder,
float& GlobalMinimumVoxelSize
)
{
GlobalMinimumVoxelSize = HeterogeneousVolumes::GetMinimumVoxelSizeOutsideFrustum();
// Cycle through all Volume PrimitiveSceneProxies to collect bounds information and minimum voxel-size
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
float ViewMinimumVoxelSize = HeterogeneousVolumes::GetMinimumVoxelSizeOutsideFrustum();
FBoxSphereBounds::Builder AggregatePrimitiveBoundsBuilder;
// Build view bounds
const FViewInfo& View = Views[ViewIndex];
FVector WorldCameraOrigin = View.ViewMatrices.GetViewOrigin();
FBoxSphereBounds WorldCameraBounds(FSphere(WorldCameraOrigin, HeterogeneousVolumes::GetMaxTraceDistance()));
float TanHalfFOV = HeterogeneousVolumes::CalcTanHalfFOV(View.FOV);
float HalfWidth = View.ViewRect.Width() * 0.5 / HeterogeneousVolumes::GetDownsampleFactor();
float PixelWidth = TanHalfFOV / HalfWidth;
for (auto MeshBatchIt = HeterogeneousVolumesMeshBatches.begin(); MeshBatchIt != HeterogeneousVolumesMeshBatches.end(); ++MeshBatchIt)
{
const FVolumetricMeshBatch& MeshBatch = *MeshBatchIt;
const FMeshBatch* Mesh = MeshBatch.Mesh;
const FPrimitiveSceneProxy* PrimitiveSceneProxy = MeshBatch.Proxy;
if (!ShouldRenderMeshBatchWithHeterogeneousVolumes(Mesh, PrimitiveSceneProxy, View.GetFeatureLevel()))
{
continue;
}
for (int32 VolumeIndex = 0; VolumeIndex < Mesh->Elements.Num(); ++VolumeIndex)
{
IHeterogeneousVolumeInterface* HeterogeneousVolume = (IHeterogeneousVolumeInterface*)Mesh->Elements[VolumeIndex].UserData;
//check(HeterogeneousVolume != nullptr);
if (HeterogeneousVolume == nullptr)
{
continue;
}
const FBoxSphereBounds& PrimitiveBounds = HeterogeneousVolume->GetBounds();
AggregatePrimitiveBoundsBuilder += PrimitiveBounds;
bool bIntersectsViewFrustum = View.ViewFrustum.IntersectBox(PrimitiveBounds.Origin, PrimitiveBounds.BoxExtent);
float VoxelWidth = bIntersectsViewFrustum ? BuildOptions.ShadingRateInFrustum : BuildOptions.ShadingRateOutOfFrustum;
if (BuildOptions.bUseProjectedPixelSizeForOrthoGrid)
{
FVector VoxelCenter = PrimitiveBounds.Origin;
float Distance = FMath::Max(FVector(PrimitiveBounds.Origin - WorldCameraOrigin).Length() - PrimitiveBounds.SphereRadius, 0.0);
VoxelWidth *= Distance * PixelWidth;
}
VoxelWidth = FMath::Max(VoxelWidth, HeterogeneousVolume->GetMinimumVoxelSize());
ViewMinimumVoxelSize = FMath::Min(VoxelWidth, ViewMinimumVoxelSize);
}
}
// When converting to pixel-space units, clamp per-view minimum voxel-size to in-frustum minimum
if (AggregatePrimitiveBoundsBuilder.IsValid())
{
FBoxSphereBounds AggregatePrimitiveBounds(AggregatePrimitiveBoundsBuilder);
if (BuildOptions.bUseProjectedPixelSizeForOrthoGrid && View.ViewFrustum.IntersectBox(AggregatePrimitiveBounds.Origin, AggregatePrimitiveBounds.BoxExtent))
{
ViewMinimumVoxelSize = FMath::Max(ViewMinimumVoxelSize, HeterogeneousVolumes::GetMinimumVoxelSizeInFrustum());
}
// Update global bounds and minimum voxel-size
TopLevelGridBoundsBuilder += AggregatePrimitiveBounds;
GlobalMinimumVoxelSize = FMath::Min(GlobalMinimumVoxelSize, ViewMinimumVoxelSize);
}
}
}
void ExtractFrustumVoxelGridUniformBuffer(
FRDGBuilder& GraphBuilder,
const TRDGUniformBufferRef<FFrustumVoxelGridUniformBufferParameters>& FrustumGridUniformBuffer,
FAdaptiveFrustumGridParameterCache& AdaptiveFrustumGridParameterCache
)
{
const TRDGParameterStruct<FFrustumVoxelGridUniformBufferParameters>& Parameters = FrustumGridUniformBuffer->GetParameters();
AdaptiveFrustumGridParameterCache.TopLevelGridWorldBoundsMin = Parameters->TopLevelGridWorldBoundsMin;
AdaptiveFrustumGridParameterCache.TopLevelGridWorldBoundsMax = Parameters->TopLevelGridWorldBoundsMax;
AdaptiveFrustumGridParameterCache.TopLevelGridResolution = Parameters->TopLevelFroxelGridResolution;
AdaptiveFrustumGridParameterCache.VoxelDimensions = Parameters->VoxelDimensions;
AdaptiveFrustumGridParameterCache.bUseFrustumGrid = Parameters->bUseFrustumGrid;
AdaptiveFrustumGridParameterCache.NearPlaneDepth = Parameters->NearPlaneDepth;
AdaptiveFrustumGridParameterCache.FarPlaneDepth = Parameters->FarPlaneDepth;
AdaptiveFrustumGridParameterCache.TanHalfFOV = Parameters->TanHalfFOV;
AdaptiveFrustumGridParameterCache.WorldToClip = Parameters->WorldToClip;
AdaptiveFrustumGridParameterCache.ClipToWorld = Parameters->ClipToWorld;
AdaptiveFrustumGridParameterCache.WorldToView = Parameters->WorldToView;
AdaptiveFrustumGridParameterCache.ViewToWorld = Parameters->ViewToWorld;
AdaptiveFrustumGridParameterCache.ViewToClip = Parameters->ViewToClip;
AdaptiveFrustumGridParameterCache.ClipToView = Parameters->ClipToView;
for (int i = 0; i < 6; ++i)
{
AdaptiveFrustumGridParameterCache.ViewFrustumPlanes[i] = Parameters->ViewFrustumPlanes[i];
}
GraphBuilder.QueueBufferExtraction(Parameters->TopLevelFroxelGridBuffer->GetParent(), &AdaptiveFrustumGridParameterCache.TopLevelGridBuffer);
GraphBuilder.QueueBufferExtraction(Parameters->ExtinctionFroxelGridBuffer->GetParent(), &AdaptiveFrustumGridParameterCache.ExtinctionGridBuffer);
GraphBuilder.QueueBufferExtraction(Parameters->EmissionFroxelGridBuffer->GetParent(), &AdaptiveFrustumGridParameterCache.EmissionGridBuffer);
GraphBuilder.QueueBufferExtraction(Parameters->ScatteringFroxelGridBuffer->GetParent(), &AdaptiveFrustumGridParameterCache.ScatteringGridBuffer);
}
void RegisterExternalFrustumVoxelGridUniformBuffer(
FRDGBuilder& GraphBuilder,
const FAdaptiveFrustumGridParameterCache& AdaptiveFrustumGridParameterCache,
TRDGUniformBufferRef<FFrustumVoxelGridUniformBufferParameters>& FrustumGridUniformBuffer
)
{
FFrustumVoxelGridUniformBufferParameters* UniformBufferParameters = GraphBuilder.AllocParameters<FFrustumVoxelGridUniformBufferParameters>();
{
UniformBufferParameters->WorldToClip = AdaptiveFrustumGridParameterCache.WorldToClip;
UniformBufferParameters->ClipToWorld = AdaptiveFrustumGridParameterCache.ClipToWorld;
UniformBufferParameters->WorldToView = AdaptiveFrustumGridParameterCache.WorldToView;
UniformBufferParameters->ViewToWorld = AdaptiveFrustumGridParameterCache.ViewToWorld;
UniformBufferParameters->ViewToClip = AdaptiveFrustumGridParameterCache.ViewToClip;
UniformBufferParameters->ClipToView = AdaptiveFrustumGridParameterCache.ClipToView;
UniformBufferParameters->TopLevelGridWorldBoundsMin = AdaptiveFrustumGridParameterCache.TopLevelGridWorldBoundsMin;
UniformBufferParameters->TopLevelGridWorldBoundsMax = AdaptiveFrustumGridParameterCache.TopLevelGridWorldBoundsMax;
UniformBufferParameters->TopLevelFroxelGridResolution = AdaptiveFrustumGridParameterCache.TopLevelGridResolution;
UniformBufferParameters->VoxelDimensions = AdaptiveFrustumGridParameterCache.TopLevelGridResolution;
UniformBufferParameters->bUseFrustumGrid = AdaptiveFrustumGridParameterCache.bUseFrustumGrid;
UniformBufferParameters->NearPlaneDepth = AdaptiveFrustumGridParameterCache.NearPlaneDepth;
UniformBufferParameters->FarPlaneDepth = AdaptiveFrustumGridParameterCache.FarPlaneDepth;
UniformBufferParameters->TanHalfFOV = AdaptiveFrustumGridParameterCache.TanHalfFOV;
// Frustum assignment
for (int i = 0; i < 6; ++i)
{
UniformBufferParameters->ViewFrustumPlanes[i] = AdaptiveFrustumGridParameterCache.ViewFrustumPlanes[i];
}
UniformBufferParameters->TopLevelFroxelGridBuffer = GraphBuilder.CreateSRV(GraphBuilder.RegisterExternalBuffer(AdaptiveFrustumGridParameterCache.TopLevelGridBuffer));
UniformBufferParameters->ExtinctionFroxelGridBuffer = GraphBuilder.CreateSRV(GraphBuilder.RegisterExternalBuffer(AdaptiveFrustumGridParameterCache.ExtinctionGridBuffer));
UniformBufferParameters->EmissionFroxelGridBuffer = GraphBuilder.CreateSRV(GraphBuilder.RegisterExternalBuffer(AdaptiveFrustumGridParameterCache.EmissionGridBuffer));
UniformBufferParameters->ScatteringFroxelGridBuffer = GraphBuilder.CreateSRV(GraphBuilder.RegisterExternalBuffer(AdaptiveFrustumGridParameterCache.ScatteringGridBuffer));
}
FrustumGridUniformBuffer = GraphBuilder.CreateUniformBuffer(UniformBufferParameters);
}
TRDGUniformBufferRef <FFrustumVoxelGridUniformBufferParameters> CreateEmptyFrustumVoxelGridUniformBuffer(
FRDGBuilder& GraphBuilder
)
{
FFrustumVoxelGridUniformBufferParameters* UniformBufferParameters = GraphBuilder.AllocParameters<FFrustumVoxelGridUniformBufferParameters>();
{
UniformBufferParameters->WorldToClip = FMatrix44f::Identity;
UniformBufferParameters->ClipToWorld = FMatrix44f::Identity;
UniformBufferParameters->WorldToView = FMatrix44f::Identity;
UniformBufferParameters->ViewToWorld = FMatrix44f::Identity;
UniformBufferParameters->ViewToClip = FMatrix44f::Identity;
UniformBufferParameters->ClipToView = FMatrix44f::Identity;
UniformBufferParameters->TopLevelFroxelGridBuffer = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FTopLevelGridData)));
UniformBufferParameters->ExtinctionFroxelGridBuffer = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FScalarGridData)));
UniformBufferParameters->EmissionFroxelGridBuffer = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FVectorGridData)));
UniformBufferParameters->ScatteringFroxelGridBuffer = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FVectorGridData)));
UniformBufferParameters->TopLevelGridWorldBoundsMin = FVector3f(0);
UniformBufferParameters->TopLevelGridWorldBoundsMax = FVector3f(0);
UniformBufferParameters->TopLevelFroxelGridResolution = FIntVector(0);
UniformBufferParameters->VoxelDimensions = FIntVector(0);
UniformBufferParameters->bUseFrustumGrid = false;
UniformBufferParameters->NearPlaneDepth = 0.0;
UniformBufferParameters->FarPlaneDepth = 0.0;
UniformBufferParameters->TanHalfFOV = 1.0;
}
return GraphBuilder.CreateUniformBuffer(UniformBufferParameters);
}
void ClipNearFarDistances(const FViewInfo& View, const FBoxSphereBounds& TopLevelGridBounds, float& NearPlaneDistance, float& FarPlaneDistance)
{
// Determine near and far planes, in world-space
float d = -FVector::DotProduct(View.GetViewDirection(), View.ViewLocation);
// Analyze the input volumes and determine the near/far extents
FVector Center = TopLevelGridBounds.GetSphere().Center;
// Project center onto the camera view-plane
float SignedDistance = FVector::DotProduct(View.GetViewDirection(), Center) + d;
float Radius = TopLevelGridBounds.GetSphere().W;
float NearDistance = SignedDistance - Radius;
float FarDistance = SignedDistance + Radius;
if (NearPlaneDistance < 0.0)
{
NearPlaneDistance = NearDistance;
}
if (FarPlaneDistance < 0.0)
{
FarPlaneDistance = FarDistance;
}
NearPlaneDistance = FMath::Max(NearPlaneDistance, 0.01);
FarPlaneDistance = FMath::Max(FarPlaneDistance, NearDistance + 1.0);
}
void CalculateTopLevelGridResolution(
FBoxSphereBounds TopLevelGridBounds,
float GlobalMinimumVoxelSize,
FIntVector& TopLevelGridResolution
)
{
// Bound Top-level grid resolution to cover fully allocated child grids at minimum voxel size
FIntVector CombinedChildGridResolution = FIntVector(HeterogeneousVolumes::GetBottomLevelGridResolution());
if (HeterogeneousVolumes::EnableIndirectionGrid())
{
CombinedChildGridResolution *= HeterogeneousVolumes::GetIndirectionGridResolution();
}
FVector TopLevelGridVoxelSize = FVector(CombinedChildGridResolution) * GlobalMinimumVoxelSize;
FVector TopLevelGridResolutionAsFloat = (TopLevelGridBounds.BoxExtent * 2.0) / TopLevelGridVoxelSize;
TopLevelGridResolution.X = FMath::CeilToInt(TopLevelGridResolutionAsFloat.X);
TopLevelGridResolution.Y = FMath::CeilToInt(TopLevelGridResolutionAsFloat.Y);
TopLevelGridResolution.Z = FMath::CeilToInt(TopLevelGridResolutionAsFloat.Z);
// Clamp to a moderate limit to also handle indirection grid allocation
TopLevelGridResolution.X = FMath::Clamp(TopLevelGridResolution.X, 1, 128);
TopLevelGridResolution.Y = FMath::Clamp(TopLevelGridResolution.Y, 1, 128);
TopLevelGridResolution.Z = FMath::Clamp(TopLevelGridResolution.Z, 1, 256);
}
void CalculateVoxelSize(
FRDGBuilder& GraphBuilder,
const FScene* Scene,
/*const*/ TArray<FViewInfo>& Views,
const TSet<FVolumetricMeshBatch>& HeterogeneousVolumesMeshBatches,
const FVoxelGridBuildOptions& BuildOptions,
FBoxSphereBounds TopLevelGridBounds,
FIntVector TopLevelGridResolution,
FRDGBufferRef& TopLevelGridBuffer
)
{
TopLevelGridBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(FTopLevelGridData), TopLevelGridResolution.X * TopLevelGridResolution.Y * TopLevelGridResolution.Z),
TEXT("HeterogeneousVolumes.TopLevelGridBuffer")
);
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(TopLevelGridBuffer), 0xFFFFFFF8);
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
// Build view
const FViewInfo& View = Views[ViewIndex];
FVector WorldCameraOrigin = View.ViewMatrices.GetViewOrigin();
FBoxSphereBounds WorldCameraBounds(FSphere(WorldCameraOrigin, HeterogeneousVolumes::GetMaxTraceDistance()));
for (auto MeshBatchIt = HeterogeneousVolumesMeshBatches.begin(); MeshBatchIt != HeterogeneousVolumesMeshBatches.end(); ++MeshBatchIt)
{
const FVolumetricMeshBatch& MeshBatch = *MeshBatchIt;
const FMeshBatch* Mesh = MeshBatch.Mesh;
const FPrimitiveSceneProxy* PrimitiveSceneProxy = MeshBatch.Proxy;
for (int32 VolumeIndex = 0; VolumeIndex < Mesh->Elements.Num(); ++VolumeIndex)
{
IHeterogeneousVolumeInterface* HeterogeneousVolume = (IHeterogeneousVolumeInterface*)Mesh->Elements[VolumeIndex].UserData;
//check(HeterogeneousVolume != nullptr);
if (HeterogeneousVolume == nullptr)
{
continue;
}
const FBoxSphereBounds& PrimitiveBounds = HeterogeneousVolume->GetBounds();
FTopLevelGridCalculateVoxelSize::FParameters* PassParameters = GraphBuilder.AllocParameters<FTopLevelGridCalculateVoxelSize::FParameters>();
{
PassParameters->View = View.ViewUniformBuffer;
PassParameters->TopLevelGridResolution = TopLevelGridResolution;
PassParameters->TopLevelGridWorldBoundsMin = FVector3f(TopLevelGridBounds.Origin - TopLevelGridBounds.BoxExtent);
PassParameters->TopLevelGridWorldBoundsMax = FVector3f(TopLevelGridBounds.Origin + TopLevelGridBounds.BoxExtent);
PassParameters->PrimitiveWorldBoundsMin = FVector3f(PrimitiveBounds.Origin - PrimitiveBounds.BoxExtent);
PassParameters->PrimitiveWorldBoundsMax = FVector3f(PrimitiveBounds.Origin + PrimitiveBounds.BoxExtent);
PassParameters->ShadingRateInFrustum = BuildOptions.ShadingRateInFrustum;
PassParameters->ShadingRateOutOfFrustum = BuildOptions.ShadingRateOutOfFrustum;
PassParameters->MinVoxelSizeInFrustum = FMath::Max(HeterogeneousVolume->GetMinimumVoxelSize(), HeterogeneousVolumes::GetMinimumVoxelSizeInFrustum());
PassParameters->MinVoxelSizeOutOfFrustum = HeterogeneousVolumes::GetMinimumVoxelSizeOutsideFrustum();
PassParameters->DownsampleFactor = HeterogeneousVolumes::GetDownsampleFactor();
PassParameters->bUseProjectedPixelSize = BuildOptions.bUseProjectedPixelSizeForOrthoGrid;
PassParameters->RWTopLevelGridBuffer = GraphBuilder.CreateUAV(TopLevelGridBuffer);
}
FIntVector GroupCount;
GroupCount.X = FMath::DivideAndRoundUp(TopLevelGridResolution.X, FTopLevelGridCalculateVoxelSize::GetThreadGroupSize3D());
GroupCount.Y = FMath::DivideAndRoundUp(TopLevelGridResolution.Y, FTopLevelGridCalculateVoxelSize::GetThreadGroupSize3D());
GroupCount.Z = FMath::DivideAndRoundUp(TopLevelGridResolution.Z, FTopLevelGridCalculateVoxelSize::GetThreadGroupSize3D());
FTopLevelGridCalculateVoxelSize::FPermutationDomain PermutationVector;
TShaderRef<FTopLevelGridCalculateVoxelSize> ComputeShader = View.ShaderMap->GetShader<FTopLevelGridCalculateVoxelSize>(PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("TopLevelGridCalculateVoxelSize"),
ERDGPassFlags::Compute | ERDGPassFlags::NeverCull,
ComputeShader,
PassParameters,
GroupCount
);
}
}
}
}
void MarkTopLevelGrid(
FRDGBuilder& GraphBuilder,
const FScene* Scene,
FBoxSphereBounds TopLevelGridBounds,
FIntVector TopLevelGridResolution,
FRDGBufferRef& TopLevelGridBuffer,
FRDGBufferRef& IndirectionGridBuffer
)
{
int32 IndirectionGridBufferSize = TopLevelGridResolution.X * TopLevelGridResolution.Y * TopLevelGridResolution.Z * 16 * sizeof(FTopLevelGridData);
IndirectionGridBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(FTopLevelGridData), IndirectionGridBufferSize),
TEXT("HeterogeneousVolumes.OrthoGrid.IndirectionGridBuffer")
);
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(IndirectionGridBuffer), 0xFFFFFFF8);
FRDGBufferRef IndirectionGridAllocatorBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), 2),
TEXT("HeterogeneousVolume.OrthoGrid.IndirectionGridAllocatorBuffer")
);
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(IndirectionGridAllocatorBuffer, PF_R32_UINT), 0);
FAllocateBottomLevelGrid::FParameters* PassParameters = GraphBuilder.AllocParameters<FAllocateBottomLevelGrid::FParameters>();
{
//PassParameters->View = View.ViewUniformBuffer;
PassParameters->TopLevelGridResolution = TopLevelGridResolution;
PassParameters->TopLevelGridWorldBoundsMin = FVector3f(TopLevelGridBounds.Origin - TopLevelGridBounds.BoxExtent);
PassParameters->TopLevelGridWorldBoundsMax = FVector3f(TopLevelGridBounds.Origin + TopLevelGridBounds.BoxExtent);
PassParameters->MaxVoxelResolution = HeterogeneousVolumes::GetBottomLevelGridResolution();
PassParameters->bSampleAtVertices = HeterogeneousVolumes::EnableLinearInterpolation();
PassParameters->RWTopLevelGridBuffer = GraphBuilder.CreateUAV(TopLevelGridBuffer);
// Indirection Grid
PassParameters->IndirectionGridBufferSize = IndirectionGridBufferSize;
PassParameters->RWIndirectionGridBuffer = GraphBuilder.CreateUAV(IndirectionGridBuffer);
PassParameters->RWIndirectionGridAllocatorBuffer = GraphBuilder.CreateUAV(IndirectionGridAllocatorBuffer, PF_R32_UINT);
}
FIntVector GroupCount;
GroupCount.X = FMath::DivideAndRoundUp(TopLevelGridResolution.X, FAllocateBottomLevelGrid::GetThreadGroupSize3D());
GroupCount.Y = FMath::DivideAndRoundUp(TopLevelGridResolution.Y, FAllocateBottomLevelGrid::GetThreadGroupSize3D());
GroupCount.Z = FMath::DivideAndRoundUp(TopLevelGridResolution.Z, FAllocateBottomLevelGrid::GetThreadGroupSize3D());
const FGlobalShaderMap* GlobalShaderMap = GetGlobalShaderMap(Scene->GetFeatureLevel());
FAllocateBottomLevelGrid::FPermutationDomain PermutationVector;
PermutationVector.Set<FAllocateBottomLevelGrid::FEnableIndirectionGrid>(HeterogeneousVolumes::EnableIndirectionGrid());
TShaderRef<FAllocateBottomLevelGrid> ComputeShader = GlobalShaderMap->GetShader<FAllocateBottomLevelGrid>(PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("AllocateBottomLevelGrid"),
ERDGPassFlags::Compute | ERDGPassFlags::NeverCull,
ComputeShader,
PassParameters,
GroupCount
);
}
void GenerateRasterTiles(
FRDGBuilder& GraphBuilder,
const FScene* Scene,
bool bEnableIndirectionGrid,
FIntVector TopLevelGridResolution,
FRDGBufferRef TopLevelGridBuffer,
FRDGBufferRef& RasterTileBuffer,
FRDGBufferRef& RasterTileAllocatorBuffer
)
{
const uint32 RasterTileVoxelResolution = HeterogeneousVolumes::GetBottomLevelGridResolution();
uint32 TileFactor = HeterogeneousVolumes::EnableIndirectionGrid() ? 64 : 1;
uint32 MaxNumRasterTiles = TileFactor * TopLevelGridResolution.X * TopLevelGridResolution.Y * TopLevelGridResolution.Z;
RasterTileBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(FRasterTileData), MaxNumRasterTiles),
TEXT("HeterogeneousVolumes.OrthoGrid.RasterTileBuffer")
);
RasterTileAllocatorBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), 1),
TEXT("HeterogeneousVolume.OrthoGrid.RasterTileAllocatorBuffer")
);
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(RasterTileAllocatorBuffer, PF_R32_UINT), 0);
{
FGenerateRasterTiles::FParameters* PassParameters = GraphBuilder.AllocParameters<FGenerateRasterTiles::FParameters>();
{
PassParameters->TopLevelGridResolution = TopLevelGridResolution;
PassParameters->TopLevelGridBuffer = GraphBuilder.CreateSRV(TopLevelGridBuffer);
PassParameters->RasterTileVoxelResolution = RasterTileVoxelResolution;
PassParameters->MaxNumRasterTiles = MaxNumRasterTiles;
PassParameters->RWRasterTileAllocatorBuffer = GraphBuilder.CreateUAV(RasterTileAllocatorBuffer, PF_R32_UINT);
PassParameters->RWRasterTileBuffer = GraphBuilder.CreateUAV(RasterTileBuffer);
}
FIntVector GroupCount;
GroupCount.X = FMath::DivideAndRoundUp(TopLevelGridResolution.X, FTopLevelGridCalculateVoxelSize::GetThreadGroupSize3D());
GroupCount.Y = FMath::DivideAndRoundUp(TopLevelGridResolution.Y, FTopLevelGridCalculateVoxelSize::GetThreadGroupSize3D());
GroupCount.Z = FMath::DivideAndRoundUp(TopLevelGridResolution.Z, FTopLevelGridCalculateVoxelSize::GetThreadGroupSize3D());
const FGlobalShaderMap* GlobalShaderMap = GetGlobalShaderMap(Scene->GetFeatureLevel());
FGenerateRasterTiles::FPermutationDomain PermutationVector;
PermutationVector.Set<FGenerateRasterTiles::FEnableIndirectionGrid>(bEnableIndirectionGrid);
TShaderRef<FGenerateRasterTiles> ComputeShader = GlobalShaderMap->GetShader<FGenerateRasterTiles>(PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("GenerateRasterTiles"),
ERDGPassFlags::Compute | ERDGPassFlags::NeverCull,
ComputeShader,
PassParameters,
GroupCount
);
}
}
void CalculateTopLevelGridResolutionForFrustumGrid(
const FViewInfo& View,
float MinimumVoxelSize,
float NearPlaneDistance,
float& FarPlaneDistance,
FIntVector& TopLevelGridResolution
)
{
// Determine top-level grid resolution
// Build a grid where the resolution is proportional to a bottom-level grid that is using all available memory
int32 BottomLevelGridResolution = HeterogeneousVolumes::GetBottomLevelGridResolution();
float ShadingRate = HeterogeneousVolumes::GetShadingRateForFrustumGrid();
int32 Width = FMath::CeilToInt32(View.ViewRect.Width() / ShadingRate);
int32 Height = FMath::CeilToInt32(View.ViewRect.Height() / ShadingRate);
// Use view frustum field-of-view and preferred minimum voxel size to find optimal far-plane distance
if (HeterogeneousVolumes::EnableOrthoVoxelGrid() &&
HeterogeneousVolumes::EnableFarPlaneAutoTransition() &&
ShadingRate >= HeterogeneousVolumes::GetShadingRateForOrthoGrid())
{
float GlobalMinimumVoxelSize = FMath::Max(MinimumVoxelSize, HeterogeneousVolumes::GetMinimumVoxelSizeInFrustum());
float Theta = FMath::DegreesToRadians(View.FOV * 0.5);
float PixelTheta = (2.0 * Theta) / Width;
float TanOfPixel = FMath::Tan(PixelTheta);
float OptimalFarPlaneDepth = GlobalMinimumVoxelSize / TanOfPixel;
FarPlaneDistance = FMath::Min(FarPlaneDistance, OptimalFarPlaneDepth);
}
// Depth slices should not be smaller than the declared minimum voxel size
int32 MaxDepth = FMath::CeilToInt32((FarPlaneDistance - NearPlaneDistance) / MinimumVoxelSize);
int32 Depth = FMath::Min(FMath::CeilToInt32(HeterogeneousVolumes::GetDepthSliceCountForFrustumGrid() / ShadingRate), MaxDepth);
TopLevelGridResolution = FIntVector(Width, Height, Depth);
TopLevelGridResolution.X = FMath::Max(FMath::DivideAndRoundUp(TopLevelGridResolution.X, BottomLevelGridResolution), 1);
TopLevelGridResolution.Y = FMath::Max(FMath::DivideAndRoundUp(TopLevelGridResolution.Y, BottomLevelGridResolution), 1);
TopLevelGridResolution.Z = FMath::Max(FMath::DivideAndRoundUp(TopLevelGridResolution.Z, BottomLevelGridResolution), 1);
}
void MarkTopLevelGridVoxelsForFrustumGrid(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
FIntVector TopLevelGridResolution,
float NearPlaneDistance,
float FarPlaneDistance,
const FMatrix& ViewToWorld,
FRDGBufferRef& TopLevelGridBuffer
)
{
int32 TopLevelVoxelCount = TopLevelGridResolution.X * TopLevelGridResolution.Y * TopLevelGridResolution.Z;
TopLevelGridBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(FTopLevelGridData), TopLevelVoxelCount),
TEXT("HeterogeneousVolumes.FrustumGrid.TopLevelGridBuffer")
);
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(TopLevelGridBuffer), 0xFFFFFFF8);
for (int32 MeshBatchIndex = 0; MeshBatchIndex < View.HeterogeneousVolumesMeshBatches.Num(); ++MeshBatchIndex)
{
const FMeshBatch* Mesh = View.HeterogeneousVolumesMeshBatches[MeshBatchIndex].Mesh;
const FPrimitiveSceneProxy* PrimitiveSceneProxy = View.HeterogeneousVolumesMeshBatches[MeshBatchIndex].Proxy;
for (int32 VolumeIndex = 0; VolumeIndex < Mesh->Elements.Num(); ++VolumeIndex)
{
IHeterogeneousVolumeInterface* HeterogeneousVolume = (IHeterogeneousVolumeInterface*)Mesh->Elements[VolumeIndex].UserData;
//check(HeterogeneousVolume != nullptr);
if (HeterogeneousVolume == nullptr)
{
continue;
}
const FBoxSphereBounds& PrimitiveBounds = HeterogeneousVolume->GetBounds();
FMarkTopLevelGridVoxelsForFrustumGrid::FParameters* PassParameters = GraphBuilder.AllocParameters<FMarkTopLevelGridVoxelsForFrustumGrid::FParameters>();
{
PassParameters->View = View.ViewUniformBuffer;
PassParameters->PrimitiveWorldBoundsMin = FVector3f(PrimitiveBounds.Origin - PrimitiveBounds.BoxExtent);
PassParameters->PrimitiveWorldBoundsMax = FVector3f(PrimitiveBounds.Origin + PrimitiveBounds.BoxExtent);
PassParameters->ViewToWorld = FMatrix44f(ViewToWorld);
PassParameters->TanHalfFOV = HeterogeneousVolumes::CalcTanHalfFOV(View.FOV);
PassParameters->NearPlaneDepth = NearPlaneDistance;
PassParameters->FarPlaneDepth = FarPlaneDistance;
PassParameters->VoxelDimensions = TopLevelGridResolution;
PassParameters->TopLevelGridResolution = TopLevelGridResolution;
PassParameters->RWTopLevelGridBuffer = GraphBuilder.CreateUAV(TopLevelGridBuffer);
}
FIntVector GroupCount;
GroupCount.X = FMath::DivideAndRoundUp(TopLevelGridResolution.X, FMarkTopLevelGridVoxelsForFrustumGrid::GetThreadGroupSize3D());
GroupCount.Y = FMath::DivideAndRoundUp(TopLevelGridResolution.Y, FMarkTopLevelGridVoxelsForFrustumGrid::GetThreadGroupSize3D());
GroupCount.Z = FMath::DivideAndRoundUp(TopLevelGridResolution.Z, FMarkTopLevelGridVoxelsForFrustumGrid::GetThreadGroupSize3D());
FMarkTopLevelGridVoxelsForFrustumGrid::FPermutationDomain PermutationVector;
TShaderRef<FMarkTopLevelGridVoxelsForFrustumGrid> ComputeShader = View.ShaderMap->GetShader<FMarkTopLevelGridVoxelsForFrustumGrid>(PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("MarkTopLevelGridVoxelsForFrustumGrid"),
ERDGPassFlags::Compute | ERDGPassFlags::NeverCull,
ComputeShader,
PassParameters,
GroupCount
);
}
}
}
void RasterizeVolumesIntoFrustumVoxelGrid(
FRDGBuilder& GraphBuilder,
const FScene* Scene,
const FViewInfo& View,
const FVoxelGridBuildOptions& BuildOptions,
// Transform data
FMatrix& ViewToWorld,
float NearPlaneDistance,
float FarPlaneDistance,
// Raster tile
FRDGBufferRef RasterTileBuffer,
FRDGBufferRef RasterTileAllocatorBuffer,
// Top-level grid
FBoxSphereBounds TopLevelGridBounds,
FIntVector TopLevelGridResolution,
FRDGBufferRef& TopLevelGridBuffer,
// Bottom-level grid
FRDGBufferRef& ExtinctionGridBuffer,
FRDGBufferRef& EmissionGridBuffer,
FRDGBufferRef& ScatteringGridBuffer
)
{
//Setup indirect dispatch
FRDGBufferRef RasterizeBottomLevelGridIndirectArgsBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateIndirectDesc<FRHIDispatchIndirectParameters>(1),
TEXT("HeterogeneousVolume.FrustumGrid.RasterizeBottomLevelGridIndirectArgs")
);
FSetRasterizeBottomLevelGridIndirectArgs::FParameters* IndirectArgsPassParameters = GraphBuilder.AllocParameters<FSetRasterizeBottomLevelGridIndirectArgs::FParameters>();
{
IndirectArgsPassParameters->MaxDispatchThreadGroupsPerDimension = GRHIMaxDispatchThreadGroupsPerDimension;
IndirectArgsPassParameters->RasterTileAllocatorBuffer = GraphBuilder.CreateSRV(RasterTileAllocatorBuffer, PF_R32_UINT);
IndirectArgsPassParameters->RWRasterizeBottomLevelGridIndirectArgsBuffer = GraphBuilder.CreateUAV(RasterizeBottomLevelGridIndirectArgsBuffer, PF_R32_UINT);
}
FIntVector GroupCount(1, 1, 1);
TShaderRef<FSetRasterizeBottomLevelGridIndirectArgs> ComputeShader = View.ShaderMap->GetShader<FSetRasterizeBottomLevelGridIndirectArgs>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("SetRasterizeBottomLevelGridIndirectArgs"),
ERDGPassFlags::Compute,
ComputeShader,
IndirectArgsPassParameters,
GroupCount
);
// Pre-allocate bottom-level voxel grid pool based on user-defined budget
int32 MaxBottomLevelVoxelCount = (HeterogeneousVolumes::GetMaxBottomLevelMemoryInMegabytesForFrustumGrid() * 1e6) / sizeof(FVectorGridData);
int32 BottomLevelGridBufferSize = MaxBottomLevelVoxelCount;
ExtinctionGridBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(FScalarGridData), BottomLevelGridBufferSize),
TEXT("HeterogeneousVolumes.FrustumGrid.ExtinctionGridBuffer")
);
EmissionGridBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(FVectorGridData), BottomLevelGridBufferSize),
TEXT("HeterogeneousVolumes.FrustumGrid.EmissionGridBuffer")
);
ScatteringGridBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(FVectorGridData), BottomLevelGridBufferSize),
TEXT("HeterogeneousVolumes.FrustumGrid.ScatteringGridBuffer")
);
FRDGBufferRef BottomLevelGridAllocatorBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), 2),
TEXT("HeterogeneousVolume.FrustumGrid.BottomLevelGridAllocatorBuffer")
);
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(BottomLevelGridAllocatorBuffer, PF_R32_UINT), 0);
// Rasterize volumes into bottom-level grid
for (int32 MeshBatchIndex = 0; MeshBatchIndex < View.HeterogeneousVolumesMeshBatches.Num(); ++MeshBatchIndex)
{
const FMeshBatch* Mesh = View.HeterogeneousVolumesMeshBatches[MeshBatchIndex].Mesh;
const FPrimitiveSceneProxy* PrimitiveSceneProxy = View.HeterogeneousVolumesMeshBatches[MeshBatchIndex].Proxy;
const FMaterialRenderProxy* MaterialRenderProxy = Mesh->MaterialRenderProxy;
if (!ShouldRenderMeshBatchWithHeterogeneousVolumes(Mesh, PrimitiveSceneProxy, View.GetFeatureLevel()))
{
continue;
}
for (int32 VolumeIndex = 0; VolumeIndex < Mesh->Elements.Num(); ++VolumeIndex)
{
const IHeterogeneousVolumeInterface* HeterogeneousVolumeInterface = (IHeterogeneousVolumeInterface*)Mesh->Elements[VolumeIndex].UserData;
//check(HeterogeneousVolumeInterface != nullptr);
if (HeterogeneousVolumeInterface == nullptr)
{
continue;
}
const FPrimitiveSceneInfo* PrimitiveSceneInfo = PrimitiveSceneProxy->GetPrimitiveSceneInfo();
const int32 PrimitiveId = PrimitiveSceneInfo->GetIndex();
const FBoxSphereBounds LocalBoxSphereBounds = PrimitiveSceneProxy->GetLocalBounds();
const FBoxSphereBounds PrimitiveBounds = PrimitiveSceneProxy->GetBounds();
const FMaterial& Material = MaterialRenderProxy->GetMaterialWithFallback(View.GetFeatureLevel(), MaterialRenderProxy);
FRasterizeBottomLevelFrustumGridCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FRasterizeBottomLevelFrustumGridCS::FParameters>();
{
// Scene data
PassParameters->View = View.ViewUniformBuffer;
PassParameters->Scene = View.GetSceneUniforms().GetBuffer(GraphBuilder);
PassParameters->EyeAdaptationBuffer = GraphBuilder.CreateSRV(GetEyeAdaptationBuffer(GraphBuilder, View));
// Primitive data
PassParameters->PrimitiveWorldBoundsMin = FVector3f(PrimitiveBounds.Origin - PrimitiveBounds.BoxExtent);
PassParameters->PrimitiveWorldBoundsMax = FVector3f(PrimitiveBounds.Origin + PrimitiveBounds.BoxExtent);
// TODO: Convert to relative-local space
//FVector3f ViewOriginHigh = FDFVector3(View.ViewMatrices.GetViewOrigin()).High;
//FMatrix44f RelativeLocalToWorld = FDFMatrix::MakeToRelativeWorldMatrix(ViewOriginHigh, HeterogeneousVolumeInterface->GetLocalToWorld()).M;
FMatrix InstanceToLocal = HeterogeneousVolumeInterface->GetInstanceToLocal();
FMatrix LocalToWorld = HeterogeneousVolumeInterface->GetLocalToWorld();
PassParameters->LocalToWorld = FMatrix44f(InstanceToLocal * LocalToWorld);
PassParameters->WorldToLocal = FMatrix44f(PassParameters->LocalToWorld.Inverse());
FMatrix LocalToInstance = InstanceToLocal.Inverse();
FBoxSphereBounds InstanceBoxSphereBounds = LocalBoxSphereBounds.TransformBy(FTransform(LocalToInstance));
PassParameters->LocalBoundsOrigin = FVector3f(InstanceBoxSphereBounds.Origin);
PassParameters->LocalBoundsExtent = FVector3f(InstanceBoxSphereBounds.BoxExtent);
PassParameters->PrimitiveId = PrimitiveId;
// Volume data
PassParameters->TopLevelGridResolution = TopLevelGridResolution;
PassParameters->VoxelDimensions = TopLevelGridResolution;
PassParameters->ViewToWorld = FMatrix44f(ViewToWorld);
PassParameters->TanHalfFOV = HeterogeneousVolumes::CalcTanHalfFOV(View.FOV);
PassParameters->NearPlaneDepth = NearPlaneDistance;
PassParameters->FarPlaneDepth = FarPlaneDistance;
// Sampling data
PassParameters->bJitter = BuildOptions.bJitter;
FBlueNoise BlueNoise = GetBlueNoiseGlobalParameters();
PassParameters->BlueNoise = CreateUniformBufferImmediate(BlueNoise, EUniformBufferUsage::UniformBuffer_SingleDraw);
// Raster tile data
PassParameters->RasterTileAllocatorBuffer = GraphBuilder.CreateSRV(RasterTileAllocatorBuffer, PF_R32_UINT);
PassParameters->RasterTileBuffer = GraphBuilder.CreateSRV(RasterTileBuffer);
// Indirect args
PassParameters->IndirectArgs = RasterizeBottomLevelGridIndirectArgsBuffer;
// Grid data
PassParameters->RWBottomLevelGridAllocatorBuffer = GraphBuilder.CreateUAV(BottomLevelGridAllocatorBuffer, PF_R32_UINT);
PassParameters->RWTopLevelGridBuffer = GraphBuilder.CreateUAV(TopLevelGridBuffer);
PassParameters->RWExtinctionGridBuffer = GraphBuilder.CreateUAV(ExtinctionGridBuffer);
PassParameters->RWEmissionGridBuffer = GraphBuilder.CreateUAV(EmissionGridBuffer);
PassParameters->RWScatteringGridBuffer = GraphBuilder.CreateUAV(ScatteringGridBuffer);
PassParameters->BottomLevelGridBufferSize = BottomLevelGridBufferSize;
}
GraphBuilder.AddPass(
RDG_EVENT_NAME("FrustumGrid.RasterizeBottomLevelGrid"),
PassParameters,
ERDGPassFlags::Compute,
[PassParameters, Scene, &View, MaterialRenderProxy, &Material](FRDGAsyncTask, FRHIComputeCommandList& RHICmdList)
{
FRasterizeBottomLevelFrustumGridCS::FPermutationDomain PermutationVector;
TShaderRef<FRasterizeBottomLevelFrustumGridCS> ComputeShader = Material.GetShader<FRasterizeBottomLevelFrustumGridCS>(&FLocalVertexFactory::StaticType, PermutationVector, false);
if (!ComputeShader.IsNull())
{
ClearUnusedGraphResources(ComputeShader, PassParameters);
FMeshDrawShaderBindings ShaderBindings;
UE::MeshPassUtils::SetupComputeBindings(ComputeShader, Scene, Scene->GetFeatureLevel(), nullptr, *MaterialRenderProxy, Material, ShaderBindings);
UE::MeshPassUtils::DispatchIndirect(RHICmdList, ComputeShader, ShaderBindings, *PassParameters, PassParameters->IndirectArgs->GetIndirectRHICallBuffer(), 0);
}
}
);
}
}
}
void BuildFrustumVoxelGrid(
FRDGBuilder& GraphBuilder,
const FScene* Scene,
const FViewInfo& View,
const FVoxelGridBuildOptions& BuildOptions,
TRDGUniformBufferRef<FFrustumVoxelGridUniformBufferParameters>& FrustumGridUniformBuffer
)
{
if (!ShouldRenderHeterogeneousVolumesForView(View) || !HeterogeneousVolumes::EnableFrustumVoxelGrid() || !BuildOptions.bBuildFrustumGrid)
{
FrustumGridUniformBuffer = CreateEmptyFrustumVoxelGridUniformBuffer(GraphBuilder);
return;
}
RDG_EVENT_SCOPE(GraphBuilder, "Frustum Grid Build");
// Determine the minimum voxel size for the scene, based on screen projection or user-defined minima
FBoxSphereBounds::Builder TopLevelGridBoundsBuilder;
float MinimumVoxelSize;
CalcViewBoundsAndMinimumVoxelSize(GraphBuilder, View, BuildOptions, TopLevelGridBoundsBuilder, MinimumVoxelSize);
if (!TopLevelGridBoundsBuilder.IsValid())
{
FrustumGridUniformBuffer = CreateEmptyFrustumVoxelGridUniformBuffer(GraphBuilder);
return;
}
FBoxSphereBounds TopLevelGridBounds(TopLevelGridBoundsBuilder);
float NearPlaneDistance = HeterogeneousVolumes::GetNearPlaneDistanceForFrustumGrid();
float FarPlaneDistance = HeterogeneousVolumes::GetFarPlaneDistanceForFrustumGrid();
ClipNearFarDistances(View, TopLevelGridBounds, NearPlaneDistance, FarPlaneDistance);
FIntVector TopLevelGridResolution;
CalculateTopLevelGridResolutionForFrustumGrid(
View,
MinimumVoxelSize,
NearPlaneDistance,
FarPlaneDistance,
TopLevelGridResolution
);
// Construct top-level grid over global bounding domain with some pre-determined resolution
int32 TopLevelVoxelCount = TopLevelGridResolution.X * TopLevelGridResolution.Y * TopLevelGridResolution.Z;
if (TopLevelVoxelCount == 0)
{
FrustumGridUniformBuffer = CreateEmptyFrustumVoxelGridUniformBuffer(GraphBuilder);
return;
}
FMatrix WorldToView = View.ViewMatrices.GetViewMatrix();
FMatrix ViewToWorld = View.ViewMatrices.GetInvViewMatrix();
// Mark top-level voxels for rasterization
FRDGBufferRef TopLevelGridBuffer;
MarkTopLevelGridVoxelsForFrustumGrid(
GraphBuilder,
View,
TopLevelGridResolution,
NearPlaneDistance,
FarPlaneDistance,
ViewToWorld,
TopLevelGridBuffer
);
// Generate raster tiles of approximately equal work
bool bEnableIndirectionGrid = false;
FRDGBufferRef RasterTileBuffer;
FRDGBufferRef RasterTileAllocatorBuffer;
GenerateRasterTiles(
GraphBuilder,
Scene,
bEnableIndirectionGrid,
// Grid data
TopLevelGridResolution,
TopLevelGridBuffer,
// Tile data
RasterTileBuffer,
RasterTileAllocatorBuffer
);
FRDGBufferRef ExtinctionGridBuffer;
FRDGBufferRef EmissionGridBuffer;
FRDGBufferRef ScatteringGridBuffer;
RasterizeVolumesIntoFrustumVoxelGrid(
GraphBuilder,
Scene,
View,
BuildOptions,
ViewToWorld,
NearPlaneDistance,
FarPlaneDistance,
// Raster tile
RasterTileBuffer,
RasterTileAllocatorBuffer,
// Top-level grid
TopLevelGridBounds,
TopLevelGridResolution,
TopLevelGridBuffer,
// Bottom-level grid
ExtinctionGridBuffer,
EmissionGridBuffer,
ScatteringGridBuffer
);
// Create Adpative Voxel Grid uniform buffer
FFrustumVoxelGridUniformBufferParameters* UniformBufferParameters = GraphBuilder.AllocParameters<FFrustumVoxelGridUniformBufferParameters>();
{
FMatrix ViewToClip = FPerspectiveMatrix(
FMath::DegreesToRadians(View.FOV * 0.5),
TopLevelGridResolution.X,
TopLevelGridResolution.Y,
NearPlaneDistance,
FarPlaneDistance
);
FMatrix ClipToView = ViewToClip.Inverse();
UniformBufferParameters->ViewToClip = FMatrix44f(ViewToClip);
UniformBufferParameters->ClipToView = FMatrix44f(ClipToView);
FMatrix WorldToClip = WorldToView * ViewToClip;
FMatrix ClipToWorld = ClipToView * ViewToWorld;
UniformBufferParameters->WorldToClip = FMatrix44f(WorldToClip);
UniformBufferParameters->ClipToWorld = FMatrix44f(ClipToWorld);
UniformBufferParameters->WorldToView = FMatrix44f(WorldToView);
UniformBufferParameters->ViewToWorld = FMatrix44f(ViewToWorld);
UniformBufferParameters->TopLevelGridWorldBoundsMin = FVector3f(TopLevelGridBounds.Origin - TopLevelGridBounds.BoxExtent);
UniformBufferParameters->TopLevelGridWorldBoundsMax = FVector3f(TopLevelGridBounds.Origin + TopLevelGridBounds.BoxExtent);
UniformBufferParameters->TopLevelFroxelGridResolution = TopLevelGridResolution;
UniformBufferParameters->VoxelDimensions = TopLevelGridResolution;
UniformBufferParameters->bUseFrustumGrid = HeterogeneousVolumes::EnableFrustumVoxelGrid();
UniformBufferParameters->NearPlaneDepth = NearPlaneDistance;
UniformBufferParameters->FarPlaneDepth = FarPlaneDistance;
UniformBufferParameters->TanHalfFOV = HeterogeneousVolumes::CalcTanHalfFOV(View.FOV);
// Frustum assignment
{
// Near/Far plane definition is reversed when explicitly specifying clipping planes
FPlane NearPlane;
ViewToClip.GetFrustumFarPlane(NearPlane);
UniformBufferParameters->ViewFrustumPlanes[0] = FVector4f(NearPlane.X, NearPlane.Y, NearPlane.Z, NearPlane.W);
FPlane FarPlane;
ViewToClip.GetFrustumNearPlane(FarPlane);
UniformBufferParameters->ViewFrustumPlanes[1] = FVector4f(FarPlane.X, FarPlane.Y, FarPlane.Z, FarPlane.W);
FPlane LeftPlane;
ViewToClip.GetFrustumLeftPlane(LeftPlane);
UniformBufferParameters->ViewFrustumPlanes[2] = FVector4f(LeftPlane.X, LeftPlane.Y, LeftPlane.Z, LeftPlane.W);
FPlane RightPlane;
ViewToClip.GetFrustumRightPlane(RightPlane);
UniformBufferParameters->ViewFrustumPlanes[3] = FVector4f(RightPlane.X, RightPlane.Y, RightPlane.Z, RightPlane.W);
FPlane TopPlane;
ViewToClip.GetFrustumTopPlane(TopPlane);
UniformBufferParameters->ViewFrustumPlanes[4] = FVector4f(TopPlane.X, TopPlane.Y, TopPlane.Z, TopPlane.W);
FPlane BottomPlane;
ViewToClip.GetFrustumBottomPlane(BottomPlane);
UniformBufferParameters->ViewFrustumPlanes[5] = FVector4f(BottomPlane.X, BottomPlane.Y, BottomPlane.Z, BottomPlane.W);
}
UniformBufferParameters->TopLevelFroxelGridBuffer = GraphBuilder.CreateSRV(TopLevelGridBuffer);
UniformBufferParameters->ExtinctionFroxelGridBuffer = GraphBuilder.CreateSRV(ExtinctionGridBuffer);
UniformBufferParameters->EmissionFroxelGridBuffer = GraphBuilder.CreateSRV(EmissionGridBuffer);
UniformBufferParameters->ScatteringFroxelGridBuffer = GraphBuilder.CreateSRV(ScatteringGridBuffer);
}
FrustumGridUniformBuffer = GraphBuilder.CreateUniformBuffer(UniformBufferParameters);
}
void ExtractOrthoVoxelGridUniformBuffer(
FRDGBuilder& GraphBuilder,
const TRDGUniformBufferRef<FOrthoVoxelGridUniformBufferParameters>& OrthoGridUniformBuffer,
FAdaptiveOrthoGridParameterCache& AdaptiveOrthoGridParameterCache
)
{
const TRDGParameterStruct<FOrthoVoxelGridUniformBufferParameters>& Parameters = OrthoGridUniformBuffer->GetParameters();
AdaptiveOrthoGridParameterCache.TopLevelGridWorldBoundsMin = Parameters->TopLevelGridWorldBoundsMin;
AdaptiveOrthoGridParameterCache.TopLevelGridWorldBoundsMax = Parameters->TopLevelGridWorldBoundsMax;
AdaptiveOrthoGridParameterCache.TopLevelGridResolution = Parameters->TopLevelGridResolution;
AdaptiveOrthoGridParameterCache.bUseOrthoGrid = Parameters->bUseOrthoGrid;
AdaptiveOrthoGridParameterCache.bUseMajorantGrid = Parameters->bUseMajorantGrid;
AdaptiveOrthoGridParameterCache.bEnableIndirectionGrid = Parameters->bEnableIndirectionGrid;
GraphBuilder.QueueBufferExtraction(Parameters->TopLevelGridBitmaskBuffer->GetParent(), &AdaptiveOrthoGridParameterCache.TopLevelGridBitmaskBuffer);
GraphBuilder.QueueBufferExtraction(Parameters->TopLevelGridBuffer->GetParent(), &AdaptiveOrthoGridParameterCache.TopLevelGridBuffer);
GraphBuilder.QueueBufferExtraction(Parameters->IndirectionGridBuffer->GetParent(), &AdaptiveOrthoGridParameterCache.IndirectionGridBuffer);
GraphBuilder.QueueBufferExtraction(Parameters->ExtinctionGridBuffer->GetParent(), &AdaptiveOrthoGridParameterCache.ExtinctionGridBuffer);
GraphBuilder.QueueBufferExtraction(Parameters->EmissionGridBuffer->GetParent(), &AdaptiveOrthoGridParameterCache.EmissionGridBuffer);
GraphBuilder.QueueBufferExtraction(Parameters->ScatteringGridBuffer->GetParent(), &AdaptiveOrthoGridParameterCache.ScatteringGridBuffer);
GraphBuilder.QueueBufferExtraction(Parameters->MajorantGridBuffer->GetParent(), &AdaptiveOrthoGridParameterCache.MajorantGridBuffer);
}
void RegisterExternalOrthoVoxelGridUniformBuffer(
FRDGBuilder& GraphBuilder,
const FAdaptiveOrthoGridParameterCache& AdaptiveOrthoGridParameterCache,
TRDGUniformBufferRef<FOrthoVoxelGridUniformBufferParameters>& OrthoGridUniformBuffer
)
{
FOrthoVoxelGridUniformBufferParameters* UniformBufferParameters = GraphBuilder.AllocParameters<FOrthoVoxelGridUniformBufferParameters>();
{
UniformBufferParameters->TopLevelGridWorldBoundsMin = AdaptiveOrthoGridParameterCache.TopLevelGridWorldBoundsMin;
UniformBufferParameters->TopLevelGridWorldBoundsMax = AdaptiveOrthoGridParameterCache.TopLevelGridWorldBoundsMax;
UniformBufferParameters->TopLevelGridResolution = AdaptiveOrthoGridParameterCache.TopLevelGridResolution;
UniformBufferParameters->bUseOrthoGrid = AdaptiveOrthoGridParameterCache.bUseOrthoGrid;
UniformBufferParameters->bUseMajorantGrid = AdaptiveOrthoGridParameterCache.bUseMajorantGrid;
UniformBufferParameters->bEnableIndirectionGrid = AdaptiveOrthoGridParameterCache.bEnableIndirectionGrid;
UniformBufferParameters->TopLevelGridBitmaskBuffer = GraphBuilder.CreateSRV(GraphBuilder.RegisterExternalBuffer(AdaptiveOrthoGridParameterCache.TopLevelGridBitmaskBuffer));
UniformBufferParameters->TopLevelGridBuffer = GraphBuilder.CreateSRV(GraphBuilder.RegisterExternalBuffer(AdaptiveOrthoGridParameterCache.TopLevelGridBuffer));
UniformBufferParameters->IndirectionGridBuffer = GraphBuilder.CreateSRV(GraphBuilder.RegisterExternalBuffer(AdaptiveOrthoGridParameterCache.IndirectionGridBuffer));
UniformBufferParameters->ExtinctionGridBuffer = GraphBuilder.CreateSRV(GraphBuilder.RegisterExternalBuffer(AdaptiveOrthoGridParameterCache.ExtinctionGridBuffer));
UniformBufferParameters->EmissionGridBuffer = GraphBuilder.CreateSRV(GraphBuilder.RegisterExternalBuffer(AdaptiveOrthoGridParameterCache.EmissionGridBuffer));
UniformBufferParameters->ScatteringGridBuffer = GraphBuilder.CreateSRV(GraphBuilder.RegisterExternalBuffer(AdaptiveOrthoGridParameterCache.ScatteringGridBuffer));
UniformBufferParameters->MajorantGridBuffer = GraphBuilder.CreateSRV(GraphBuilder.RegisterExternalBuffer(AdaptiveOrthoGridParameterCache.MajorantGridBuffer));
}
OrthoGridUniformBuffer = GraphBuilder.CreateUniformBuffer(UniformBufferParameters);
}
TRDGUniformBufferRef<FOrthoVoxelGridUniformBufferParameters> CreateEmptyOrthoVoxelGridUniformBuffer(
FRDGBuilder& GraphBuilder
)
{
FOrthoVoxelGridUniformBufferParameters* OrthoGridUniformBufferParameters = GraphBuilder.AllocParameters<FOrthoVoxelGridUniformBufferParameters>();
{
OrthoGridUniformBufferParameters->TopLevelGridWorldBoundsMin = FVector3f(0);
OrthoGridUniformBufferParameters->TopLevelGridWorldBoundsMax = FVector3f(0);
OrthoGridUniformBufferParameters->TopLevelGridResolution = FIntVector(0);
OrthoGridUniformBufferParameters->TopLevelGridBitmaskBuffer = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FTopLevelGridBitmaskData)));
OrthoGridUniformBufferParameters->TopLevelGridBuffer = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FTopLevelGridData)));
OrthoGridUniformBufferParameters->IndirectionGridBuffer = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FTopLevelGridData)));
OrthoGridUniformBufferParameters->ExtinctionGridBuffer = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FScalarGridData)));
OrthoGridUniformBufferParameters->EmissionGridBuffer = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FVectorGridData)));
OrthoGridUniformBufferParameters->ScatteringGridBuffer = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FVectorGridData)));
OrthoGridUniformBufferParameters->bUseOrthoGrid = false;
OrthoGridUniformBufferParameters->bUseMajorantGrid = false;
OrthoGridUniformBufferParameters->bEnableIndirectionGrid = false;
OrthoGridUniformBufferParameters->MajorantGridBuffer = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FScalarGridData)));
}
return GraphBuilder.CreateUniformBuffer(OrthoGridUniformBufferParameters);
}
namespace HeterogeneousVolumes
{
TRDGUniformBufferRef<FOrthoVoxelGridUniformBufferParameters> GetOrthoVoxelGridUniformBuffer(
FRDGBuilder& GraphBuilder,
FSceneViewState* ViewState
)
{
if (ViewState && ViewState->OrthoVoxelGridUniformBuffer)
{
return ViewState->OrthoVoxelGridUniformBuffer;
}
return CreateEmptyOrthoVoxelGridUniformBuffer(GraphBuilder);
}
TRDGUniformBufferRef<FFrustumVoxelGridUniformBufferParameters> GetFrustumVoxelGridUniformBuffer(
FRDGBuilder& GraphBuilder,
FSceneViewState* ViewState
)
{
if (ViewState && ViewState->FrustumVoxelGridUniformBuffer)
{
return ViewState->FrustumVoxelGridUniformBuffer;
}
return CreateEmptyFrustumVoxelGridUniformBuffer(GraphBuilder);
}
}
void RasterizeVolumesIntoOrthoVoxelGrid(
FRDGBuilder& GraphBuilder,
const FScene* Scene,
const TArray<FViewInfo>& Views,
const TSet<FVolumetricMeshBatch>& HeterogeneousVolumesMeshBatches,
const FVoxelGridBuildOptions BuildOptions,
// Raster tile
FRDGBufferRef RasterTileBuffer,
FRDGBufferRef RasterTileAllocatorBuffer,
// Top-level grid
FBoxSphereBounds TopLevelGridBounds,
FIntVector TopLevelGridResolution,
FRDGBufferRef& TopLevelGridBuffer,
// Indirection grid
FRDGBufferRef& IndirectionGridBuffer,
// Bottom-level grid
FRDGBufferRef& ExtinctionGridBuffer,
FRDGBufferRef& EmissionGridBuffer,
FRDGBufferRef& ScatteringGridBuffer
)
{
// Setup indirect dispatch
FRDGBufferRef RasterizeBottomLevelGridIndirectArgsBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateIndirectDesc<FRHIDispatchIndirectParameters>(1),
TEXT("HeterogeneousVolume.RasterizeBottomLevelGridIndirectArgs")
);
FSetRasterizeBottomLevelGridIndirectArgs::FParameters* IndirectArgsPassParameters = GraphBuilder.AllocParameters<FSetRasterizeBottomLevelGridIndirectArgs::FParameters>();
{
IndirectArgsPassParameters->MaxDispatchThreadGroupsPerDimension = GRHIMaxDispatchThreadGroupsPerDimension;
IndirectArgsPassParameters->RasterTileAllocatorBuffer = GraphBuilder.CreateSRV(RasterTileAllocatorBuffer, PF_R32_UINT);
IndirectArgsPassParameters->RWRasterizeBottomLevelGridIndirectArgsBuffer = GraphBuilder.CreateUAV(RasterizeBottomLevelGridIndirectArgsBuffer, PF_R32_UINT);
}
const FGlobalShaderMap* GlobalShaderMap = GetGlobalShaderMap(Scene->GetFeatureLevel());
FIntVector GroupCount(1, 1, 1);
TShaderRef<FSetRasterizeBottomLevelGridIndirectArgs> ComputeShader = GlobalShaderMap->GetShader<FSetRasterizeBottomLevelGridIndirectArgs>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("SetRasterizeBottomLevelGridIndirectArgs"),
ERDGPassFlags::Compute,
ComputeShader,
IndirectArgsPassParameters,
GroupCount
);
int32 MaxBottomLevelVoxelCount = (HeterogeneousVolumes::GetMaxBottomLevelMemoryInMegabytesForOrthoGrid() * 1e6) / sizeof(FVectorGridData);
int32 BottomLevelGridBufferSize = MaxBottomLevelVoxelCount;
ExtinctionGridBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(FScalarGridData), BottomLevelGridBufferSize),
TEXT("HeterogeneousVolumes.OrthoGrid.ExtinctionGridBuffer")
);
EmissionGridBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(FVectorGridData), BottomLevelGridBufferSize),
TEXT("HeterogeneousVolumes.OrthoGrid.EmissionGridBuffer")
);
ScatteringGridBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(FVectorGridData), BottomLevelGridBufferSize),
TEXT("HeterogeneousVolumes.OrthoGrid.ScatteringGridBuffer")
);
FRDGBufferRef BottomLevelGridAllocatorBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), 2),
TEXT("HeterogeneousVolume.OrthoGrid.BottomLevelGridAllocatorBuffer")
);
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(BottomLevelGridAllocatorBuffer, PF_R32_UINT), 0);
#if 0
// Enable bottom-level voxel hashing
uint32 HashTableBufferSize = 1;
if (HeterogeneousVolumes::EnableVoxelHashing())
{
HashTableBufferSize = HeterogeneousVolumes::GetVoxelHashingMemoryInMegabytes() * 1.0e6;
}
FRDGBufferRef HashTableBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), HashTableBufferSize),
TEXT("HeterogeneousVolume.OrthoGrid.HashTableBufferSize")
);
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(HashTableBuffer, PF_R32_UINT), 0);
FRDGBufferRef HashToVoxelBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), HashTableBufferSize),
TEXT("HeterogeneousVolume.OrthoGrid.HashToVoxelBuffer")
);
#endif
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
// Build view
const FViewInfo& View = Views[ViewIndex];
FVector WorldCameraOrigin = View.ViewMatrices.GetViewOrigin();
FBoxSphereBounds WorldCameraBounds(FSphere(WorldCameraOrigin, HeterogeneousVolumes::GetMaxTraceDistance()));
for (auto MeshBatchIt = HeterogeneousVolumesMeshBatches.begin(); MeshBatchIt != HeterogeneousVolumesMeshBatches.end(); ++MeshBatchIt)
{
FVolumetricMeshBatch VolumetricMeshBatch = *MeshBatchIt;
const FMeshBatch* Mesh = VolumetricMeshBatch.Mesh;
const FPrimitiveSceneProxy* PrimitiveSceneProxy = VolumetricMeshBatch.Proxy;
const FMaterialRenderProxy* MaterialRenderProxy = Mesh->MaterialRenderProxy;
if (!ShouldRenderMeshBatchWithHeterogeneousVolumes(Mesh, PrimitiveSceneProxy, View.GetFeatureLevel()))
{
continue;
}
for (int32 VolumeIndex = 0; VolumeIndex < Mesh->Elements.Num(); ++VolumeIndex)
{
const IHeterogeneousVolumeInterface* HeterogeneousVolumeInterface = (IHeterogeneousVolumeInterface*)Mesh->Elements[VolumeIndex].UserData;
//check(HeterogeneousVolumeInterface != nullptr);
if (HeterogeneousVolumeInterface == nullptr)
{
continue;
}
const FPrimitiveSceneInfo* PrimitiveSceneInfo = PrimitiveSceneProxy->GetPrimitiveSceneInfo();
const int32 PrimitiveId = PrimitiveSceneInfo->GetIndex();
const FBoxSphereBounds LocalBoxSphereBounds = PrimitiveSceneProxy->GetLocalBounds();
const FBoxSphereBounds PrimitiveBounds = PrimitiveSceneProxy->GetBounds();
const FMaterial& Material = MaterialRenderProxy->GetMaterialWithFallback(View.GetFeatureLevel(), MaterialRenderProxy);
FRasterizeBottomLevelOrthoGridCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FRasterizeBottomLevelOrthoGridCS::FParameters>();
{
// Scene data
PassParameters->View = View.ViewUniformBuffer;
PassParameters->Scene = View.GetSceneUniforms().GetBuffer(GraphBuilder);
PassParameters->EyeAdaptationBuffer = GraphBuilder.CreateSRV(GetEyeAdaptationBuffer(GraphBuilder, View));
// Primitive data
PassParameters->PrimitiveWorldBoundsMin = FVector3f(PrimitiveBounds.Origin - PrimitiveBounds.BoxExtent);
PassParameters->PrimitiveWorldBoundsMax = FVector3f(PrimitiveBounds.Origin + PrimitiveBounds.BoxExtent);
// TODO: Convert to relative-local space
//FVector3f ViewOriginHigh = FDFVector3(View.ViewMatrices.GetViewOrigin()).High;
//FMatrix44f RelativeLocalToWorld = FDFMatrix::MakeToRelativeWorldMatrix(ViewOriginHigh, HeterogeneousVolumeInterface->GetLocalToWorld()).M;
FMatrix InstanceToLocal = HeterogeneousVolumeInterface->GetInstanceToLocal();
FMatrix LocalToWorld = HeterogeneousVolumeInterface->GetLocalToWorld();
PassParameters->LocalToWorld = FMatrix44f(InstanceToLocal * LocalToWorld);
PassParameters->WorldToLocal = FMatrix44f(PassParameters->LocalToWorld.Inverse());
FMatrix LocalToInstance = InstanceToLocal.Inverse();
FBoxSphereBounds InstanceBoxSphereBounds = LocalBoxSphereBounds.TransformBy(LocalToInstance);
PassParameters->LocalBoundsOrigin = FVector3f(InstanceBoxSphereBounds.Origin);
PassParameters->LocalBoundsExtent = FVector3f(InstanceBoxSphereBounds.BoxExtent);
PassParameters->PrimitiveId = PrimitiveId;
// Volume data
PassParameters->TopLevelGridResolution = TopLevelGridResolution;
PassParameters->TopLevelGridWorldBoundsMin = FVector3f(TopLevelGridBounds.Origin - TopLevelGridBounds.BoxExtent);
PassParameters->TopLevelGridWorldBoundsMax = FVector3f(TopLevelGridBounds.Origin + TopLevelGridBounds.BoxExtent);
// Sampling data
FBlueNoise BlueNoise = GetBlueNoiseGlobalParameters();
PassParameters->BlueNoise = CreateUniformBufferImmediate(BlueNoise, EUniformBufferUsage::UniformBuffer_SingleDraw);
// Unify with "object" definition??
PassParameters->PrimitiveWorldBoundsMin = FVector3f(PrimitiveBounds.Origin - PrimitiveBounds.BoxExtent);
PassParameters->PrimitiveWorldBoundsMax = FVector3f(PrimitiveBounds.Origin + PrimitiveBounds.BoxExtent);
PassParameters->BottomLevelGridBufferSize = BottomLevelGridBufferSize;
// Raster tile data
PassParameters->RasterTileAllocatorBuffer = GraphBuilder.CreateSRV(RasterTileAllocatorBuffer, PF_R32_UINT);
PassParameters->RasterTileBuffer = GraphBuilder.CreateSRV(RasterTileBuffer);
// Indirect args
PassParameters->IndirectArgs = RasterizeBottomLevelGridIndirectArgsBuffer;
// Sampling mode
PassParameters->bJitter = BuildOptions.bJitter;
PassParameters->bSampleAtVertices = HeterogeneousVolumes::EnableLinearInterpolation();
// Grid data
PassParameters->TopLevelGridBuffer = GraphBuilder.CreateSRV(TopLevelGridBuffer);
PassParameters->RWBottomLevelGridAllocatorBuffer = GraphBuilder.CreateUAV(BottomLevelGridAllocatorBuffer, PF_R32_UINT);
PassParameters->RWTopLevelGridBuffer = GraphBuilder.CreateUAV(TopLevelGridBuffer);
PassParameters->RWExtinctionGridBuffer = GraphBuilder.CreateUAV(ExtinctionGridBuffer);
PassParameters->RWEmissionGridBuffer = GraphBuilder.CreateUAV(EmissionGridBuffer);
PassParameters->RWScatteringGridBuffer = GraphBuilder.CreateUAV(ScatteringGridBuffer);
// Indirection Grid
PassParameters->FixedBottomLevelResolution = HeterogeneousVolumes::GetBottomLevelGridResolution();
PassParameters->RWIndirectionGridBuffer = GraphBuilder.CreateUAV(IndirectionGridBuffer);
#if 0
// Hash table
if (HeterogeneousVolumes::EnableVoxelHashing())
{
PassParameters->RWHashTable = GraphBuilder.CreateUAV(HashTableBuffer);
PassParameters->RWHashToVoxelBuffer = GraphBuilder.CreateUAV(HashToVoxelBuffer);
PassParameters->HashTableSize = HashTableBufferSize;
}
#endif
PassParameters->HomogeneousThreshold = HeterogeneousVolumes::GetHomogeneousAggregationThreshold();
}
GraphBuilder.AddPass(
RDG_EVENT_NAME("RasterizeBottomLevelGrid"),
PassParameters,
ERDGPassFlags::Compute,
// Why is scene explicitly copied?
[PassParameters, Scene, &View, MaterialRenderProxy, &Material](FRDGAsyncTask, FRHIComputeCommandList& RHICmdList)
{
FRasterizeBottomLevelOrthoGridCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FRasterizeBottomLevelOrthoGridCS::FEnableIndirectionGrid>(HeterogeneousVolumes::EnableIndirectionGrid());
PermutationVector.Set<FRasterizeBottomLevelOrthoGridCS::FEnableHomogeneousAggregation>(HeterogeneousVolumes::EnableHomogeneousAggregation());
TShaderRef<FRasterizeBottomLevelOrthoGridCS> ComputeShader = Material.GetShader<FRasterizeBottomLevelOrthoGridCS>(&FLocalVertexFactory::StaticType, PermutationVector, false);
if (!ComputeShader.IsNull())
{
ClearUnusedGraphResources(ComputeShader, PassParameters);
FMeshDrawShaderBindings ShaderBindings;
UE::MeshPassUtils::SetupComputeBindings(ComputeShader, Scene, Scene->GetFeatureLevel(), nullptr, *MaterialRenderProxy, Material, ShaderBindings);
UE::MeshPassUtils::DispatchIndirect(RHICmdList, ComputeShader, ShaderBindings, *PassParameters, PassParameters->IndirectArgs->GetIndirectRHICallBuffer(), 0);
}
}
);
}
}
}
}
bool ShouldAddToVoxelGrid(const FPrimitiveSceneProxy* Proxy, const FViewInfo& View, const FVoxelGridBuildOptions& BuildOptions)
{
// TODO: Should only add element if it is within the MaxTraceDistance
switch (BuildOptions.VoxelGridBuildMode)
{
default:
case EVoxelGridBuildMode::PathTracing:
return Proxy->IsShown(&View);
case EVoxelGridBuildMode::Shadows:
return Proxy->IsShadowCast(&View);
}
}
void CollectHeterogeneousVolumeMeshBatches(
FRDGBuilder& GraphBuilder,
const FVoxelGridBuildOptions& BuildOptions,
const FScene* Scene,
const TArray<FViewInfo>& Views,
const TArray<FVisibleLightInfo, SceneRenderingAllocator> VisibleLightInfos,
TSet<FVolumetricMeshBatch>& HeterogeneousVolumesMeshBatches
)
{
HeterogeneousVolumesMeshBatches.Reset();
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
for (int32 MeshBatchIndex = 0; MeshBatchIndex < View.HeterogeneousVolumesMeshBatches.Num(); ++MeshBatchIndex)
{
const FVolumetricMeshBatch& MeshBatch = View.HeterogeneousVolumesMeshBatches[MeshBatchIndex];
if (ShouldAddToVoxelGrid(MeshBatch.Proxy, View, BuildOptions))
{
HeterogeneousVolumesMeshBatches.FindOrAdd(MeshBatch);
}
}
}
TArray<FLightSceneInfoCompact, TInlineAllocator<64>> LightSceneInfoCompact;
for (auto LightIt = Scene->Lights.CreateConstIterator(); LightIt; ++LightIt)
{
// TODO: Use global bounds information..
//if (LightIt->AffectsPrimitive(HeterogeneousVolumeInterface->GetBounds(), HeterogeneousVolumeInterface->GetPrimitiveSceneProxy()))
// TODO: For view / light masking, should we check if the light is visible in any view, instead of just the first view? What kind of artifacts would
// result with mixed settings on views? For now, we'll assume all views in a family have the same channel mask (true for scene captures where
// view / light masks are currently exposed), so this doesn't matter.
if (HeterogeneousVolumes::SupportsShadowForLightType(LightIt->LightType) &&
(LightIt->LightSceneInfo->Proxy->GetViewLightingChannelMask() & Views[0].ViewLightingChannelMask))
{
LightSceneInfoCompact.Add(*LightIt);
}
}
// TODO: Temporarily disable per-light primitive gathering because it causes a crash with VoxelGrid builder
if (HeterogeneousVolumes::GetShadowMode() == HeterogeneousVolumes::EShadowMode::VoxelGrid)
{
return;
}
int32 NumPasses = LightSceneInfoCompact.Num();
for (int32 PassIndex = 0; PassIndex < NumPasses; ++PassIndex)
{
const FLightSceneInfo* LightSceneInfo = LightSceneInfoCompact[PassIndex].LightSceneInfo;
if (LightSceneInfo->Proxy->CastsVolumetricShadow())
{
const FVisibleLightInfo* VisibleLightInfo = &VisibleLightInfos[LightSceneInfo->Id];
check(VisibleLightInfo);
for (int32 ShadowIndex = 0; ShadowIndex < VisibleLightInfo->ShadowsToProject.Num(); ++ShadowIndex)
{
const FProjectedShadowInfo* ProjectedShadowInfo = HeterogeneousVolumes::GetProjectedShadowInfo(VisibleLightInfo, ShadowIndex);
if (ProjectedShadowInfo != nullptr)
{
const TArray<FMeshBatchAndRelevance, SceneRenderingAllocator>& MeshBatches = ProjectedShadowInfo->GetDynamicSubjectHeterogeneousVolumeMeshElements();
for (int32 MeshBatchIndex = 0; MeshBatchIndex < MeshBatches.Num(); ++MeshBatchIndex)
{
const FMeshBatchAndRelevance& MeshBatch = MeshBatches[MeshBatchIndex];
if (ShouldAddToVoxelGrid(MeshBatch.PrimitiveSceneProxy, *ProjectedShadowInfo->ShadowDepthView, BuildOptions))
{
HeterogeneousVolumesMeshBatches.FindOrAdd(FVolumetricMeshBatch(MeshBatch.Mesh, MeshBatch.PrimitiveSceneProxy));
}
}
}
}
}
}
}
void BuildOrthoVoxelGrid(
FRDGBuilder& GraphBuilder,
const FScene* Scene,
/*const*/ TArray<FViewInfo>& Views,
const TArray<FVisibleLightInfo, SceneRenderingAllocator>& VisibleLightInfos,
const FVoxelGridBuildOptions& BuildOptions,
TRDGUniformBufferRef<FOrthoVoxelGridUniformBufferParameters>& OrthoGridUniformBuffer
)
{
if (!ShouldRenderHeterogeneousVolumes(Scene) || !HeterogeneousVolumes::EnableOrthoVoxelGrid() || !BuildOptions.bBuildOrthoGrid)
{
OrthoGridUniformBuffer = CreateEmptyOrthoVoxelGridUniformBuffer(GraphBuilder);
return;
}
check(!Views.IsEmpty());
RDG_EVENT_SCOPE(GraphBuilder, "Ortho Grid Build");
TSet<FVolumetricMeshBatch> HeterogeneousVolumesMeshBatches;
CollectHeterogeneousVolumeMeshBatches(
GraphBuilder,
BuildOptions,
Scene,
Views,
VisibleLightInfos,
HeterogeneousVolumesMeshBatches
);
if (HeterogeneousVolumesMeshBatches.IsEmpty())
{
OrthoGridUniformBuffer = CreateEmptyOrthoVoxelGridUniformBuffer(GraphBuilder);
return;
}
// Collect global bounds
FBoxSphereBounds::Builder TopLevelGridBoundsBuilder;
float GlobalMinimumVoxelSize;
CalcGlobalBoundsAndMinimumVoxelSize(
GraphBuilder,
Views,
HeterogeneousVolumesMeshBatches,
BuildOptions,
TopLevelGridBoundsBuilder,
GlobalMinimumVoxelSize
);
if (!TopLevelGridBoundsBuilder.IsValid())
{
OrthoGridUniformBuffer = CreateEmptyOrthoVoxelGridUniformBuffer(GraphBuilder);
return;
}
FBoxSphereBounds TopLevelGridBounds(TopLevelGridBoundsBuilder);
// Determine top-level grid resolution
FIntVector TopLevelGridResolution;
CalculateTopLevelGridResolution(
TopLevelGridBounds,
GlobalMinimumVoxelSize,
TopLevelGridResolution
);
// Calculate the preferred voxel size for each bottom-level grid in a top-level cell
FRDGBufferRef TopLevelGridBuffer;
CalculateVoxelSize(
GraphBuilder,
Scene,
Views,
HeterogeneousVolumesMeshBatches,
BuildOptions,
TopLevelGridBounds,
TopLevelGridResolution,
TopLevelGridBuffer
);
// Allocate bottom-level grid
FRDGBufferRef IndirectionGridBuffer;
MarkTopLevelGrid(
GraphBuilder,
Scene,
// Grid data
TopLevelGridBounds,
TopLevelGridResolution,
TopLevelGridBuffer,
IndirectionGridBuffer
);
// Generate raster tiles
FRDGBufferRef RasterTileBuffer;
FRDGBufferRef RasterTileAllocatorBuffer;
GenerateRasterTiles(
GraphBuilder,
Scene,
HeterogeneousVolumes::EnableIndirectionGrid(),
// Grid data
TopLevelGridResolution,
TopLevelGridBuffer,
// Tile data
RasterTileBuffer,
RasterTileAllocatorBuffer
);
// Volume rasterization
FRDGBufferRef ExtinctionGridBuffer;
FRDGBufferRef EmissionGridBuffer;
FRDGBufferRef ScatteringGridBuffer;
RasterizeVolumesIntoOrthoVoxelGrid(
GraphBuilder,
Scene,
Views,
HeterogeneousVolumesMeshBatches,
BuildOptions,
// Tile data
RasterTileBuffer,
RasterTileAllocatorBuffer,
// Grid data
TopLevelGridBounds,
TopLevelGridResolution,
TopLevelGridBuffer,
IndirectionGridBuffer,
ExtinctionGridBuffer,
EmissionGridBuffer,
ScatteringGridBuffer
);
FRDGBufferRef TopLevelGridBitmaskBuffer;
CreateTopLevelBitmask(GraphBuilder, Views, TopLevelGridResolution, TopLevelGridBuffer, TopLevelGridBitmaskBuffer);
FRDGBufferRef MajorantGridBuffer;
BuildMajorantVoxelGrid(GraphBuilder, Scene, TopLevelGridResolution, TopLevelGridBuffer, IndirectionGridBuffer, ExtinctionGridBuffer, MajorantGridBuffer);
// Create Adpative Voxel Grid uniform buffer
FOrthoVoxelGridUniformBufferParameters* OrthoGridUniformBufferParameters = GraphBuilder.AllocParameters<FOrthoVoxelGridUniformBufferParameters>();
{
OrthoGridUniformBufferParameters->TopLevelGridWorldBoundsMin = FVector3f(TopLevelGridBounds.Origin - TopLevelGridBounds.BoxExtent);
OrthoGridUniformBufferParameters->TopLevelGridWorldBoundsMax = FVector3f(TopLevelGridBounds.Origin + TopLevelGridBounds.BoxExtent);
OrthoGridUniformBufferParameters->TopLevelGridResolution = TopLevelGridResolution;
OrthoGridUniformBufferParameters->TopLevelGridBitmaskBuffer = GraphBuilder.CreateSRV(TopLevelGridBitmaskBuffer);
OrthoGridUniformBufferParameters->TopLevelGridBuffer = GraphBuilder.CreateSRV(TopLevelGridBuffer);
OrthoGridUniformBufferParameters->IndirectionGridBuffer = GraphBuilder.CreateSRV(IndirectionGridBuffer);
OrthoGridUniformBufferParameters->ExtinctionGridBuffer = GraphBuilder.CreateSRV(ExtinctionGridBuffer);
OrthoGridUniformBufferParameters->EmissionGridBuffer = GraphBuilder.CreateSRV(EmissionGridBuffer);
OrthoGridUniformBufferParameters->ScatteringGridBuffer = GraphBuilder.CreateSRV(ScatteringGridBuffer);
OrthoGridUniformBufferParameters->bUseOrthoGrid = HeterogeneousVolumes::EnableOrthoVoxelGrid();
OrthoGridUniformBufferParameters->bUseMajorantGrid = HeterogeneousVolumes::EnableMajorantGrid();
OrthoGridUniformBufferParameters->bEnableIndirectionGrid = HeterogeneousVolumes::EnableIndirectionGrid();
OrthoGridUniformBufferParameters->MajorantGridBuffer = GraphBuilder.CreateSRV(MajorantGridBuffer);
}
OrthoGridUniformBuffer = GraphBuilder.CreateUniformBuffer(OrthoGridUniformBufferParameters);
}
void RenderTransmittanceWithVoxelGrid(
FRDGBuilder& GraphBuilder,
// Scene data
const FSceneTextures& SceneTextures,
FScene* Scene,
FViewInfo& View,
const TRDGUniformBufferRef<FOrthoVoxelGridUniformBufferParameters>& OrthoGridUniformBuffer,
const TRDGUniformBufferRef<FFrustumVoxelGridUniformBufferParameters>& FrustumGridUniformBuffer,
// Output
FRDGTextureRef& HeterogeneousVolumeRadiance
)
{
FIntVector GroupCount = FComputeShaderUtils::GetGroupCount(HeterogeneousVolumes::GetScaledViewRect(View.ViewRect), FRenderTransmittanceWithVoxelGridCS::GetThreadGroupSize2D());
int32 BufferSize = View.ViewRect.Width() * View.ViewRect.Height();
FRDGBufferRef DebugBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(FRenderDebugData), BufferSize),
TEXT("HeterogeneousVolumes.RenderDebugData")
);
FRenderTransmittanceWithVoxelGridCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FRenderTransmittanceWithVoxelGridCS::FParameters>();
{
// Scene data
PassParameters->View = View.ViewUniformBuffer;
PassParameters->SceneTextures = GetSceneTextureParameters(GraphBuilder, SceneTextures);
// Ray data
PassParameters->bJitter = HeterogeneousVolumes::ShouldJitter();
PassParameters->MaxTraceDistance = HeterogeneousVolumes::GetMaxTraceDistance();
// Volume data
PassParameters->OrthoGridUniformBuffer = OrthoGridUniformBuffer;
PassParameters->FrustumGridUniformBuffer = FrustumGridUniformBuffer;
// Dispatch data
PassParameters->GroupCount = GroupCount;
// Output
PassParameters->RWLightingTexture = GraphBuilder.CreateUAV(HeterogeneousVolumeRadiance);
PassParameters->RWDebugBuffer = GraphBuilder.CreateUAV(DebugBuffer);
}
FRenderTransmittanceWithVoxelGridCS::FPermutationDomain PermutationVector;
int32 DebugMode = FMath::Clamp(HeterogeneousVolumes::GetDebugMode() - 1, 0, FRenderTransmittanceWithVoxelGridCS::FDebugMode::MaxValue);
PermutationVector.Set<FRenderTransmittanceWithVoxelGridCS::FDebugMode>(DebugMode);
TShaderRef<FRenderTransmittanceWithVoxelGridCS> ComputeShader = View.ShaderMap->GetShader<FRenderTransmittanceWithVoxelGridCS>(PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("RenderTransmittanceWithVoxelGridCS"),
ERDGPassFlags::Compute | ERDGPassFlags::NeverCull,
ComputeShader,
PassParameters,
GroupCount
);
}
struct FVolumetricShadowMapDebugData
{
FVector3f LightRayStart;
FVector3f LightRayEnd;
FVector3f RayOrigin;
FVector3f RayEnd;
float HitSpan[2];
};
class FRenderVolumetricShadowMapForLightWithVoxelGridCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FRenderVolumetricShadowMapForLightWithVoxelGridCS);
SHADER_USE_PARAMETER_STRUCT(FRenderVolumetricShadowMapForLightWithVoxelGridCS, FGlobalShader);
class FUseAVSMCompression : SHADER_PERMUTATION_BOOL("USE_AVSM_COMPRESSION");
class FIsOfflineRender : SHADER_PERMUTATION_INT("IS_OFFLINE_RENDER", 2);
using FPermutationDomain = TShaderPermutationDomain<FUseAVSMCompression, FIsOfflineRender>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
// Scene data
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureParameters, SceneTextures)
SHADER_PARAMETER_STRUCT_REF(FBlueNoise, BlueNoise)
// Shadow data
SHADER_PARAMETER(float, ShadowStepSize)
SHADER_PARAMETER(float, ShadowStepFactor)
// Volumetric Shadow Map data
SHADER_PARAMETER(FVector3f, TranslatedWorldOrigin)
SHADER_PARAMETER(FIntPoint, ShadowResolution)
SHADER_PARAMETER(int, MaxSampleCount)
SHADER_PARAMETER(int, StochasticFilteringMode)
SHADER_PARAMETER(float, AbsoluteErrorThreshold)
SHADER_PARAMETER(float, RelativeErrorThreshold)
SHADER_PARAMETER(int, NumShadowMatrices)
SHADER_PARAMETER_ARRAY(FMatrix44f, TranslatedWorldToShadow, [6])
SHADER_PARAMETER_ARRAY(FMatrix44f, ShadowToTranslatedWorld, [6])
// Volume data
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FOrthoVoxelGridUniformBufferParameters, OrthoGridUniformBuffer)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FFrustumVoxelGridUniformBufferParameters, FrustumGridUniformBuffer)
// Ray data
SHADER_PARAMETER(float, MaxTraceDistance)
SHADER_PARAMETER(int, MaxStepCount)
SHADER_PARAMETER(int, bJitter)
// Dispatch data
SHADER_PARAMETER(FIntVector, GroupCount)
SHADER_PARAMETER(int, ShadowDebugTweak)
// Output
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<int>, RWVolumetricShadowLinkedListAllocatorBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FAVSMLinkedListPackedData>, RWVolumetricShadowLinkedListBuffer)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, RWBeerShadowMapTexture)
// Debug
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FVolumetricShadowMapDebugData>, RWDebugBuffer)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(
const FGlobalShaderPermutationParameters& Parameters
)
{
return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform);
}
static void ModifyCompilationEnvironment(
const FGlobalShaderPermutationParameters& Parameters,
FShaderCompilerEnvironment& OutEnvironment
)
{
FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_1D"), GetThreadGroupSize1D());
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_2D"), GetThreadGroupSize2D());
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_3D"), GetThreadGroupSize3D());
// Temporary disabling..
OutEnvironment.SetDefine(TEXT("DIM_USE_TRANSMITTANCE_VOLUME"), 0);
OutEnvironment.SetDefine(TEXT("DIM_USE_INSCATTERING_VOLUME"), 0);
OutEnvironment.SetDefine(TEXT("DIM_USE_LUMEN_GI"), 0);
OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads);
}
static int32 GetThreadGroupSize1D() { return GetThreadGroupSize3D() * GetThreadGroupSize3D() * GetThreadGroupSize3D(); }
static int32 GetThreadGroupSize2D() { return 8; }
static int32 GetThreadGroupSize3D() { return 4; }
};
IMPLEMENT_GLOBAL_SHADER(FRenderVolumetricShadowMapForLightWithVoxelGridCS, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesVoxelGridShadows.usf", "RenderVolumetricShadowMapForLightWithVoxelGridCS", SF_Compute);
class FCompressVolumetricShadowMapCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FCompressVolumetricShadowMapCS);
SHADER_USE_PARAMETER_STRUCT(FCompressVolumetricShadowMapCS, FGlobalShader);
class FUseAVSMCompression : SHADER_PERMUTATION_BOOL("USE_AVSM_COMPRESSION");
using FPermutationDomain = TShaderPermutationDomain<FUseAVSMCompression>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
// Input
SHADER_PARAMETER(FIntPoint, ShadowResolution)
SHADER_PARAMETER(int, MaxSampleCount)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<FAVSMLinkedListPackedData>, VolumetricShadowLinkedListBuffer)
// Output
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<int>, RWVolumetricShadowIndirectionAllocatorBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FAVSMIndirectionPackedData>, RWVolumetricShadowIndirectionBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FAVSMSamplePackedData>, RWVolumetricShadowTransmittanceBuffer)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(
const FGlobalShaderPermutationParameters& Parameters
)
{
return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform);
}
static void ModifyCompilationEnvironment(
const FGlobalShaderPermutationParameters& Parameters,
FShaderCompilerEnvironment& OutEnvironment
)
{
FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_1D"), GetThreadGroupSize1D());
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_2D"), GetThreadGroupSize2D());
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_3D"), GetThreadGroupSize3D());
// Temporary disabling..
OutEnvironment.SetDefine(TEXT("DIM_USE_TRANSMITTANCE_VOLUME"), 0);
OutEnvironment.SetDefine(TEXT("DIM_USE_INSCATTERING_VOLUME"), 0);
OutEnvironment.SetDefine(TEXT("DIM_USE_LUMEN_GI"), 0);
OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads);
}
static int32 GetThreadGroupSize1D() { return GetThreadGroupSize3D() * GetThreadGroupSize3D() * GetThreadGroupSize3D(); }
static int32 GetThreadGroupSize2D() { return 8; }
static int32 GetThreadGroupSize3D() { return 4; }
};
IMPLEMENT_GLOBAL_SHADER(FCompressVolumetricShadowMapCS, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesVoxelGridShadows.usf", "CompressVolumetricShadowMapCS", SF_Compute);
class FCombineVolumetricShadowMapsCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FCombineVolumetricShadowMapsCS);
SHADER_USE_PARAMETER_STRUCT(FCombineVolumetricShadowMapsCS, FGlobalShader);
class FUseAVSMCompression : SHADER_PERMUTATION_BOOL("USE_AVSM_COMPRESSION");
using FPermutationDomain = TShaderPermutationDomain<FUseAVSMCompression>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
// Input
SHADER_PARAMETER(FIntPoint, ShadowResolution)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<FAVSMLinkedListPackedData>, VolumetricShadowLinkedListBuffer0)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<FAVSMLinkedListPackedData>, VolumetricShadowLinkedListBuffer1)
// Volumetric Shadow Map data
SHADER_PARAMETER(int, MaxSampleCount)
SHADER_PARAMETER(float, AbsoluteErrorThreshold)
SHADER_PARAMETER(float, RelativeErrorThreshold)
// Output
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FAVSMLinkedListPackedData>, RWVolumetricShadowLinkedListBuffer)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(
const FGlobalShaderPermutationParameters& Parameters
)
{
return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform);
}
static void ModifyCompilationEnvironment(
const FGlobalShaderPermutationParameters& Parameters,
FShaderCompilerEnvironment& OutEnvironment
)
{
FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_1D"), GetThreadGroupSize1D());
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_2D"), GetThreadGroupSize2D());
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_3D"), GetThreadGroupSize3D());
// Temporary disabling..
OutEnvironment.SetDefine(TEXT("DIM_USE_TRANSMITTANCE_VOLUME"), 0);
OutEnvironment.SetDefine(TEXT("DIM_USE_INSCATTERING_VOLUME"), 0);
OutEnvironment.SetDefine(TEXT("DIM_USE_LUMEN_GI"), 0);
OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads);
}
static int32 GetThreadGroupSize1D() { return GetThreadGroupSize3D() * GetThreadGroupSize3D() * GetThreadGroupSize3D(); }
static int32 GetThreadGroupSize2D() { return 8; }
static int32 GetThreadGroupSize3D() { return 4; }
};
IMPLEMENT_GLOBAL_SHADER(FCombineVolumetricShadowMapsCS, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesVoxelGridShadows.usf", "CombineVolumetricShadowMapsCS", SF_Compute);
class FConvertBeerLawShadowMapToVolumetricShadowMapCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FConvertBeerLawShadowMapToVolumetricShadowMapCS);
SHADER_USE_PARAMETER_STRUCT(FConvertBeerLawShadowMapToVolumetricShadowMapCS, FGlobalShader);
using FPermutationDomain = TShaderPermutationDomain<>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
// Input
SHADER_PARAMETER(FIntPoint, ShadowResolution)
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D<float4>, BeerShadowMapTexture)
// Output
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<int>, RWVolumetricShadowIndirectionAllocatorBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FAVSMIndirectionPackedData>, RWVolumetricShadowIndirectionBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FAVSMSamplePackedData>, RWVolumetricShadowTransmittanceBuffer)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(
const FGlobalShaderPermutationParameters& Parameters
)
{
return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform);
}
static void ModifyCompilationEnvironment(
const FGlobalShaderPermutationParameters& Parameters,
FShaderCompilerEnvironment& OutEnvironment
)
{
FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_1D"), GetThreadGroupSize1D());
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_2D"), GetThreadGroupSize2D());
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_3D"), GetThreadGroupSize3D());
OutEnvironment.SetDefine(TEXT("DIM_USE_TRANSMITTANCE_VOLUME"), 0);
OutEnvironment.SetDefine(TEXT("DIM_USE_INSCATTERING_VOLUME"), 0);
OutEnvironment.SetDefine(TEXT("DIM_USE_LUMEN_GI"), 0);
OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads);
}
static int32 GetThreadGroupSize1D() { return GetThreadGroupSize3D() * GetThreadGroupSize3D() * GetThreadGroupSize3D(); }
static int32 GetThreadGroupSize2D() { return 8; }
static int32 GetThreadGroupSize3D() { return 4; }
};
IMPLEMENT_GLOBAL_SHADER(FConvertBeerLawShadowMapToVolumetricShadowMapCS, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesVoxelGridShadows.usf", "ConvertBeerLawShadowMapToVolumetricShadowMapCS", SF_Compute);
void ConvertBeerLawShadowMapToVolumetricShadowMap(
FRDGBuilder& GraphBuilder,
FViewInfo& View,
//FIntVector GroupCount,
// Input
FIntPoint ShadowMapResolution,
FRDGTextureRef BeerShadowMapTexture,
// Output
FRDGBufferRef& VolumetricShadowIndirectionBuffer,
FRDGBufferRef& VolumetricShadowTransmittanceBuffer
)
{
FIntVector GroupCount = FIntVector(1);
GroupCount.X = FMath::DivideAndRoundUp(ShadowMapResolution.X, FConvertBeerLawShadowMapToVolumetricShadowMapCS::GetThreadGroupSize2D());
GroupCount.Y = FMath::DivideAndRoundUp(ShadowMapResolution.X, FConvertBeerLawShadowMapToVolumetricShadowMapCS::GetThreadGroupSize2D());
int32 ThreadGroupSize2D = FConvertBeerLawShadowMapToVolumetricShadowMapCS::GetThreadGroupSize2D();
int32 VolumetricShadowPixelCount = GroupCount.X * ThreadGroupSize2D * GroupCount.Y * ThreadGroupSize2D;
FRDGBufferRef VolumetricShadowIndirectionAllocatorBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), 1),
TEXT("HeterogeneousVolume.VolumetricShadowIndirectionAllocatorBuffer")
);
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(VolumetricShadowIndirectionAllocatorBuffer, PF_R32_UINT), 0);
VolumetricShadowIndirectionBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(FAVSMIndirectionPackedData), VolumetricShadowPixelCount),
TEXT("HeterogeneousVolume.VolumetricShadowIndirectionBuffer")
);
// Beer-Lambert Shadow Maps have two samples
const int32 MaxSampleCount = 2;
int32 TopLevelSampleCount = FMath::CeilToInt(MaxSampleCount / 4.0);
int32 VolumetricShadowTransmittanceMaxCount = VolumetricShadowPixelCount * (Align(TopLevelSampleCount, 4) + Align(MaxSampleCount, 4));
VolumetricShadowTransmittanceBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(FAVSMSamplePackedData), VolumetricShadowTransmittanceMaxCount),
TEXT("HeterogeneousVolume.VolumetricShadowTransmittanceBuffer")
);
FConvertBeerLawShadowMapToVolumetricShadowMapCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FConvertBeerLawShadowMapToVolumetricShadowMapCS::FParameters>();
{
// Input
PassParameters->ShadowResolution = ShadowMapResolution;
PassParameters->BeerShadowMapTexture = GraphBuilder.CreateSRV(BeerShadowMapTexture);
// Output
PassParameters->RWVolumetricShadowIndirectionAllocatorBuffer = GraphBuilder.CreateUAV(VolumetricShadowIndirectionAllocatorBuffer, PF_R32_UINT);
PassParameters->RWVolumetricShadowIndirectionBuffer = GraphBuilder.CreateUAV(VolumetricShadowIndirectionBuffer);
PassParameters->RWVolumetricShadowTransmittanceBuffer = GraphBuilder.CreateUAV(VolumetricShadowTransmittanceBuffer);
}
FConvertBeerLawShadowMapToVolumetricShadowMapCS::FPermutationDomain PermutationVector;
TShaderRef<FConvertBeerLawShadowMapToVolumetricShadowMapCS> ComputeShader = View.ShaderMap->GetShader<FConvertBeerLawShadowMapToVolumetricShadowMapCS>(PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
// Add Light name..
RDG_EVENT_NAME("FConvertBeerLawShadowMapToVolumetricShadowMapCS"),
ERDGPassFlags::Compute,
ComputeShader,
PassParameters,
GroupCount
);
}
void CreateAdaptiveVolumetricShadowMapUniformBufferParameters(
FRDGBuilder& GraphBuilder,
const FVector3f& TranslatedWorldOrigin,
const FVector4f& TranslatedWorldPlane,
const FMatrix44f* TranslatedWorldToShadow,
FIntPoint VolumetricShadowMapResolution,
float VolumetricShadowMapDownsampleFactor,
int32 NumShadowMatrices,
uint32 VolumetricShadowMapMaxSampleCount,
bool bIsDirectionalLight,
FRDGBufferRef VolumetricShadowMapLinkedListBuffer,
FRDGBufferRef VolumetricShadowMapIndirectionBuffer,
FRDGBufferRef VolumetricShadowMapSampleBuffer,
FAdaptiveVolumetricShadowMapUniformBufferParameters*& Parameters
)
{
Parameters = GraphBuilder.AllocParameters<FAdaptiveVolumetricShadowMapUniformBufferParameters>();
{
Parameters->NumShadowMatrices = NumShadowMatrices;
for (int32 i = 0; i < NumShadowMatrices; ++i)
{
Parameters->TranslatedWorldToShadow[i] = TranslatedWorldToShadow[i];
}
Parameters->TranslatedWorldOrigin = TranslatedWorldOrigin;
Parameters->TranslatedWorldPlane = TranslatedWorldPlane;
Parameters->Resolution = VolumetricShadowMapResolution;
Parameters->DownsampleFactor = VolumetricShadowMapDownsampleFactor;
Parameters->MaxSampleCount = VolumetricShadowMapMaxSampleCount;
Parameters->bIsEmpty = (VolumetricShadowMapResolution == FIntPoint::ZeroValue);
Parameters->bIsDirectionalLight = bIsDirectionalLight;
Parameters->LinkedListBuffer = GraphBuilder.CreateSRV(VolumetricShadowMapLinkedListBuffer);
Parameters->IndirectionBuffer = GraphBuilder.CreateSRV(VolumetricShadowMapIndirectionBuffer);
Parameters->SampleBuffer = GraphBuilder.CreateSRV(VolumetricShadowMapSampleBuffer);
Parameters->RadianceTexture = GraphBuilder.CreateSRV(GSystemTextures.GetBlackDummy(GraphBuilder));
// Test
Parameters->TextureSampler = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
}
}
void CreateAdaptiveVolumetricShadowMapUniformBuffer(
FRDGBuilder& GraphBuilder,
const FVector3f& TranslatedWorldOrigin,
const FVector4f& TranslatedWorldPlane,
const FMatrix44f* TranslatedWorldToShadow,
FIntPoint VolumetricShadowMapResolution,
float VolumetricShadowMapDownsampleFactor,
int32 NumShadowMatrices,
uint32 VolumetricShadowMapMaxSampleCount,
bool bIsDirectionalLight,
FRDGBufferRef VolumetricShadowMapLinkedListBuffer,
FRDGBufferRef VolumetricShadowMapIndirectionBuffer,
FRDGBufferRef VolumetricShadowMapSampleBuffer,
TRDGUniformBufferRef<FAdaptiveVolumetricShadowMapUniformBufferParameters>& AdaptiveVolumetricShadowMapUniformBuffer
)
{
FAdaptiveVolumetricShadowMapUniformBufferParameters* UniformBufferParameters;
CreateAdaptiveVolumetricShadowMapUniformBufferParameters(
GraphBuilder,
TranslatedWorldOrigin,
TranslatedWorldPlane,
TranslatedWorldToShadow,
VolumetricShadowMapResolution,
VolumetricShadowMapDownsampleFactor,
NumShadowMatrices,
VolumetricShadowMapMaxSampleCount,
bIsDirectionalLight,
VolumetricShadowMapLinkedListBuffer,
VolumetricShadowMapIndirectionBuffer,
VolumetricShadowMapSampleBuffer,
UniformBufferParameters
);
AdaptiveVolumetricShadowMapUniformBuffer = GraphBuilder.CreateUniformBuffer(UniformBufferParameters);
}
namespace HeterogeneousVolumes
{
TRDGUniformBufferRef<FAdaptiveVolumetricShadowMapUniformBufferParameters> CreateEmptyAdaptiveVolumetricShadowMapUniformBuffer(
FRDGBuilder& GraphBuilder
)
{
FAdaptiveVolumetricShadowMapUniformBufferParameters* UniformBufferParameters = GraphBuilder.AllocParameters<FAdaptiveVolumetricShadowMapUniformBufferParameters>();
{
UniformBufferParameters->NumShadowMatrices = 1;
for (int32 i = 0; i < UniformBufferParameters->NumShadowMatrices; ++i)
{
UniformBufferParameters->TranslatedWorldToShadow[i] = FMatrix44f::Identity;
}
UniformBufferParameters->TranslatedWorldOrigin = FVector3f::ZeroVector;
UniformBufferParameters->TranslatedWorldPlane = FVector4f::Zero();
UniformBufferParameters->Resolution = FIntPoint::ZeroValue;
UniformBufferParameters->DownsampleFactor = 1.0f;
UniformBufferParameters->MaxSampleCount = 0;
UniformBufferParameters->bIsEmpty = true;
UniformBufferParameters->bIsDirectionalLight = false;
UniformBufferParameters->LinkedListBuffer = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FAVSMLinkedListPackedData)));
UniformBufferParameters->IndirectionBuffer = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FAVSMIndirectionPackedData)));
UniformBufferParameters->SampleBuffer = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FAVSMSamplePackedData)));
UniformBufferParameters->RadianceTexture = GraphBuilder.CreateSRV(GSystemTextures.GetBlackDummy(GraphBuilder));
UniformBufferParameters->TextureSampler = TStaticSamplerState<SF_Bilinear>::GetRHI();
}
return GraphBuilder.CreateUniformBuffer(UniformBufferParameters);
}
void DestroyAdaptiveVolumetricShadowMapUniformBuffer(
TRDGUniformBufferRef<FAdaptiveVolumetricShadowMapUniformBufferParameters>& AdaptiveVolumetricShadowMapUniformBuffer
)
{
AdaptiveVolumetricShadowMapUniformBuffer = nullptr;
}
TRDGUniformBufferRef<FAdaptiveVolumetricShadowMapUniformBufferParameters> GetAdaptiveVolumetricShadowMapUniformBuffer(
FRDGBuilder& GraphBuilder,
FSceneViewState* ViewState,
const FLightSceneInfo* LightSceneInfo
)
{
if (ViewState && LightSceneInfo)
{
TRDGUniformBufferRef<FAdaptiveVolumetricShadowMapUniformBufferParameters>* AdaptiveVolumetricShadowMapUniformBuffer = ViewState->AdaptiveVolumetricShadowMapUniformBufferMap.Find(LightSceneInfo->Id);
if (AdaptiveVolumetricShadowMapUniformBuffer != nullptr)
{
return *AdaptiveVolumetricShadowMapUniformBuffer;
}
}
return CreateEmptyAdaptiveVolumetricShadowMapUniformBuffer(GraphBuilder);
}
TRDGUniformBufferRef<FAdaptiveVolumetricShadowMapUniformBufferParameters> GetAdaptiveVolumetricCameraMapUniformBuffer(
FRDGBuilder& GraphBuilder,
FSceneViewState* ViewState
)
{
if (ViewState && ShouldCompositeHeterogeneousVolumesWithTranslucency())
{
if (ViewState->AdaptiveVolumetricCameraMapUniformBuffer != nullptr)
{
return ViewState->AdaptiveVolumetricCameraMapUniformBuffer;
}
else if (ViewState->AdaptiveVolumetricCameraMapUniformBufferParameters != nullptr)
{
return GraphBuilder.CreateUniformBuffer(ViewState->AdaptiveVolumetricCameraMapUniformBufferParameters);
}
}
return CreateEmptyAdaptiveVolumetricShadowMapUniformBuffer(GraphBuilder);
}
FAdaptiveVolumetricShadowMapUniformBufferParameters GetAdaptiveVolumetricCameraMapParameters(
FRDGBuilder& GraphBuilder,
FSceneViewState* ViewState)
{
FAdaptiveVolumetricShadowMapUniformBufferParameters Parameters;
TRDGUniformBufferRef<FAdaptiveVolumetricShadowMapUniformBufferParameters> UniformBuffer = GetAdaptiveVolumetricCameraMapUniformBuffer(GraphBuilder, ViewState);
if (ViewState && UniformBuffer)
{
Parameters.NumShadowMatrices = UniformBuffer->GetParameters()->NumShadowMatrices;
for (int32 i = 0; i < Parameters.NumShadowMatrices; ++i)
{
Parameters.TranslatedWorldToShadow[i] = UniformBuffer->GetParameters()->TranslatedWorldToShadow[i];
}
Parameters.TranslatedWorldOrigin = UniformBuffer->GetParameters()->TranslatedWorldOrigin;
Parameters.TranslatedWorldPlane = UniformBuffer->GetParameters()->TranslatedWorldPlane;
Parameters.Resolution = UniformBuffer->GetParameters()->Resolution;
Parameters.DownsampleFactor = UniformBuffer->GetParameters()->DownsampleFactor;
Parameters.MaxSampleCount = UniformBuffer->GetParameters()->MaxSampleCount;
Parameters.bIsEmpty = UniformBuffer->GetParameters()->bIsEmpty;
Parameters.bIsDirectionalLight = UniformBuffer->GetParameters()->bIsDirectionalLight;
Parameters.LinkedListBuffer = UniformBuffer->GetParameters()->LinkedListBuffer;
Parameters.IndirectionBuffer = UniformBuffer->GetParameters()->IndirectionBuffer;
Parameters.SampleBuffer = UniformBuffer->GetParameters()->SampleBuffer;
Parameters.RadianceTexture = UniformBuffer->GetParameters()->RadianceTexture;
Parameters.TextureSampler = UniformBuffer->GetParameters()->TextureSampler;
}
else
{
Parameters.NumShadowMatrices = 1;
for (int32 i = 0; i < Parameters.NumShadowMatrices; ++i)
{
Parameters.TranslatedWorldToShadow[i] = FMatrix44f::Identity;
}
Parameters.TranslatedWorldOrigin = FVector3f::ZeroVector;
Parameters.TranslatedWorldPlane = FVector4f::Zero();
Parameters.Resolution = FIntPoint::ZeroValue;
Parameters.DownsampleFactor = 1.0f;
Parameters.MaxSampleCount = 0;
Parameters.bIsEmpty = true;
Parameters.bIsDirectionalLight = false;
Parameters.LinkedListBuffer = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FAVSMLinkedListPackedData)));
Parameters.IndirectionBuffer = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FAVSMIndirectionPackedData)));
Parameters.SampleBuffer = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FAVSMSamplePackedData)));
Parameters.RadianceTexture = GraphBuilder.CreateSRV(GSystemTextures.GetBlackDummy(GraphBuilder));
Parameters.TextureSampler = TStaticSamplerState<SF_Bilinear>::GetRHI();
}
return Parameters;
}
} // namespace HeterogeneousVolumes
void CompressVolumetricShadowMap(
FRDGBuilder& GraphBuilder,
FViewInfo& View,
FIntVector GroupCount,
// Input
FIntPoint ShadowMapResolution,
uint32 MaxSampleCount,
FRDGBufferRef VolumetricShadowLinkedListBuffer,
// Output
FRDGBufferRef& VolumetricShadowIndirectionBuffer,
FRDGBufferRef& VolumetricShadowTransmittanceBuffer
)
{
int32 VolumetricShadowPixelCount = ShadowMapResolution.X * ShadowMapResolution.Y * GroupCount.Z;
FRDGBufferRef VolumetricShadowIndirectionAllocatorBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), 1),
TEXT("HeterogeneousVolume.VolumetricShadowIndirectionAllocatorBuffer")
);
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(VolumetricShadowIndirectionAllocatorBuffer, PF_R32_UINT), 0);
VolumetricShadowIndirectionBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(FAVSMIndirectionPackedData), VolumetricShadowPixelCount),
TEXT("HeterogeneousVolume.VolumetricShadowIndirectionBuffer")
);
int32 TopLevelSampleCount = FMath::CeilToInt(MaxSampleCount / 4.0);
int32 VolumetricShadowTransmittanceMaxCount = VolumetricShadowPixelCount * (Align(TopLevelSampleCount, 4) + Align(MaxSampleCount, 4));
VolumetricShadowTransmittanceBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(FAVSMSamplePackedData), VolumetricShadowTransmittanceMaxCount),
TEXT("HeterogeneousVolume.VolumetricShadowTransmittanceBuffer")
);
FCompressVolumetricShadowMapCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FCompressVolumetricShadowMapCS::FParameters>();
{
// Input
PassParameters->ShadowResolution = ShadowMapResolution;
PassParameters->MaxSampleCount = MaxSampleCount;
PassParameters->VolumetricShadowLinkedListBuffer = GraphBuilder.CreateSRV(VolumetricShadowLinkedListBuffer);
// Output
PassParameters->RWVolumetricShadowIndirectionAllocatorBuffer = GraphBuilder.CreateUAV(VolumetricShadowIndirectionAllocatorBuffer, PF_R32_UINT);
PassParameters->RWVolumetricShadowIndirectionBuffer = GraphBuilder.CreateUAV(VolumetricShadowIndirectionBuffer);
PassParameters->RWVolumetricShadowTransmittanceBuffer = GraphBuilder.CreateUAV(VolumetricShadowTransmittanceBuffer);
}
FCompressVolumetricShadowMapCS::FPermutationDomain PermutationVector;
TShaderRef<FCompressVolumetricShadowMapCS> ComputeShader = View.ShaderMap->GetShader<FCompressVolumetricShadowMapCS>(PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
// Add Light name..
RDG_EVENT_NAME("CompressVolumetricShadowMapCS"),
ERDGPassFlags::Compute | ERDGPassFlags::NeverCull,
ComputeShader,
PassParameters,
GroupCount
);
}
void CombineVolumetricShadowMap(
FRDGBuilder& GraphBuilder,
FViewInfo& View,
FIntVector GroupCount,
// Input
uint32 LightType,
FIntPoint ShadowMapResolution,
uint32 MaxSampleCount,
FRDGBufferRef VolumetricShadowLinkedListBuffer0,
FRDGBufferRef VolumetricShadowLinkedListBuffer1,
// Output
FRDGBufferRef& VolumetricShadowLinkedListBuffer
)
{
bool bIsMultiProjection = (LightType == LightType_Point) || (LightType == LightType_Rect);
GroupCount.X = FMath::DivideAndRoundUp(ShadowMapResolution.X, FCombineVolumetricShadowMapsCS::GetThreadGroupSize2D());
GroupCount.Y = FMath::DivideAndRoundUp(ShadowMapResolution.Y, FCombineVolumetricShadowMapsCS::GetThreadGroupSize2D());
GroupCount.Z = bIsMultiProjection ? 6 : 1;
MaxSampleCount = HeterogeneousVolumes::GetShadowMaxSampleCount();
int32 VolumetricShadowLinkedListElementCount = ShadowMapResolution.X * ShadowMapResolution.Y * MaxSampleCount;
if (bIsMultiProjection)
{
VolumetricShadowLinkedListElementCount *= 6;
}
VolumetricShadowLinkedListBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(FAVSMLinkedListPackedData), VolumetricShadowLinkedListElementCount),
TEXT("HeterogeneousVolume.VolumetricShadowLinkedListBuffer")
);
FCombineVolumetricShadowMapsCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FCombineVolumetricShadowMapsCS::FParameters>();
{
// Input
PassParameters->ShadowResolution = ShadowMapResolution;
PassParameters->VolumetricShadowLinkedListBuffer0 = GraphBuilder.CreateSRV(VolumetricShadowLinkedListBuffer0);
PassParameters->VolumetricShadowLinkedListBuffer1 = GraphBuilder.CreateSRV(VolumetricShadowLinkedListBuffer1);
PassParameters->MaxSampleCount = MaxSampleCount;
PassParameters->AbsoluteErrorThreshold = HeterogeneousVolumes::GetShadowAbsoluteErrorThreshold();
PassParameters->RelativeErrorThreshold = HeterogeneousVolumes::GetShadowRelativeErrorThreshold();
// Output
PassParameters->RWVolumetricShadowLinkedListBuffer = GraphBuilder.CreateUAV(VolumetricShadowLinkedListBuffer);
}
FCombineVolumetricShadowMapsCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FCombineVolumetricShadowMapsCS::FUseAVSMCompression>(HeterogeneousVolumes::UseAVSMCompression());
TShaderRef<FCombineVolumetricShadowMapsCS> ComputeShader = View.ShaderMap->GetShader<FCombineVolumetricShadowMapsCS>(PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
// Add Light name..
RDG_EVENT_NAME("CombineVolumetricShadowMapsCS"),
ERDGPassFlags::Compute | ERDGPassFlags::NeverCull,
ComputeShader,
PassParameters,
GroupCount
);
}
void RenderVolumetricShadowMapForLightWithVoxelGrid(
FRDGBuilder& GraphBuilder,
// Scene data
const FSceneTextures& SceneTextures,
FScene* Scene,
FViewInfo& View,
// Light data
bool bApplyEmissionAndTransmittance,
bool bApplyDirectLighting,
bool bApplyShadowTransmittance,
uint32 LightType,
const FLightSceneInfo* LightSceneInfo,
// Shadow data
const FVisibleLightInfo* VisibleLightInfo,
const FVirtualShadowMapArray& VirtualShadowMapArray,
// Volume data
const TRDGUniformBufferRef<FOrthoVoxelGridUniformBufferParameters>& OrthoGridUniformBuffer,
const TRDGUniformBufferRef<FFrustumVoxelGridUniformBufferParameters>& FrustumGridUniformBuffer,
// Output
bool& bIsDirectionalLight,
FVector3f& TranslatedWorldOrigin,
FVector4f& TranslatedWorldPlane,
FMatrix44f* TranslatedWorldToShadow,
FIntVector& GroupCount,
int32& NumShadowMatrices,
FIntPoint& ShadowMapResolution,
uint32& MaxSampleCount,
FRDGTextureRef& BeerShadowMapTexture,
FRDGBufferRef& VolumetricShadowLinkedListBuffer
)
{
check(LightSceneInfo);
check(VisibleLightInfo);
const FProjectedShadowInfo* ProjectedShadowInfo = HeterogeneousVolumes::GetProjectedShadowInfo(VisibleLightInfo, 0);
check(ProjectedShadowInfo != NULL)
ShadowMapResolution = HeterogeneousVolumes::GetShadowMapResolution();
bool bIsMultiProjection = (LightType == LightType_Point) || (LightType == LightType_Rect);
GroupCount = FIntVector(1);
GroupCount.X = FMath::DivideAndRoundUp(ShadowMapResolution.X, FRenderVolumetricShadowMapForLightWithVoxelGridCS::GetThreadGroupSize2D());
GroupCount.Y = FMath::DivideAndRoundUp(ShadowMapResolution.Y, FRenderVolumetricShadowMapForLightWithVoxelGridCS::GetThreadGroupSize2D());
GroupCount.Z = bIsMultiProjection ? 6 : 1;
// TODO: Allocate debug data
int32 BufferSize = ShadowMapResolution.X * ShadowMapResolution.Y;
if (bIsMultiProjection)
{
BufferSize *= 6;
}
FRDGBufferRef DebugBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(FVolumetricShadowMapDebugData), BufferSize),
TEXT("HeterogeneousVolume.RenderDebugData")
);
FRDGBufferRef VolumetricShadowLinkedListAllocatorBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), 1),
TEXT("HeterogeneousVolume.VolumetricShadowLinkedListAllocatorBuffer")
);
MaxSampleCount = HeterogeneousVolumes::GetShadowMaxSampleCount();
int32 VolumetricShadowLinkedListElementCount = ShadowMapResolution.X * ShadowMapResolution.Y * MaxSampleCount;
if (bIsMultiProjection)
{
VolumetricShadowLinkedListElementCount *= 6;
}
VolumetricShadowLinkedListBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(FAVSMLinkedListPackedData), VolumetricShadowLinkedListElementCount),
TEXT("HeterogeneousVolume.VolumetricShadowLinkedListBuffer")
);
const float RelativeErrorThreshold = HeterogeneousVolumes::GetShadowRelativeErrorThreshold();
// Initialize allocator to contain 1-spp
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(VolumetricShadowLinkedListAllocatorBuffer, PF_R32_UINT), ShadowMapResolution.X * ShadowMapResolution.Y);
FDeferredLightUniformStruct DeferredLightUniform;
if (bApplyDirectLighting && (LightSceneInfo != nullptr))
{
DeferredLightUniform = GetDeferredLightParameters(View, *LightSceneInfo);
}
TUniformBufferRef<FDeferredLightUniformStruct> DeferredLightUB = CreateUniformBufferImmediate(DeferredLightUniform, UniformBuffer_SingleDraw);
FRenderVolumetricShadowMapForLightWithVoxelGridCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FRenderVolumetricShadowMapForLightWithVoxelGridCS::FParameters>();
{
// Scene data
PassParameters->View = View.ViewUniformBuffer;
PassParameters->SceneTextures = GetSceneTextureParameters(GraphBuilder, SceneTextures);
FBlueNoise BlueNoise = GetBlueNoiseGlobalParameters();
PassParameters->BlueNoise = CreateUniformBufferImmediate(BlueNoise, EUniformBufferUsage::UniformBuffer_SingleDraw);
// Ray Data
PassParameters->ShadowStepSize = HeterogeneousVolumes::GetStepSizeForShadows();
PassParameters->ShadowStepFactor = 1.0;
PassParameters->MaxTraceDistance = HeterogeneousVolumes::GetMaxTraceDistance();
PassParameters->MaxStepCount = HeterogeneousVolumes::GetMaxStepCount();
PassParameters->bJitter = HeterogeneousVolumes::ShouldJitter();
PassParameters->StochasticFilteringMode = static_cast<int32>(HeterogeneousVolumes::GetStochasticFilteringMode());
PassParameters->NumShadowMatrices = ProjectedShadowInfo->OnePassShadowViewProjectionMatrices.Num();
if (PassParameters->NumShadowMatrices > 0)
{
FVector PreViewTranslation = View.ViewMatrices.GetPreViewTranslation();
FMatrix TranslatedWorldToWorldMatrix = FTranslationMatrix(-PreViewTranslation);
FVector LightPosition = LightSceneInfo->Proxy->GetPosition();
FMatrix WorldToLightMatrix = LightSceneInfo->Proxy->GetWorldToLight();
// Remove light rotation when building the RectLight projections..
FMatrix RotationalAdjustmentMatrix = FMatrix::Identity;
if (LightType == LIGHT_TYPE_RECT)
{
FVector LightDirection = LightSceneInfo->Proxy->GetDirection().GetSafeNormal();
RotationalAdjustmentMatrix = FRotationMatrix(LightDirection.Rotation());
}
FMatrix ViewMatrix[] = {
FLookFromMatrix(FVector::Zero(), FVector(-1, 0, 0), FVector(0, 0, 1)),
FLookFromMatrix(FVector::Zero(), FVector(1, 0, 0), FVector(0, 0, 1)),
FLookFromMatrix(FVector::Zero(), FVector(0, -1, 0), FVector(0, 0, 1)),
FLookFromMatrix(FVector::Zero(), FVector(0, 1, 0), FVector(0, 0, 1)),
FLookFromMatrix(FVector::Zero(), FVector(0, 0, -1), FVector(1, 0, 0)),
FLookFromMatrix(FVector::Zero(), FVector(0, 0, 1), FVector(1, 0, 0))
};
FMatrix PerspectiveMatrix = FPerspectiveMatrix(
PI / 4.0f,
ShadowMapResolution.X,
ShadowMapResolution.Y,
1.0,
LightSceneInfo->Proxy->GetRadius()
);
FMatrix ScreenMatrix = FScaleMatrix(FVector(0.5, -0.5, -0.5)) * FTranslationMatrix(FVector(0.5, 0.5, 0.5));
for (int32 i = 0; i < PassParameters->NumShadowMatrices; ++i)
{
FMatrix WorldToShadowMatrix = WorldToLightMatrix * RotationalAdjustmentMatrix * ViewMatrix[i] * PerspectiveMatrix * ScreenMatrix;
PassParameters->TranslatedWorldToShadow[i] = FMatrix44f(TranslatedWorldToWorldMatrix * WorldToShadowMatrix);
PassParameters->ShadowToTranslatedWorld[i] = PassParameters->TranslatedWorldToShadow[i].Inverse();
}
PassParameters->TranslatedWorldOrigin = FVector3f(PreViewTranslation + LightPosition);
}
else if (LightType == LightType_Directional)
{
bIsDirectionalLight = true;
// Build orthographic projection centered around volume..
FVector PreViewTranslation = View.ViewMatrices.GetPreViewTranslation();
FMatrix TranslatedWorldToWorldMatrix = FTranslationMatrix(-PreViewTranslation);
FVector WorldBoundsMin = FVector(OrthoGridUniformBuffer->GetParameters()->TopLevelGridWorldBoundsMin);
FVector WorldBoundsMax = FVector(OrthoGridUniformBuffer->GetParameters()->TopLevelGridWorldBoundsMax);
FBoxSphereBounds VolumeBounds(FBox(WorldBoundsMin, WorldBoundsMax));
FMatrix TranslationMatrix = FTranslationMatrix(-VolumeBounds.Origin);
FVector LightDirection = LightSceneInfo->Proxy->GetDirection().GetSafeNormal();
FMatrix RotationMatrix = FInverseRotationMatrix(LightDirection.Rotation());
FMatrix ScaleMatrix = FScaleMatrix(FVector(1.0 / VolumeBounds.SphereRadius));
const FMatrix FaceMatrix(
FPlane(0, 0, 1, 0),
FPlane(0, 1, 0, 0),
FPlane(-1, 0, 0, 0),
FPlane(0, 0, 0, 1));
// Invert Z to match reverse-Z for the rest of the shadow types!
FMatrix ScreenMatrix = FScaleMatrix(FVector(0.5, -0.5, -0.5)) * FTranslationMatrix(FVector(0.5, 0.5, 0.5));
FMatrix WorldToShadowMatrix = TranslationMatrix * RotationMatrix * ScaleMatrix * FaceMatrix * ScreenMatrix;
FMatrix44f TranslatedWorldToShadowMatrix = FMatrix44f(TranslatedWorldToWorldMatrix * WorldToShadowMatrix);
PassParameters->NumShadowMatrices = 1;
PassParameters->TranslatedWorldToShadow[0] = TranslatedWorldToShadowMatrix;
PassParameters->ShadowToTranslatedWorld[0] = TranslatedWorldToShadowMatrix.Inverse();
PassParameters->TranslatedWorldOrigin = FVector3f(PreViewTranslation + VolumeBounds.Origin - LightDirection * VolumeBounds.SphereRadius);
}
else
{
FVector PreViewTranslation = View.ViewMatrices.GetPreViewTranslation();
FMatrix TranslatedWorldToWorldMatrix = FTranslationMatrix(-PreViewTranslation);
FVector4f ShadowmapMinMax = FVector4f::Zero();
FMatrix WorldToShadowMatrix = ProjectedShadowInfo->GetWorldToShadowMatrix(ShadowmapMinMax);
FMatrix44f TranslatedWorldToShadowMatrix = FMatrix44f(TranslatedWorldToWorldMatrix * WorldToShadowMatrix);
PassParameters->NumShadowMatrices = 1;
PassParameters->TranslatedWorldToShadow[0] = TranslatedWorldToShadowMatrix;
PassParameters->ShadowToTranslatedWorld[0] = TranslatedWorldToShadowMatrix.Inverse();
PassParameters->TranslatedWorldOrigin = FVector3f(View.ViewMatrices.GetPreViewTranslation() - ProjectedShadowInfo->PreShadowTranslation);
}
TranslatedWorldOrigin = PassParameters->TranslatedWorldOrigin;
NumShadowMatrices = PassParameters->NumShadowMatrices;
for (int32 i = 0; i < PassParameters->NumShadowMatrices; ++i)
{
TranslatedWorldToShadow[i] = PassParameters->TranslatedWorldToShadow[i];
}
FVector LightDirection = LightSceneInfo->Proxy->GetDirection().GetSafeNormal();
float W = -FVector3f::DotProduct(TranslatedWorldOrigin, FVector3f(LightDirection));
TranslatedWorldPlane = FVector4f(LightDirection.X, LightDirection.Y, LightDirection.Z, W);
PassParameters->ShadowResolution = ShadowMapResolution;
PassParameters->MaxSampleCount = MaxSampleCount;
PassParameters->AbsoluteErrorThreshold = HeterogeneousVolumes::GetShadowAbsoluteErrorThreshold();
PassParameters->RelativeErrorThreshold = RelativeErrorThreshold;
// Volume data
PassParameters->OrthoGridUniformBuffer = OrthoGridUniformBuffer;
PassParameters->FrustumGridUniformBuffer = FrustumGridUniformBuffer;
// Dispatch data
PassParameters->GroupCount = GroupCount;
PassParameters->ShadowDebugTweak = CVarHeterogeneousVolumesShadowDebugTweak.GetValueOnRenderThread();
// Output
PassParameters->RWVolumetricShadowLinkedListAllocatorBuffer = GraphBuilder.CreateUAV(VolumetricShadowLinkedListAllocatorBuffer, PF_R32_UINT);
PassParameters->RWVolumetricShadowLinkedListBuffer = GraphBuilder.CreateUAV(VolumetricShadowLinkedListBuffer);
PassParameters->RWBeerShadowMapTexture = GraphBuilder.CreateUAV(BeerShadowMapTexture);
PassParameters->RWDebugBuffer = GraphBuilder.CreateUAV(DebugBuffer);
}
FRenderVolumetricShadowMapForLightWithVoxelGridCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FRenderVolumetricShadowMapForLightWithVoxelGridCS::FUseAVSMCompression>(HeterogeneousVolumes::UseAVSMCompression());
PermutationVector.Set<FRenderVolumetricShadowMapForLightWithVoxelGridCS::FIsOfflineRender>(View.bIsOfflineRender);
TShaderRef<FRenderVolumetricShadowMapForLightWithVoxelGridCS> ComputeShader = View.ShaderMap->GetShader<FRenderVolumetricShadowMapForLightWithVoxelGridCS>(PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
// Add Light name..
RDG_EVENT_NAME("RenderVolumetricShadowMapForLightWithVoxelGridCS"),
ERDGPassFlags::Compute | ERDGPassFlags::NeverCull,
ComputeShader,
PassParameters,
GroupCount
);
}
void RenderVolumetricShadowMapForCameraWithVoxelGrid(
FRDGBuilder& GraphBuilder,
// Scene data
const FSceneTextures& SceneTextures,
FScene* Scene,
FViewInfo& View,
// Volume data
const TRDGUniformBufferRef<FOrthoVoxelGridUniformBufferParameters>& OrthoGridUniformBuffer,
const TRDGUniformBufferRef<FFrustumVoxelGridUniformBufferParameters>& FrustumGridUniformBuffer,
// Output
FVector3f& TranslatedWorldOrigin,
FMatrix44f& TranslatedWorldToShadow,
FIntVector& GroupCount,
FIntPoint& ShadowMapResolution,
uint32& MaxSampleCount,
FRDGTextureRef& BeerShadowMapTexture,
FRDGBufferRef& VolumetricShadowLinkedListBuffer
)
{
ShadowMapResolution = HeterogeneousVolumes::GetDownsampledResolution(View.ViewRect.Size(), HeterogeneousVolumes::GetCameraDownsampleFactor());
GroupCount = FIntVector(1);
GroupCount.X = FMath::DivideAndRoundUp(ShadowMapResolution.X, FRenderVolumetricShadowMapForLightWithVoxelGridCS::GetThreadGroupSize2D());
GroupCount.Y = FMath::DivideAndRoundUp(ShadowMapResolution.Y, FRenderVolumetricShadowMapForLightWithVoxelGridCS::GetThreadGroupSize2D());
// TODO: Allocate debug data
int32 BufferSize = ShadowMapResolution.X * ShadowMapResolution.Y;
FRDGBufferRef DebugBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(FVolumetricShadowMapDebugData), BufferSize),
TEXT("HeterogeneousVolume.RenderDebugData")
);
FRDGBufferRef VolumetricShadowLinkedListAllocatorBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), 1),
TEXT("HeterogeneousVolume.CameraAVSM.LinkedListAllocatorBuffer")
);
MaxSampleCount = HeterogeneousVolumes::GetShadowMaxSampleCount();
int32 VolumetricShadowLinkedListElementCount = ShadowMapResolution.X * ShadowMapResolution.Y * MaxSampleCount;
VolumetricShadowLinkedListBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(FAVSMLinkedListPackedData), VolumetricShadowLinkedListElementCount),
TEXT("HeterogeneousVolume.CameraAVSM.LinkedListBuffer")
);
const float RelativeErrorThreshold = HeterogeneousVolumes::GetShadowRelativeErrorThreshold();
// Initialize allocator to contain 1-spp
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(VolumetricShadowLinkedListAllocatorBuffer, PF_R32_UINT), ShadowMapResolution.X * ShadowMapResolution.Y);
// TODO: Use the frustum grid bounds instead of re-based trace-distance bounds
// Intersect TopLevelGridBounds with light ray to get appropriate culling distances.
FMatrix ViewToClip = FPerspectiveMatrix(
FMath::DegreesToRadians(View.FOV * 0.5),
ShadowMapResolution.X,
ShadowMapResolution.Y,
1.0,
HeterogeneousVolumes::GetMaxTraceDistance()
);
FMatrix ClipToView = ViewToClip.Inverse();
FMatrix ScreenMatrix = FScaleMatrix(FVector(0.5, -0.5, -0.5)) * FTranslationMatrix(FVector(0.5, 0.5, 0.5));
TranslatedWorldToShadow = FMatrix44f(View.ViewMatrices.GetTranslatedViewMatrix() * ViewToClip * ScreenMatrix);
FMatrix44f ShadowToTranslatedWorld = TranslatedWorldToShadow.Inverse();
TranslatedWorldOrigin = ShadowToTranslatedWorld.GetOrigin();
FRenderVolumetricShadowMapForLightWithVoxelGridCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FRenderVolumetricShadowMapForLightWithVoxelGridCS::FParameters>();
{
// Scene data
PassParameters->View = View.ViewUniformBuffer;
PassParameters->SceneTextures = GetSceneTextureParameters(GraphBuilder, SceneTextures);
FBlueNoise BlueNoise = GetBlueNoiseGlobalParameters();
PassParameters->BlueNoise = CreateUniformBufferImmediate(BlueNoise, EUniformBufferUsage::UniformBuffer_SingleDraw);
// Ray Data
PassParameters->ShadowStepSize = HeterogeneousVolumes::GetStepSizeForShadows();
PassParameters->ShadowStepFactor = 1.0;
PassParameters->MaxTraceDistance = HeterogeneousVolumes::GetMaxTraceDistance();
PassParameters->MaxStepCount = HeterogeneousVolumes::GetMaxStepCount();
PassParameters->bJitter = HeterogeneousVolumes::ShouldJitter();
PassParameters->ShadowResolution = ShadowMapResolution;
PassParameters->NumShadowMatrices = 1;
PassParameters->TranslatedWorldToShadow[0] = TranslatedWorldToShadow;
PassParameters->ShadowToTranslatedWorld[0] = ShadowToTranslatedWorld;
PassParameters->MaxSampleCount = MaxSampleCount;
PassParameters->AbsoluteErrorThreshold = HeterogeneousVolumes::GetShadowAbsoluteErrorThreshold();
PassParameters->RelativeErrorThreshold = RelativeErrorThreshold;
// Volume data
PassParameters->OrthoGridUniformBuffer = OrthoGridUniformBuffer;
PassParameters->FrustumGridUniformBuffer = FrustumGridUniformBuffer;
// Dispatch data
PassParameters->GroupCount = GroupCount;
// Output
PassParameters->RWVolumetricShadowLinkedListAllocatorBuffer = GraphBuilder.CreateUAV(VolumetricShadowLinkedListAllocatorBuffer, PF_R32_UINT);
PassParameters->RWVolumetricShadowLinkedListBuffer = GraphBuilder.CreateUAV(VolumetricShadowLinkedListBuffer);
PassParameters->RWBeerShadowMapTexture = GraphBuilder.CreateUAV(BeerShadowMapTexture);
PassParameters->RWDebugBuffer = GraphBuilder.CreateUAV(DebugBuffer);
}
bool bUseCustomProjection = false;
FRenderVolumetricShadowMapForLightWithVoxelGridCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FRenderVolumetricShadowMapForLightWithVoxelGridCS::FUseAVSMCompression>(HeterogeneousVolumes::UseAVSMCompression());
TShaderRef<FRenderVolumetricShadowMapForLightWithVoxelGridCS> ComputeShader = View.ShaderMap->GetShader<FRenderVolumetricShadowMapForLightWithVoxelGridCS>(PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
// Add Light name..
RDG_EVENT_NAME("RenderVolumetricShadowMapForCameraWithVoxelGridCS"),
ERDGPassFlags::Compute | ERDGPassFlags::NeverCull,
ComputeShader,
PassParameters,
GroupCount
);
}
void RenderAdaptiveVolumetricShadowMapWithVoxelGrid(
FRDGBuilder& GraphBuilder,
// Scene data
const FSceneTextures& SceneTextures,
FScene* Scene,
FViewInfo& View,
// Shadow data
TArray<FVisibleLightInfo, SceneRenderingAllocator>& VisibleLightInfos,
const FVirtualShadowMapArray& VirtualShadowMapArray,
// Volume data
const TRDGUniformBufferRef<FOrthoVoxelGridUniformBufferParameters>& OrthoGridUniformBuffer,
const TRDGUniformBufferRef<FFrustumVoxelGridUniformBufferParameters>& FrustumGridUniformBuffer
)
{
RDG_EVENT_SCOPE(GraphBuilder, "Adaptive Volumetric Shadow Maps");
bool bShouldRenderShadowMaps = !View.ViewRect.IsEmpty() &&
(OrthoGridUniformBuffer->GetParameters()->bUseOrthoGrid || FrustumGridUniformBuffer->GetParameters()->bUseFrustumGrid);
// Light culling
TArray<FLightSceneInfoCompact, TInlineAllocator<64>> LightSceneInfoCompact;
for (auto LightIt = Scene->Lights.CreateConstIterator(); LightIt; ++LightIt)
{
// TODO: Use global bounds information..
//if (LightIt->AffectsPrimitive(HeterogeneousVolumeInterface->GetBounds(), HeterogeneousVolumeInterface->GetPrimitiveSceneProxy()))
if (HeterogeneousVolumes::SupportsShadowForLightType(LightIt->LightType) &&
(LightIt->LightSceneInfo->Proxy->GetViewLightingChannelMask() & View.ViewLightingChannelMask))
{
LightSceneInfoCompact.Add(*LightIt);
}
}
// Light loop:
int32 NumPasses = LightSceneInfoCompact.Num();
for (int32 PassIndex = 0; PassIndex < NumPasses; ++PassIndex)
{
bool bApplyEmissionAndTransmittance = (PassIndex == (NumPasses - 1));
bool bApplyDirectLighting = !LightSceneInfoCompact.IsEmpty();
bool bApplyShadowTransmittance = false;
uint32 LightType = 0;
FLightSceneInfo* LightSceneInfo = nullptr;
const FVisibleLightInfo* VisibleLightInfo = nullptr;
if (bApplyDirectLighting)
{
LightType = LightSceneInfoCompact[PassIndex].LightType;
LightSceneInfo = LightSceneInfoCompact[PassIndex].LightSceneInfo;
check(LightSceneInfo != nullptr);
bApplyDirectLighting = (LightSceneInfo != nullptr);
bool bDynamicallyShadowed = false;
if (LightSceneInfo)
{
VisibleLightInfo = &VisibleLightInfos[LightSceneInfo->Id];
bApplyShadowTransmittance = LightSceneInfo->Proxy && LightSceneInfo->Proxy->CastsVolumetricShadow();
bDynamicallyShadowed = HeterogeneousVolumes::IsDynamicShadow(VisibleLightInfo);
}
TRDGUniformBufferRef<FAdaptiveVolumetricShadowMapUniformBufferParameters> AdaptiveVolumetricShadowMapUniformBuffer;
bool bCreateShadowMap = bShouldRenderShadowMaps && bApplyShadowTransmittance && bDynamicallyShadowed && !ShouldRenderRayTracingShadowsForLight(*View.Family, LightSceneInfoCompact[PassIndex]);
if (bCreateShadowMap)
{
FString LightName;
FSceneRenderer::GetLightNameForDrawEvent(LightSceneInfo->Proxy, LightName);
RDG_EVENT_SCOPE(GraphBuilder, "%s", *LightName);
FRDGTextureDesc Desc = SceneTextures.Color.Target->Desc;
Desc.Format = PF_FloatRGBA;
Desc.Flags &= ~(TexCreate_FastVRAM);
FRDGTextureRef BeerShadowMapTexture = GraphBuilder.CreateTexture(Desc, TEXT("BeerShadowMapTexture"));
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(BeerShadowMapTexture), FLinearColor::Transparent);
bool bIsDirectionalLight = false;
FVector3f TranslatedWorldOrigin = FVector3f::Zero();
FVector4f TranslatedWorldPlane = FVector4f::Zero();
FMatrix44f TranslatedWorldToShadow[] =
{
FMatrix44f::Identity,
FMatrix44f::Identity,
FMatrix44f::Identity,
FMatrix44f::Identity,
FMatrix44f::Identity,
FMatrix44f::Identity
};
FIntVector GroupCount = FIntVector::ZeroValue;
int32 NumShadowMatrices = 0;
FIntPoint VolumetricShadowMapResolution = FIntPoint::NoneValue;
uint32 VolumetricShadowMapMaxSampleCount = 0;
FRDGBufferRef VolumetricShadowMapLinkedListBuffer;
RenderVolumetricShadowMapForLightWithVoxelGrid(
GraphBuilder,
// Scene data
SceneTextures,
Scene,
View,
// Light data
bApplyEmissionAndTransmittance,
bApplyDirectLighting,
bApplyShadowTransmittance,
LightType,
LightSceneInfo,
// Shadow data
VisibleLightInfo,
VirtualShadowMapArray,
// Volume data
OrthoGridUniformBuffer,
FrustumGridUniformBuffer,
// Output
bIsDirectionalLight,
TranslatedWorldOrigin,
TranslatedWorldPlane,
TranslatedWorldToShadow,
GroupCount,
NumShadowMatrices,
VolumetricShadowMapResolution,
VolumetricShadowMapMaxSampleCount,
BeerShadowMapTexture,
VolumetricShadowMapLinkedListBuffer
);
FRDGBufferRef VolumetricShadowMapIndirectionBuffer;
FRDGBufferRef VolumetricShadowMapSampleBuffer;
CompressVolumetricShadowMap(
GraphBuilder,
View,
GroupCount,
VolumetricShadowMapResolution,
VolumetricShadowMapMaxSampleCount,
VolumetricShadowMapLinkedListBuffer,
VolumetricShadowMapIndirectionBuffer,
VolumetricShadowMapSampleBuffer
);
float DownsampleFactor = 1.0f;
CreateAdaptiveVolumetricShadowMapUniformBuffer(
GraphBuilder,
TranslatedWorldOrigin,
TranslatedWorldPlane,
TranslatedWorldToShadow,
VolumetricShadowMapResolution,
DownsampleFactor,
NumShadowMatrices,
VolumetricShadowMapMaxSampleCount,
bIsDirectionalLight,
VolumetricShadowMapLinkedListBuffer,
VolumetricShadowMapIndirectionBuffer,
VolumetricShadowMapSampleBuffer,
AdaptiveVolumetricShadowMapUniformBuffer
);
}
else
{
AdaptiveVolumetricShadowMapUniformBuffer = HeterogeneousVolumes::CreateEmptyAdaptiveVolumetricShadowMapUniformBuffer(GraphBuilder);
}
if (View.ViewState)
{
TRDGUniformBufferRef<FAdaptiveVolumetricShadowMapUniformBufferParameters>& AdaptiveVolumetricShadowMap = View.ViewState->AdaptiveVolumetricShadowMapUniformBufferMap.FindOrAdd(LightSceneInfo->Id);
AdaptiveVolumetricShadowMap = AdaptiveVolumetricShadowMapUniformBuffer;
}
}
}
}
void RenderAdaptiveVolumetricCameraMapWithVoxelGrid(
FRDGBuilder& GraphBuilder,
// Scene data
const FSceneTextures& SceneTextures,
FScene* Scene,
FViewInfo& View,
// Volume data
const TRDGUniformBufferRef<FOrthoVoxelGridUniformBufferParameters>& OrthoGridUniformBuffer,
const TRDGUniformBufferRef<FFrustumVoxelGridUniformBufferParameters>& FrustumGridUniformBuffer
)
{
if (View.ViewState == nullptr)
{
return;
}
RDG_EVENT_SCOPE(GraphBuilder, "Adaptive Volumetric Camera Map");
FVector3f TranslatedWorldOrigin = FVector3f::ZeroVector;
int32 NumShadowMatrices = 1;
FMatrix44f TranslatedWorldToShadow[] =
{
FMatrix44f::Identity
};
FIntPoint VolumetricShadowMapResolution = FIntPoint::NoneValue;
uint32 VolumetricShadowMapMaxSampleCount = 0;
FRDGBufferRef VolumetricShadowMapLinkedListBuffer = GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FAVSMLinkedListPackedData));;
FRDGBufferRef VolumetricShadowMapIndirectionBuffer = GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FAVSMIndirectionPackedData));
FRDGBufferRef VolumetricShadowMapSampleBuffer = GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FAVSMSamplePackedData));
bool bShouldRenderCameraMap = !View.ViewRect.IsEmpty()
&& (OrthoGridUniformBuffer->GetParameters()->bUseOrthoGrid || FrustumGridUniformBuffer->GetParameters()->bUseFrustumGrid);
if (bShouldRenderCameraMap)
{
FRDGTextureDesc Desc = SceneTextures.Color.Target->Desc;
Desc.Format = PF_FloatRGBA;
Desc.Flags &= ~(TexCreate_FastVRAM);
FRDGTextureRef CameraShadowMapTexture = GraphBuilder.CreateTexture(Desc, TEXT("CameraShadowMapTexture"));
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(CameraShadowMapTexture), FLinearColor::Transparent);
FIntVector GroupCount = FIntVector::ZeroValue;
RenderVolumetricShadowMapForCameraWithVoxelGrid(
GraphBuilder,
// Scene data
SceneTextures,
Scene,
View,
// Volume data
OrthoGridUniformBuffer,
FrustumGridUniformBuffer,
// Output
TranslatedWorldOrigin,
TranslatedWorldToShadow[0],
GroupCount,
VolumetricShadowMapResolution,
VolumetricShadowMapMaxSampleCount,
CameraShadowMapTexture,
VolumetricShadowMapLinkedListBuffer
);
CompressVolumetricShadowMap(
GraphBuilder,
View,
GroupCount,
VolumetricShadowMapResolution,
VolumetricShadowMapMaxSampleCount,
VolumetricShadowMapLinkedListBuffer,
VolumetricShadowMapIndirectionBuffer,
VolumetricShadowMapSampleBuffer
);
FVector4f TranslatedWorldPlane = FVector4f::Zero();
bool bIsDirectionalLight = false;
float DownsampleFactor = HeterogeneousVolumes::GetCameraDownsampleFactor();
CreateAdaptiveVolumetricShadowMapUniformBufferParameters(
GraphBuilder,
TranslatedWorldOrigin,
TranslatedWorldPlane,
TranslatedWorldToShadow,
VolumetricShadowMapResolution,
DownsampleFactor,
NumShadowMatrices,
VolumetricShadowMapMaxSampleCount,
bIsDirectionalLight,
VolumetricShadowMapLinkedListBuffer,
VolumetricShadowMapIndirectionBuffer,
VolumetricShadowMapSampleBuffer,
View.ViewState->AdaptiveVolumetricCameraMapUniformBufferParameters
);
}
}