1691 lines
59 KiB
C++
1691 lines
59 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "HeterogeneousVolumes.h"
|
|
#include "HeterogeneousVolumeInterface.h"
|
|
|
|
#include "PixelShaderUtils.h"
|
|
#include "PostProcess/PostProcessing.h"
|
|
#include "RayTracingDefinitions.h"
|
|
#include "RayTracingInstance.h"
|
|
#include "RayTracingInstanceBufferUtil.h"
|
|
#include "RendererPrivate.h"
|
|
#include "ScenePrivate.h"
|
|
#include "PrimitiveDrawingUtils.h"
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumes(
|
|
TEXT("r.HeterogeneousVolumes"),
|
|
1,
|
|
TEXT("Enables the Heterogeneous volume integrator (Default = 1)"),
|
|
ECVF_Scalability | ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesShadows(
|
|
TEXT("r.HeterogeneousVolumes.Shadows"),
|
|
0,
|
|
TEXT("Enables heterogeneous volume-casting shadows (default = 0)"),
|
|
ECVF_RenderThreadSafe | ECVF_ReadOnly
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarTranslucencyHeterogeneousVolumes(
|
|
TEXT("r.Translucency.HeterogeneousVolumes"),
|
|
0,
|
|
TEXT("Enables composting with heterogeneous volumes when rendering translucency (Default = 0)\n")
|
|
TEXT("0: Off\n")
|
|
TEXT("1: Camera-based Adaptive Volumetric Shadow Map\n")
|
|
TEXT("2: Camera-based Beer-Law Shadow Map\n"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<float> CVarHeterogeneousVolumesDownsampleFactor(
|
|
TEXT("r.HeterogeneousVolumes.DownsampleFactor"),
|
|
1.0,
|
|
TEXT("Downsamples the rendered viewport (Default = 1.0)"),
|
|
ECVF_Scalability | ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesComposition(
|
|
TEXT("r.HeterogeneousVolumes.Composition"),
|
|
0,
|
|
TEXT("Change the order of Heterogeneous Volumes composition (Default = 0)\n")
|
|
TEXT("0: Before Translucency\n")
|
|
TEXT("1: After Translucency\n")
|
|
TEXT("Requires enabling Heterogeneous Volumes Project Setting: 'Composite with Translucency'"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesUpsample(
|
|
TEXT("r.HeterogeneousVolumes.Upsample"),
|
|
2,
|
|
TEXT("Upsampling iterative smoothing (Default = 2)\n")
|
|
TEXT("0: Off\n")
|
|
TEXT("1: Nearest Neighbor\n")
|
|
TEXT("2: Bilinear"),
|
|
ECVF_Scalability | ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesFilter(
|
|
TEXT("r.HeterogeneousVolumes.Filter"),
|
|
2,
|
|
TEXT("Controls iterative smoothing filter applied during upsampling (Default = 2)\n")
|
|
TEXT("0: Off\n")
|
|
TEXT("1: Bilateral\n")
|
|
TEXT("2: Gaussian 3x3\n")
|
|
TEXT("3: Gaussian 5x5"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesFilterWidth(
|
|
TEXT("r.HeterogeneousVolumes.Filter.Width"),
|
|
3,
|
|
TEXT("Adjusts filter width of bilateral kernel (Default = 3)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesShadowMode(
|
|
TEXT("r.HeterogeneousVolumes.Shadows.Mode"),
|
|
0,
|
|
TEXT("0: Live-Shading (Default)")
|
|
TEXT("1: Preshaded Voxel Grid"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesDebug(
|
|
TEXT("r.HeterogeneousVolumes.Debug"),
|
|
0,
|
|
TEXT("Creates auxillary output buffers for debugging (Default = 0)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesHardwareRayTracing(
|
|
TEXT("r.HeterogeneousVolumes.HardwareRayTracing"),
|
|
0,
|
|
TEXT("Enables hardware ray tracing acceleration (Default = 0)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<float> CVarHeterogeneousVolumesIndirectLighting(
|
|
TEXT("r.HeterogeneousVolumes.IndirectLighting"),
|
|
0.0f,
|
|
TEXT("Enables indirect lighting (Default = 0.0)"),
|
|
ECVF_Scalability | ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesIndirectLightingMode(
|
|
TEXT("r.HeterogeneousVolumes.IndirectLighting.Mode"),
|
|
0,
|
|
TEXT("Changes where indirect is accumulated in the pipeline (Default = 0)\n")
|
|
TEXT("0: Off\n")
|
|
TEXT("1: Lighting cache\n")
|
|
TEXT("2: Single-scattering\n"),
|
|
ECVF_Scalability | ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesStochasticFiltering(
|
|
TEXT("r.HeterogeneousVolumes.StochasticFiltering"),
|
|
3,
|
|
TEXT("Configures the stochastic filtering kernel (Default = 3)\n")
|
|
TEXT("0: Disabled\n")
|
|
TEXT("1: Constant\n")
|
|
TEXT("2: Linear\n")
|
|
TEXT("3: Cubic"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesJitter(
|
|
TEXT("r.HeterogeneousVolumes.Jitter"),
|
|
1,
|
|
TEXT("Enables jitter when ray marching (Default = 1)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesMaxStepCount(
|
|
TEXT("r.HeterogeneousVolumes.MaxStepCount"),
|
|
512,
|
|
TEXT("The maximum ray-marching step count (Default = 512)"),
|
|
ECVF_Scalability | ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<float> CVarHeterogeneousVolumesMaxTraceDistance(
|
|
TEXT("r.HeterogeneousVolumes.MaxTraceDistance"),
|
|
30000.0,
|
|
TEXT("The maximum trace view-distance for direct volume rendering (Default = 30000)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<float> CVarHeterogeneousVolumesMaxShadowTraceDistance(
|
|
TEXT("r.HeterogeneousVolumes.MaxShadowTraceDistance"),
|
|
30000.0,
|
|
TEXT("The maximum shadow-trace distance (Default = 30000)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesPreshading(
|
|
TEXT("r.HeterogeneousVolumes.Preshading"),
|
|
0,
|
|
TEXT("Evaluates the material into a canonical preshaded volume before rendering the result (Default = 0)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesPreshadingMipLevel(
|
|
TEXT("r.HeterogeneousVolumes.Preshading.MipLevel"),
|
|
0,
|
|
TEXT("Statically determines the MIP-level when evaluating preshaded volume data (Default = 0)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesPreshadingVolumeResolutionX(
|
|
TEXT("r.HeterogeneousVolumes.VolumeResolution.X"),
|
|
0,
|
|
TEXT("Overrides the preshading and lighting volume resolution in X (Default = 0)")
|
|
TEXT("0: Disabled, uses per-volume attribute\n")
|
|
TEXT(">0: Overrides resolution in X\n"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesPreshadingVolumeResolutionY(
|
|
TEXT("r.HeterogeneousVolumes.VolumeResolution.Y"),
|
|
0,
|
|
TEXT("Overrides the preshading and lighting volume resolution in X (Default = 0)")
|
|
TEXT("0: Disabled, uses per-volume attribute\n")
|
|
TEXT(">0: Overrides resolution in Y\n"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesPreshadingVolumeResolutionZ(
|
|
TEXT("r.HeterogeneousVolumes.VolumeResolution.Z"),
|
|
0,
|
|
TEXT("Overrides the preshading and lighting volume resolution in X (Default = 0)")
|
|
TEXT("0: Disabled, uses per-volume attribute\n")
|
|
TEXT(">0: Overrides resolution in Z\n"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<float> CVarHeterogeneousVolumesShadowStepSize(
|
|
TEXT("r.HeterogeneousVolumes.ShadowStepSize"),
|
|
-1.0,
|
|
TEXT("The ray-marching step-size override for shadow rays (Default = -1.0, disabled)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesSparseVoxel(
|
|
TEXT("r.HeterogeneousVolumes.SparseVoxel"),
|
|
0,
|
|
TEXT("Uses sparse-voxel rendering algorithms (Default = 0)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesSparseVoxelGenerationMipBias(
|
|
TEXT("r.HeterogeneousVolumes.SparseVoxel.GenerationMipBias"),
|
|
0,
|
|
TEXT("Determines MIP bias for sparse voxel generation (Default = 0)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesSparseVoxelPerTileCulling(
|
|
TEXT("r.HeterogeneousVolumes.SparseVoxel.PerTileCulling"),
|
|
0,
|
|
TEXT("Enables sparse-voxel culling when using tiled rendering (Default = 1)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesSparseVoxelRefinement(
|
|
TEXT("r.HeterogeneousVolumes.SparseVoxel.Refinement"),
|
|
0,
|
|
TEXT("Uses hierarchical refinement to coalesce neighboring sparse-voxels (Default = 0)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<float> CVarHeterogeneousVolumesStepSize(
|
|
TEXT("r.HeterogeneousVolumes.StepSize"),
|
|
-1.0,
|
|
TEXT("The ray-marching step-size override (Default = -1.0, disabled)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesLightingCache(
|
|
TEXT("r.HeterogeneousVolumes.LightingCache"),
|
|
2,
|
|
TEXT("Enables an optimized pre-pass, caching certain volumetric rendering lighting quantities (Default = 2)\n")
|
|
TEXT("0: Disabled\n")
|
|
TEXT("1: Cache transmittance (deprecated)\n")
|
|
TEXT("2: Cache in-scattering\n"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesLightingCacheUseAVSM(
|
|
TEXT("r.HeterogeneousVolumes.LightingCache.UseAVSM"),
|
|
1,
|
|
TEXT("Enables use of AVSMs when evaluating self-shadowing (Default = 1)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesLightingCacheDownsampleFactor(
|
|
TEXT("r.HeterogeneousVolumes.LightingCache.DownsampleFactor"),
|
|
0,
|
|
TEXT("Overrides the lighting-cache downsample factor, relative to the preshading volume resolution (Default = 0)\n")
|
|
TEXT("0: Disabled, uses per-volume attribute\n")
|
|
TEXT(">0: Overrides the lighting-cache downsample factor"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesDepthSort(
|
|
TEXT("r.HeterogeneousVolumes.DepthSort"),
|
|
1,
|
|
TEXT("Iterates over volumes in depth-sorted order, based on its centroid (Default = 1)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesApplyHeightFog(
|
|
TEXT("r.HeterogeneousVolumes.HeightFog"),
|
|
1,
|
|
TEXT("Applies height fog to Heterogeneous Volumes (Default = 1)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesApplyVolumetricFog(
|
|
TEXT("r.HeterogeneousVolumes.VolumetricFog"),
|
|
1,
|
|
TEXT("Applies volumetric fog to Heterogeneous Volumes (Default = 1)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesApplyFogInscatteringMode(
|
|
TEXT("r.HeterogeneousVolumes.ApplyFogInscattering"),
|
|
2,
|
|
TEXT("Determines the method for applying fog in-scattering (default = 2)\n")
|
|
TEXT("0: Off\n")
|
|
TEXT("1: Reference\n")
|
|
TEXT("2: Linear approximation\n"),
|
|
ECVF_Scalability | ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesUseAnalyticDerivatives(
|
|
TEXT("r.HeterogeneousVolumes.UseAnalyticDerivatives"),
|
|
0,
|
|
TEXT("Enables support for analytic derivatives (Default = 0)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesVelocity(
|
|
TEXT("r.HeterogeneousVolumes.Velocity"),
|
|
0,
|
|
TEXT("Writes Heterogeneous Volumes velocity to the feature buffer (Default = 0)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarHeterogeneousVolumesCLOD(
|
|
TEXT("r.HeterogeneousVolumes.CLOD"),
|
|
1,
|
|
TEXT("Uses Continuous Level-of-Detail to accelerate rendering (Default = 1)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<float> CVarHeterogeneousVolumesCLODBias(
|
|
TEXT("r.HeterogeneousVolumes.CLOD.Bias"),
|
|
0.0,
|
|
TEXT("Biases evaluation result when computing Continuous Level-of-Detail (Default = 0.0)\n")
|
|
TEXT("> 0: Coarser\n")
|
|
TEXT("< 0: Sharper\n"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
DECLARE_GPU_STAT_NAMED(HeterogeneousVolumeShadowsStat, TEXT("HeterogeneousVolumeShadows"));
|
|
DECLARE_GPU_STAT_NAMED(HeterogeneousVolumesStat, TEXT("HeterogeneousVolumes"));
|
|
|
|
static bool IsHeterogeneousVolumesEnabled()
|
|
{
|
|
return CVarHeterogeneousVolumes.GetValueOnRenderThread() != 0;
|
|
}
|
|
|
|
bool ShouldHeterogeneousVolumesCastShadows()
|
|
{
|
|
return CVarHeterogeneousVolumesShadows.GetValueOnAnyThread() != 0;
|
|
}
|
|
|
|
bool ShouldCompositeHeterogeneousVolumesWithTranslucency()
|
|
{
|
|
return CVarTranslucencyHeterogeneousVolumes.GetValueOnAnyThread() != 0;
|
|
}
|
|
|
|
namespace HeterogeneousVolumes
|
|
{
|
|
ETranslucencyCompositingMode GetTranslucencyCompositingMode()
|
|
{
|
|
int32 ClampedValue = FMath::Clamp(CVarTranslucencyHeterogeneousVolumes.GetValueOnRenderThread(), 0, 2);
|
|
return static_cast<ETranslucencyCompositingMode>(ClampedValue);
|
|
}
|
|
}
|
|
|
|
EHeterogeneousVolumesCompositionType GetHeterogeneousVolumesCompositionType()
|
|
{
|
|
int32 CompositionOrder = CVarHeterogeneousVolumesComposition.GetValueOnRenderThread();
|
|
switch (CompositionOrder)
|
|
{
|
|
case 0:
|
|
default:
|
|
return EHeterogeneousVolumesCompositionType::BeforeTranslucent;
|
|
case 1:
|
|
return EHeterogeneousVolumesCompositionType::AfterTranslucent;
|
|
}
|
|
}
|
|
|
|
EHeterogeneousVolumesCompositionType GetHeterogeneousVolumesComposition()
|
|
{
|
|
// Composition order can only be modified if the Project Setting is enabled
|
|
if (!ShouldCompositeHeterogeneousVolumesWithTranslucency())
|
|
{
|
|
return EHeterogeneousVolumesCompositionType::AfterTranslucent;
|
|
}
|
|
|
|
return GetHeterogeneousVolumesCompositionType();
|
|
}
|
|
|
|
bool ShouldRenderHeterogeneousVolumes(
|
|
const FScene* Scene
|
|
)
|
|
{
|
|
return IsHeterogeneousVolumesEnabled()
|
|
&& Scene != nullptr
|
|
&& DoesPlatformSupportHeterogeneousVolumes(Scene->GetShaderPlatform());
|
|
}
|
|
|
|
bool ShouldRenderHeterogeneousVolumesForAnyView(
|
|
const TArrayView<FViewInfo>& Views
|
|
)
|
|
{
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
|
|
{
|
|
if (ShouldRenderHeterogeneousVolumesForView(Views, ViewIndex))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ShouldRenderHeterogeneousVolumesForView(
|
|
const FViewInfo& View
|
|
)
|
|
{
|
|
return IsHeterogeneousVolumesEnabled()
|
|
&& IStereoRendering::IsAPrimaryView(View)
|
|
&& !View.HeterogeneousVolumesMeshBatches.IsEmpty()
|
|
&& View.Family
|
|
&& !View.bIsReflectionCapture;
|
|
}
|
|
|
|
int32 GetPrimaryViewIndex(const TArrayView<FViewInfo>& Views)
|
|
{
|
|
int32 PrimaryViewIndex = Views.Num() - 1;
|
|
while (PrimaryViewIndex >= 0)
|
|
{
|
|
if (IStereoRendering::IsAPrimaryView(Views[PrimaryViewIndex]))
|
|
{
|
|
break;
|
|
}
|
|
PrimaryViewIndex--;
|
|
}
|
|
|
|
return PrimaryViewIndex;
|
|
}
|
|
|
|
FViewInfo* GetPrimaryViewForView(
|
|
const TArrayView<FViewInfo>& Views,
|
|
int32 ViewIndex
|
|
)
|
|
{
|
|
check(ViewIndex < Views.Num())
|
|
FViewInfo* PrimaryView = &Views[ViewIndex];
|
|
|
|
// For stereo views, mesh batches will only be defined on the primary
|
|
if (PrimaryView->bIsInstancedStereoEnabled && IStereoRendering::IsStereoEyeView(*PrimaryView) && !IStereoRendering::IsAPrimaryView(*PrimaryView))
|
|
{
|
|
int32 PrimaryViewIndex = GetPrimaryViewIndex(Views);
|
|
check(PrimaryViewIndex >= 0);
|
|
PrimaryView = &Views[PrimaryViewIndex];
|
|
}
|
|
|
|
return IStereoRendering::IsAPrimaryView(*PrimaryView) ? PrimaryView : nullptr;
|
|
}
|
|
|
|
bool ShouldRenderHeterogeneousVolumesForView(
|
|
const TArrayView<FViewInfo>& Views,
|
|
int32 ViewIndex
|
|
)
|
|
{
|
|
FViewInfo* PrimaryView = GetPrimaryViewForView(Views, ViewIndex);
|
|
return PrimaryView ? ShouldRenderHeterogeneousVolumesForView(*PrimaryView) : false;
|
|
}
|
|
|
|
TArray<FVolumetricMeshBatch, SceneRenderingAllocator> GetHeterogeneousVolumesMeshBatches(
|
|
const TArrayView<FViewInfo>& Views,
|
|
int32 ViewIndex
|
|
)
|
|
{
|
|
FViewInfo* PrimaryView = GetPrimaryViewForView(Views, ViewIndex);
|
|
return PrimaryView ? PrimaryView->HeterogeneousVolumesMeshBatches : TArray<FVolumetricMeshBatch, SceneRenderingAllocator>();
|
|
}
|
|
|
|
bool ShouldRenderHeterogeneousVolumesAsHoldoutForView(
|
|
const FViewInfo& View
|
|
)
|
|
{
|
|
check(IStereoRendering::IsAPrimaryView(View));
|
|
|
|
// This query returns true if any volume is marked as a holdout; otherwise, the query returns false
|
|
if (ShouldRenderHeterogeneousVolumesForView(View))
|
|
{
|
|
const TArray<FVolumetricMeshBatch, SceneRenderingAllocator>& MeshBatches = View.HeterogeneousVolumesMeshBatches;
|
|
for (int32 MeshBatchIndex = 0; MeshBatchIndex < MeshBatches.Num(); ++MeshBatchIndex)
|
|
{
|
|
const FMeshBatch* Mesh = MeshBatches[MeshBatchIndex].Mesh;
|
|
const FPrimitiveSceneProxy* PrimitiveSceneProxy = MeshBatches[MeshBatchIndex].Proxy;
|
|
if (ShouldRenderMeshBatchWithHeterogeneousVolumes(Mesh, PrimitiveSceneProxy, View.GetFeatureLevel()))
|
|
{
|
|
for (int32 VolumeIndex = 0; VolumeIndex < Mesh->Elements.Num(); ++VolumeIndex)
|
|
{
|
|
const IHeterogeneousVolumeInterface* HeterogeneousVolume = (IHeterogeneousVolumeInterface*)Mesh->Elements[VolumeIndex].UserData;
|
|
if (HeterogeneousVolumes::IsHoldout(HeterogeneousVolume))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool DoesMaterialShaderSupportHeterogeneousVolumes(const FMaterialShaderParameters& MaterialShaderParameters)
|
|
{
|
|
return (MaterialShaderParameters.MaterialDomain == MD_Volume)
|
|
&& MaterialShaderParameters.bIsUsedWithHeterogeneousVolumes;
|
|
}
|
|
|
|
bool DoesMaterialShaderSupportHeterogeneousVolumes(const FMaterial& Material)
|
|
{
|
|
return (Material.GetMaterialDomain() == MD_Volume)
|
|
&& Material.IsUsedWithHeterogeneousVolumes();
|
|
}
|
|
|
|
bool ShouldRenderMeshBatchWithHeterogeneousVolumes(
|
|
const FMeshBatch* Mesh,
|
|
const FPrimitiveSceneProxy* Proxy,
|
|
ERHIFeatureLevel::Type FeatureLevel
|
|
)
|
|
{
|
|
check(Mesh);
|
|
check(Proxy);
|
|
check(Mesh->MaterialRenderProxy);
|
|
|
|
const FMaterialRenderProxy* MaterialRenderProxy = Mesh->MaterialRenderProxy;
|
|
const FMaterial& Material = MaterialRenderProxy->GetMaterialWithFallback(FeatureLevel, MaterialRenderProxy);
|
|
return IsHeterogeneousVolumesEnabled()
|
|
&& Proxy->IsHeterogeneousVolume()
|
|
&& DoesMaterialShaderSupportHeterogeneousVolumes(Material);
|
|
}
|
|
|
|
namespace HeterogeneousVolumes
|
|
{
|
|
// CVars
|
|
int32 GetDownsampleFactor()
|
|
{
|
|
return FMath::Clamp(CVarHeterogeneousVolumesDownsampleFactor.GetValueOnRenderThread(), 1, 8);
|
|
}
|
|
|
|
int32 GetUpsampleMode()
|
|
{
|
|
return FMath::Clamp(CVarHeterogeneousVolumesUpsample.GetValueOnRenderThread(), 0, 2);
|
|
}
|
|
|
|
int32 GetFilterMode()
|
|
{
|
|
return FMath::Clamp(CVarHeterogeneousVolumesFilter.GetValueOnRenderThread(), 0, 3);
|
|
}
|
|
|
|
int32 GetFilterWidth()
|
|
{
|
|
return FMath::Clamp(CVarHeterogeneousVolumesFilterWidth.GetValueOnRenderThread(), 0, 11);
|
|
}
|
|
|
|
FIntPoint GetDownsampledResolution(FIntPoint Resolution, int32 DownsampleFactor)
|
|
{
|
|
return FIntPoint::DivideAndRoundUp(Resolution, DownsampleFactor);
|
|
}
|
|
|
|
FIntPoint GetScaledViewRect(FIntRect ViewRect)
|
|
{
|
|
return GetDownsampledResolution(ViewRect.Size(), GetDownsampleFactor());
|
|
}
|
|
|
|
FIntVector GetVolumeResolution(const IHeterogeneousVolumeInterface* Interface)
|
|
{
|
|
FIntVector VolumeResolution = Interface->GetVoxelResolution();
|
|
|
|
FIntVector OverrideVolumeResolution = FIntVector(
|
|
CVarHeterogeneousVolumesPreshadingVolumeResolutionX.GetValueOnRenderThread(),
|
|
CVarHeterogeneousVolumesPreshadingVolumeResolutionY.GetValueOnRenderThread(),
|
|
CVarHeterogeneousVolumesPreshadingVolumeResolutionZ.GetValueOnRenderThread());
|
|
|
|
VolumeResolution.X = OverrideVolumeResolution.X > 0 ? OverrideVolumeResolution.X : VolumeResolution.X;
|
|
VolumeResolution.Y = OverrideVolumeResolution.Y > 0 ? OverrideVolumeResolution.Y : VolumeResolution.Y;
|
|
VolumeResolution.Z = OverrideVolumeResolution.Z > 0 ? OverrideVolumeResolution.Z : VolumeResolution.Z;
|
|
|
|
// Clamp each dimension to [1, 1024]
|
|
VolumeResolution.X = FMath::Clamp(VolumeResolution.X, 1, 1024);
|
|
VolumeResolution.Y = FMath::Clamp(VolumeResolution.Y, 1, 1024);
|
|
VolumeResolution.Z = FMath::Clamp(VolumeResolution.Z, 1, 1024);
|
|
return VolumeResolution;
|
|
}
|
|
|
|
float GetShadowStepSize()
|
|
{
|
|
return CVarHeterogeneousVolumesShadowStepSize.GetValueOnRenderThread();
|
|
}
|
|
|
|
float GetMaxTraceDistance()
|
|
{
|
|
return CVarHeterogeneousVolumesMaxTraceDistance.GetValueOnRenderThread();
|
|
}
|
|
|
|
float GetMaxShadowTraceDistance()
|
|
{
|
|
return CVarHeterogeneousVolumesMaxShadowTraceDistance.GetValueOnRenderThread();
|
|
}
|
|
|
|
float GetStepSize()
|
|
{
|
|
return CVarHeterogeneousVolumesStepSize.GetValueOnRenderThread();
|
|
}
|
|
|
|
float GetMaxStepCount()
|
|
{
|
|
return CVarHeterogeneousVolumesMaxStepCount.GetValueOnRenderThread();
|
|
}
|
|
|
|
int32 GetMipLevel()
|
|
{
|
|
return CVarHeterogeneousVolumesPreshadingMipLevel.GetValueOnRenderThread();
|
|
}
|
|
|
|
uint32 GetSparseVoxelMipBias()
|
|
{
|
|
// TODO: Clamp based on texture dimension..
|
|
return FMath::Clamp(CVarHeterogeneousVolumesSparseVoxelGenerationMipBias.GetValueOnRenderThread(), 0, 10);
|
|
}
|
|
|
|
int32 GetDebugMode()
|
|
{
|
|
return CVarHeterogeneousVolumesDebug.GetValueOnRenderThread();
|
|
}
|
|
|
|
EShadowMode GetShadowMode()
|
|
{
|
|
return static_cast<EShadowMode>(CVarHeterogeneousVolumesShadowMode.GetValueOnRenderThread());
|
|
}
|
|
|
|
EStochasticFilteringMode GetStochasticFilteringMode()
|
|
{
|
|
return static_cast<EStochasticFilteringMode>(CVarHeterogeneousVolumesStochasticFiltering.GetValueOnRenderThread());
|
|
}
|
|
|
|
bool UseSparseVoxelPipeline()
|
|
{
|
|
return CVarHeterogeneousVolumesSparseVoxel.GetValueOnAnyThread() != 0;
|
|
}
|
|
|
|
bool ShouldRefineSparseVoxels()
|
|
{
|
|
return CVarHeterogeneousVolumesSparseVoxelRefinement.GetValueOnRenderThread() != 0;
|
|
}
|
|
|
|
bool UseSparseVoxelPerTileCulling()
|
|
{
|
|
return CVarHeterogeneousVolumesSparseVoxelPerTileCulling.GetValueOnAnyThread() != 0;
|
|
}
|
|
|
|
int32 GetLightingCacheMode()
|
|
{
|
|
// Force in-scattering lighting cache for all but cinematic scalability
|
|
if (GetScalabilityMode() != EScalabilityMode::Cinematic)
|
|
{
|
|
return 2;
|
|
}
|
|
|
|
int32 Value = FMath::Clamp(CVarHeterogeneousVolumesLightingCache.GetValueOnAnyThread(), 0, 2);
|
|
return Value;
|
|
}
|
|
|
|
bool UseAdaptiveVolumetricShadowMapForSelfShadowing(const FPrimitiveSceneProxy* PrimitiveSceneProxy)
|
|
{
|
|
bool bUseAVSM = CVarHeterogeneousVolumesLightingCacheUseAVSM.GetValueOnRenderThread() != 0;
|
|
bool bPrimitiveCastsDynamicShadows = PrimitiveSceneProxy->CastsDynamicShadow();
|
|
return ShouldHeterogeneousVolumesCastShadows() && bUseAVSM && bPrimitiveCastsDynamicShadows;
|
|
}
|
|
|
|
bool UseLightingCacheForInscattering()
|
|
{
|
|
return GetLightingCacheMode() == 2;
|
|
}
|
|
|
|
bool UseLightingCacheForTransmittance()
|
|
{
|
|
return GetLightingCacheMode() == 1;
|
|
}
|
|
|
|
bool ShouldJitter()
|
|
{
|
|
return CVarHeterogeneousVolumesJitter.GetValueOnRenderThread() != 0;
|
|
}
|
|
|
|
bool UseHardwareRayTracing()
|
|
{
|
|
return IsRayTracingEnabled()
|
|
&& (CVarHeterogeneousVolumesHardwareRayTracing.GetValueOnRenderThread() != 0);
|
|
}
|
|
|
|
bool UseIndirectLighting()
|
|
{
|
|
return CVarHeterogeneousVolumesIndirectLighting.GetValueOnRenderThread() != 0;
|
|
}
|
|
|
|
float GetIndirectLightingFactor()
|
|
{
|
|
return FMath::Max(CVarHeterogeneousVolumesIndirectLighting.GetValueOnRenderThread(), 0.0f);
|
|
}
|
|
|
|
EIndirectLightingMode GetIndirectLightingMode()
|
|
{
|
|
return static_cast<EIndirectLightingMode>(FMath::Clamp(CVarHeterogeneousVolumesIndirectLightingMode.GetValueOnRenderThread(), 0, 2));
|
|
}
|
|
|
|
bool ShouldApplyHeightFog()
|
|
{
|
|
return CVarHeterogeneousVolumesApplyHeightFog.GetValueOnRenderThread() != 0;
|
|
}
|
|
|
|
bool ShouldApplyVolumetricFog()
|
|
{
|
|
return CVarHeterogeneousVolumesApplyVolumetricFog.GetValueOnRenderThread() != 0;
|
|
}
|
|
|
|
EFogMode GetFogInscatteringMode()
|
|
{
|
|
return static_cast<EFogMode>(FMath::Clamp(CVarHeterogeneousVolumesApplyFogInscatteringMode.GetValueOnRenderThread(), 0, 2));
|
|
}
|
|
|
|
bool UseAnalyticDerivatives()
|
|
{
|
|
return CVarHeterogeneousVolumesUseAnalyticDerivatives.GetValueOnRenderThread() != 0;
|
|
}
|
|
|
|
bool ShouldWriteVelocity()
|
|
{
|
|
return CVarHeterogeneousVolumesVelocity.GetValueOnRenderThread() != 0;
|
|
}
|
|
|
|
bool UseContinuousLOD()
|
|
{
|
|
return CVarHeterogeneousVolumesCLOD.GetValueOnRenderThread() != 0;
|
|
}
|
|
|
|
float GetCLODBias()
|
|
{
|
|
return CVarHeterogeneousVolumesCLODBias.GetValueOnRenderThread();
|
|
}
|
|
|
|
// Convenience Utils
|
|
int GetVoxelCount(FIntVector VolumeResolution)
|
|
{
|
|
return VolumeResolution.X * VolumeResolution.Y * VolumeResolution.Z;
|
|
}
|
|
|
|
int GetVoxelCount(const FRDGTextureDesc& TextureDesc)
|
|
{
|
|
return TextureDesc.Extent.X * TextureDesc.Extent.Y * TextureDesc.Depth;
|
|
}
|
|
|
|
FIntVector GetMipVolumeResolution(FIntVector VolumeResolution, uint32 MipLevel)
|
|
{
|
|
return FIntVector(
|
|
FMath::Max(VolumeResolution.X >> MipLevel, 1),
|
|
FMath::Max(VolumeResolution.Y >> MipLevel, 1),
|
|
FMath::Max(VolumeResolution.Z >> MipLevel, 1)
|
|
);
|
|
}
|
|
|
|
float CalcLODBias(const IHeterogeneousVolumeInterface* HeterogeneousVolume)
|
|
{
|
|
return HeterogeneousVolume->GetMipBias() + HeterogeneousVolumes::GetCLODBias();
|
|
}
|
|
|
|
FLODValue CalcLOD(
|
|
const HeterogeneousVolumes::FLODInfo& LODInfo,
|
|
const IHeterogeneousVolumeInterface* HeterogeneousVolume
|
|
)
|
|
{
|
|
if (!HeterogeneousVolumes::UseContinuousLOD())
|
|
{
|
|
return FLODValue();
|
|
}
|
|
|
|
FBoxSphereBounds WorldBounds = HeterogeneousVolume->GetBounds();
|
|
FIntVector VoxelResolution = HeterogeneousVolume->GetVoxelResolution();
|
|
float VoxelResolutionMin = VoxelResolution.GetMin();
|
|
|
|
const float MaxLOD = FMath::Floor(FMath::Log2(VoxelResolutionMin));
|
|
FLODValue LODValue;
|
|
LODValue.LOD = MaxLOD;
|
|
if (!LODInfo.bIsPerspective)
|
|
{
|
|
float VolumeRatio = FVector(LODInfo.WorldSceneBounds.BoxExtent / WorldBounds.BoxExtent).Length();
|
|
float ViewLODValue = FMath::Log2(VolumeRatio) + HeterogeneousVolume->GetMipBias() + HeterogeneousVolumes::GetCLODBias();
|
|
ViewLODValue = FMath::Max(ViewLODValue, 0);
|
|
|
|
LODValue.LOD = FMath::Min(ViewLODValue, LODValue.LOD);
|
|
}
|
|
else if (LODInfo.WorldShadowFrustum.IntersectBox(WorldBounds.Origin, WorldBounds.BoxExtent))
|
|
{
|
|
// Determine the pixel-width at the near-plane
|
|
float TanHalfFOV = FMath::Tan(LODInfo.FOV * 0.5);
|
|
float HalfViewWidth = LODInfo.ViewRect.Width() * 0.5 / LODInfo.DownsampleFactor;
|
|
float PixelWidth = TanHalfFOV / HalfViewWidth;
|
|
|
|
// Project to nearest distance of volume bounds
|
|
float Distance = FMath::Max(FVector::Dist(WorldBounds.Origin, LODInfo.WorldOrigin) - WorldBounds.SphereRadius, LODInfo.NearClippingDistance);
|
|
float ProjectedPixelWidth = Distance * PixelWidth;
|
|
|
|
// MIP is defined as the log of the ratio of native voxel resolution to pixel-coverage of volume bounds
|
|
float PixelWidthCoverage = (2.0 * WorldBounds.BoxExtent.GetMax()) / ProjectedPixelWidth;
|
|
|
|
// Clamp LOD to heighten the effect on foreground elements before applying bias controls
|
|
float ViewLODValue = FMath::Max(FMath::Log2(VoxelResolutionMin / PixelWidthCoverage), 0.0f);
|
|
|
|
LODValue.LOD = FMath::Min(ViewLODValue, LODValue.LOD);
|
|
}
|
|
|
|
float TotalBias = HeterogeneousVolume->GetMipBias() + HeterogeneousVolumes::GetCLODBias();
|
|
LODValue.Bias = FMath::Min(TotalBias, MaxLOD - LODValue.LOD);
|
|
|
|
return LODValue;
|
|
}
|
|
|
|
FLODValue CalcLOD(const FSceneView& View, const IHeterogeneousVolumeInterface* HeterogeneousVolume)
|
|
{
|
|
FLODInfo LODInfo;
|
|
// TODO: Not supporting orthographic projection for now
|
|
LODInfo.bIsPerspective = true;
|
|
LODInfo.WorldSceneBounds = FBoxSphereBounds(EForceInit::ForceInitToZero);
|
|
|
|
LODInfo.WorldOrigin = View.ViewMatrices.GetViewOrigin();
|
|
LODInfo.ViewRect = View.UnconstrainedViewRect;
|
|
LODInfo.WorldShadowFrustum = View.ViewFrustum;
|
|
LODInfo.FOV = FMath::DegreesToRadians(View.FOV);
|
|
LODInfo.NearClippingDistance = View.NearClippingDistance;
|
|
LODInfo.DownsampleFactor = HeterogeneousVolumes::GetDownsampleFactor();
|
|
|
|
return CalcLOD(LODInfo, HeterogeneousVolume);
|
|
}
|
|
|
|
float CalcLODFactor(float LODValue, float LODBias)
|
|
{
|
|
return FMath::Pow(2, LODValue + LODBias);
|
|
}
|
|
|
|
float CalcLODFactor(
|
|
const HeterogeneousVolumes::FLODInfo& LODInfo,
|
|
const IHeterogeneousVolumeInterface* HeterogeneousVolume
|
|
)
|
|
{
|
|
FLODValue LODValue = CalcLOD(LODInfo, HeterogeneousVolume);
|
|
return CalcLODFactor(LODValue.LOD, LODValue.Bias);
|
|
}
|
|
|
|
float CalcLODFactor(const FSceneView& View, const IHeterogeneousVolumeInterface* HeterogeneousVolume)
|
|
{
|
|
FLODValue LODValue = CalcLOD(View, HeterogeneousVolume);
|
|
return CalcLODFactor(LODValue.LOD, LODValue.Bias);
|
|
}
|
|
|
|
FIntVector GetLightingCacheResolution(const IHeterogeneousVolumeInterface* RenderInterface, FLODValue LODValue)
|
|
{
|
|
float LODFactor = CalcLODFactor(LODValue.LOD, 0.0f);
|
|
float OverrideDownsampleFactor = CVarHeterogeneousVolumesLightingCacheDownsampleFactor.GetValueOnRenderThread();
|
|
float DownsampleFactor = OverrideDownsampleFactor > 0.0 ? OverrideDownsampleFactor : RenderInterface->GetLightingDownsampleFactor() * LODFactor;
|
|
DownsampleFactor = FMath::Max(DownsampleFactor, 0.125);
|
|
|
|
FVector VolumeResolution = FVector(GetVolumeResolution(RenderInterface));
|
|
FIntVector LightingCacheResolution = FIntVector(VolumeResolution / DownsampleFactor);
|
|
LightingCacheResolution.X = FMath::Clamp(LightingCacheResolution.X, 1, 1024);
|
|
LightingCacheResolution.Y = FMath::Clamp(LightingCacheResolution.Y, 1, 1024);
|
|
LightingCacheResolution.Z = FMath::Clamp(LightingCacheResolution.Z, 1, 512);
|
|
return LightingCacheResolution;
|
|
}
|
|
|
|
bool IsHoldout(const IHeterogeneousVolumeInterface* HeterogeneousVolumeInterface)
|
|
{
|
|
return IsPostProcessingWithAlphaChannelSupported() && HeterogeneousVolumeInterface->IsHoldout();
|
|
}
|
|
}
|
|
|
|
bool ShouldBuildVoxelGrids(const FScene* Scene)
|
|
{
|
|
// TODO: Build the light list once
|
|
if (ShouldHeterogeneousVolumesCastShadows())
|
|
{
|
|
for (auto LightIt = Scene->Lights.CreateConstIterator(); LightIt; ++LightIt)
|
|
{
|
|
if (LightIt->LightSceneInfo->Proxy->CastsVolumetricShadow())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ShouldCompositeHeterogeneousVolumesWithTranslucency())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (HeterogeneousVolumes::GetShadowMode() == HeterogeneousVolumes::EShadowMode::VoxelGrid)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ShouldCacheVoxelGrids(const FScene* Scene, FSceneViewState* ViewState)
|
|
{
|
|
// If the caching structure exists
|
|
if (ViewState == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (HeterogeneousVolumes::GetShadowMode() == HeterogeneousVolumes::EShadowMode::VoxelGrid)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// TODO: If any light supports ray tracing
|
|
|
|
return false;
|
|
}
|
|
|
|
DECLARE_CYCLE_STAT(TEXT("Heterogeneous Volumes Render"), STATGROUP_HeterogeneousVolumesRender, STATGROUP_HeterogeneousVolumesRT);
|
|
|
|
void RenderHeterogeneousVolumeShadows(
|
|
FRDGBuilder& GraphBuilder,
|
|
FScene* Scene,
|
|
const FSceneTextures& SceneTextures,
|
|
FViewInfo& View,
|
|
TArray<FVisibleLightInfo, SceneRenderingAllocator>& VisibleLightInfos
|
|
)
|
|
{
|
|
RDG_EVENT_SCOPE_STAT(GraphBuilder, HeterogeneousVolumeShadowsStat, "HeterogeneousVolumeShadows");
|
|
RDG_GPU_STAT_SCOPE(GraphBuilder, HeterogeneousVolumeShadowsStat);
|
|
SCOPED_NAMED_EVENT(HeterogeneousVolumes, FColor::Emerald);
|
|
|
|
if (HeterogeneousVolumes::GetShadowMode() == HeterogeneousVolumes::EShadowMode::LiveShading)
|
|
{
|
|
RenderAdaptiveVolumetricShadowMapWithLiveShading(
|
|
GraphBuilder,
|
|
SceneTextures,
|
|
Scene,
|
|
View,
|
|
VisibleLightInfos
|
|
);
|
|
}
|
|
}
|
|
|
|
void FDeferredShadingSceneRenderer::RenderHeterogeneousVolumeShadows(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FSceneTextures& SceneTextures
|
|
)
|
|
{
|
|
if (!ShouldBuildVoxelGrids(Scene))
|
|
{
|
|
return;
|
|
}
|
|
|
|
RDG_EVENT_SCOPE_STAT(GraphBuilder, HeterogeneousVolumeShadowsStat, "HeterogeneousVolumeShadows");
|
|
RDG_GPU_STAT_SCOPE(GraphBuilder, HeterogeneousVolumeShadowsStat);
|
|
SCOPED_NAMED_EVENT(HeterogeneousVolumes, FColor::Emerald);
|
|
|
|
TRDGUniformBufferRef<FOrthoVoxelGridUniformBufferParameters> OrthoGridUniformBuffer = nullptr;
|
|
TRDGUniformBufferRef<FFrustumVoxelGridUniformBufferParameters> FrustumGridUniformBuffer = nullptr;
|
|
if (HeterogeneousVolumes::GetShadowMode() == HeterogeneousVolumes::EShadowMode::VoxelGrid)
|
|
{
|
|
FVoxelGridBuildOptions BuildOptions;
|
|
BuildOptions.VoxelGridBuildMode = EVoxelGridBuildMode::Shadows;
|
|
BuildOptions.ShadingRateInFrustum = HeterogeneousVolumes::GetShadingRateForShadows();
|
|
BuildOptions.ShadingRateOutOfFrustum = HeterogeneousVolumes::GetOutOfFrustumShadingRateForShadows();
|
|
BuildOptions.bBuildOrthoGrid = true;
|
|
BuildOptions.bBuildFrustumGrid = false;
|
|
BuildOptions.bUseProjectedPixelSizeForOrthoGrid = true;
|
|
BuildOptions.bJitter = HeterogeneousVolumes::EnableJitterForShadows();
|
|
|
|
BuildOrthoVoxelGrid(GraphBuilder, Scene, Views, VisibleLightInfos, BuildOptions, OrthoGridUniformBuffer);
|
|
BuildFrustumVoxelGrid(GraphBuilder, Scene, Views[0], BuildOptions, FrustumGridUniformBuffer);
|
|
}
|
|
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
|
{
|
|
FViewInfo& View = Views[ViewIndex];
|
|
|
|
if (HeterogeneousVolumes::GetTranslucencyCompositingMode() == HeterogeneousVolumes::ETranslucencyCompositingMode::VolumetricShadowMap)
|
|
{
|
|
if (HeterogeneousVolumes::GetShadowMode() == HeterogeneousVolumes::EShadowMode::LiveShading)
|
|
{
|
|
RenderAdaptiveVolumetricCameraMapWithLiveShading(
|
|
GraphBuilder,
|
|
SceneTextures,
|
|
Scene,
|
|
View
|
|
);
|
|
}
|
|
else
|
|
{
|
|
RenderAdaptiveVolumetricCameraMapWithVoxelGrid(
|
|
GraphBuilder,
|
|
// Scene data
|
|
SceneTextures,
|
|
Scene,
|
|
View,
|
|
// Volume data
|
|
OrthoGridUniformBuffer,
|
|
FrustumGridUniformBuffer
|
|
);
|
|
}
|
|
}
|
|
|
|
if (ShouldHeterogeneousVolumesCastShadows())
|
|
{
|
|
if (HeterogeneousVolumes::GetShadowMode() == HeterogeneousVolumes::EShadowMode::LiveShading)
|
|
{
|
|
// This path is taken care of now in ShadowDepthRendering
|
|
#if 0
|
|
RenderAdaptiveVolumetricShadowMapWithLiveShading(
|
|
GraphBuilder,
|
|
// Scene data
|
|
SceneTextures,
|
|
Scene,
|
|
View,
|
|
// Light data
|
|
VisibleLightInfos
|
|
);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
RenderAdaptiveVolumetricShadowMapWithVoxelGrid(
|
|
GraphBuilder,
|
|
// Scene data
|
|
SceneTextures,
|
|
Scene,
|
|
View,
|
|
// Shadow Data
|
|
VisibleLightInfos,
|
|
VirtualShadowMapArray,
|
|
// Volume data
|
|
OrthoGridUniformBuffer,
|
|
FrustumGridUniformBuffer
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
FSceneViewState* ViewState = Views[0].ViewState;
|
|
if (ShouldCacheVoxelGrids(Scene, ViewState))
|
|
{
|
|
ViewState->OrthoVoxelGridUniformBuffer = OrthoGridUniformBuffer;
|
|
ViewState->FrustumVoxelGridUniformBuffer = FrustumGridUniformBuffer;
|
|
}
|
|
}
|
|
|
|
void FDeferredShadingSceneRenderer::RenderHeterogeneousVolumes(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FSceneTextures& SceneTextures
|
|
)
|
|
{
|
|
RDG_EVENT_SCOPE_STAT(GraphBuilder, HeterogeneousVolumesStat, "HeterogeneousVolumes");
|
|
RDG_GPU_STAT_SCOPE(GraphBuilder, HeterogeneousVolumesStat);
|
|
SCOPED_NAMED_EVENT(HeterogeneousVolumes, FColor::Emerald);
|
|
|
|
TRDGUniformBufferRef<FOrthoVoxelGridUniformBufferParameters> OrthoGridUniformBuffer = HeterogeneousVolumes::GetOrthoVoxelGridUniformBuffer(GraphBuilder, Views[0].ViewState);
|
|
TRDGUniformBufferRef<FFrustumVoxelGridUniformBufferParameters> FrustumGridUniformBuffer = HeterogeneousVolumes::GetFrustumVoxelGridUniformBuffer(GraphBuilder, Views[0].ViewState);
|
|
|
|
FRDGTextureRef HeterogeneousVolumeRadiance = GSystemTextures.GetBlackDummy(GraphBuilder);
|
|
FRDGTextureRef HeterogeneousVolumeHoldout = GSystemTextures.GetBlackDummy(GraphBuilder);
|
|
FRDGTextureRef HeterogeneousVolumeBeerShadowMap = GSystemTextures.GetBlackDummy(GraphBuilder);
|
|
FRDGTextureRef HeterogeneousVolumeVelocity = SceneTextures.Velocity;
|
|
if (ShouldRenderHeterogeneousVolumesForAnyView(Views))
|
|
{
|
|
FRDGTextureDesc Desc = SceneTextures.Color.Target->Desc;
|
|
Desc.Extent = HeterogeneousVolumes::GetDownsampledResolution(Desc.Extent, HeterogeneousVolumes::GetDownsampleFactor());
|
|
Desc.Format = PF_FloatRGBA;
|
|
Desc.Flags &= ~(TexCreate_FastVRAM);
|
|
HeterogeneousVolumeRadiance = GraphBuilder.CreateTexture(Desc, TEXT("HeterogeneousVolumes"));
|
|
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(HeterogeneousVolumeRadiance), FLinearColor::Black);
|
|
|
|
if (IsPrimitiveAlphaHoldoutEnabledForAnyView(Views))
|
|
{
|
|
Desc.Format = PF_R8;
|
|
HeterogeneousVolumeHoldout = GraphBuilder.CreateTexture(Desc, TEXT("HeterogeneousVolume.Holdout"));
|
|
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(HeterogeneousVolumeHoldout), FLinearColor::Black);
|
|
}
|
|
|
|
// Satisfy binding UAV binding requirement
|
|
HeterogeneousVolumeBeerShadowMap = HeterogeneousVolumeRadiance;
|
|
if (HeterogeneousVolumes::GetTranslucencyCompositingMode() == HeterogeneousVolumes::ETranslucencyCompositingMode::BeerShadowMap)
|
|
{
|
|
Desc.Format = PF_FloatRGBA;
|
|
HeterogeneousVolumeBeerShadowMap = GraphBuilder.CreateTexture(Desc, TEXT("HeterogeneousVolume.BeerShadowMap"));
|
|
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(HeterogeneousVolumeBeerShadowMap), FVector4d(HeterogeneousVolumes::GetMaxTraceDistance(), 0.0, 1.0, 1.0));
|
|
}
|
|
|
|
// Create velocity texture if it doesn't already exist
|
|
if (!HasBeenProduced(HeterogeneousVolumeVelocity))
|
|
{
|
|
bool bRequireMultiView = false; // ViewFamily required
|
|
FRDGTextureDesc VelocityDesc = FVelocityRendering::GetRenderTargetDesc(Scene->GetShaderPlatform(), SceneTextures.Color.Target->Desc.Extent, bRequireMultiView);
|
|
HeterogeneousVolumeVelocity = GraphBuilder.CreateTexture(VelocityDesc, TEXT("HeterogeneousVolume.Velocity"));
|
|
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(HeterogeneousVolumeVelocity), FLinearColor::Black);
|
|
}
|
|
}
|
|
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
|
{
|
|
FViewInfo& View = Views[ViewIndex];
|
|
if (ShouldRenderHeterogeneousVolumesForView(Views, ViewIndex))
|
|
{
|
|
if (HeterogeneousVolumes::GetDebugMode() != 0)
|
|
{
|
|
// TODO: Replace with single-scattering voxel grid implementation.
|
|
RenderTransmittanceWithVoxelGrid(
|
|
GraphBuilder,
|
|
SceneTextures,
|
|
Scene,
|
|
View,
|
|
OrthoGridUniformBuffer,
|
|
FrustumGridUniformBuffer,
|
|
HeterogeneousVolumeRadiance
|
|
);
|
|
}
|
|
else
|
|
{
|
|
// Collect volume interfaces
|
|
struct VolumeMesh
|
|
{
|
|
const IHeterogeneousVolumeInterface* Volume;
|
|
const FMaterialRenderProxy* MaterialRenderProxy;
|
|
|
|
VolumeMesh(const IHeterogeneousVolumeInterface* V, const FMaterialRenderProxy* M) :
|
|
Volume(V),
|
|
MaterialRenderProxy(M)
|
|
{}
|
|
};
|
|
|
|
TArray<VolumeMesh> VolumeMeshes;
|
|
|
|
const TArray<FVolumetricMeshBatch, SceneRenderingAllocator>& MeshBatches = GetHeterogeneousVolumesMeshBatches(Views, ViewIndex);
|
|
for (int32 MeshBatchIndex = 0; MeshBatchIndex < MeshBatches.Num(); ++MeshBatchIndex)
|
|
{
|
|
const FMeshBatch* Mesh = MeshBatches[MeshBatchIndex].Mesh;
|
|
const FPrimitiveSceneProxy* PrimitiveSceneProxy = MeshBatches[MeshBatchIndex].Proxy;
|
|
if (!ShouldRenderMeshBatchWithHeterogeneousVolumes(Mesh, PrimitiveSceneProxy, View.GetFeatureLevel()))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const FMaterialRenderProxy* MaterialRenderProxy = Mesh->MaterialRenderProxy;
|
|
for (int32 VolumeIndex = 0; VolumeIndex < Mesh->Elements.Num(); ++VolumeIndex)
|
|
{
|
|
const IHeterogeneousVolumeInterface* HeterogeneousVolume = (IHeterogeneousVolumeInterface*)Mesh->Elements[VolumeIndex].UserData;
|
|
//check(HeterogeneousVolume != nullptr);
|
|
if (HeterogeneousVolume == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
VolumeMeshes.Add(VolumeMesh(HeterogeneousVolume, MaterialRenderProxy));
|
|
}
|
|
}
|
|
|
|
// Provide coarse depth-sorting, based on camera-distance to world centroid
|
|
bool bDepthSort = CVarHeterogeneousVolumesDepthSort.GetValueOnRenderThread() == 1;
|
|
if (bDepthSort)
|
|
{
|
|
struct FDepthCompareHeterogeneousVolumes
|
|
{
|
|
FVector WorldCameraOrigin;
|
|
FDepthCompareHeterogeneousVolumes(FViewInfo& View) : WorldCameraOrigin(View.ViewMatrices.GetViewOrigin()) {}
|
|
|
|
FORCEINLINE bool operator()(const VolumeMesh& A, const VolumeMesh& B) const
|
|
{
|
|
FVector CameraToA = A.Volume->GetBounds().Origin - WorldCameraOrigin;
|
|
float SquaredDistanceToA = FVector::DotProduct(CameraToA, CameraToA);
|
|
|
|
FVector CameraToB = B.Volume->GetBounds().Origin - WorldCameraOrigin;
|
|
float SquaredDistanceToB = FVector::DotProduct(CameraToB, CameraToB);
|
|
|
|
return SquaredDistanceToA < SquaredDistanceToB;
|
|
}
|
|
};
|
|
|
|
VolumeMeshes.Sort(FDepthCompareHeterogeneousVolumes(View));
|
|
}
|
|
|
|
for (int32 VolumeIndex = 0; VolumeIndex < VolumeMeshes.Num(); ++VolumeIndex)
|
|
{
|
|
const IHeterogeneousVolumeInterface* HeterogeneousVolume = VolumeMeshes[VolumeIndex].Volume;
|
|
const FMaterialRenderProxy* MaterialRenderProxy = VolumeMeshes[VolumeIndex].MaterialRenderProxy;
|
|
const FPrimitiveSceneProxy* PrimitiveSceneProxy = HeterogeneousVolume->GetPrimitiveSceneProxy();
|
|
const FPrimitiveSceneInfo* PrimitiveSceneInfo = PrimitiveSceneProxy->GetPrimitiveSceneInfo();
|
|
const FPersistentPrimitiveIndex PrimitiveId = PrimitiveSceneInfo->GetPersistentIndex();
|
|
const FBoxSphereBounds LocalBoxSphereBounds = HeterogeneousVolume->GetLocalBounds();
|
|
|
|
RDG_EVENT_SCOPE(GraphBuilder, "%s [%d]", *HeterogeneousVolume->GetReadableName(), VolumeIndex);
|
|
|
|
// Allocate transmittance volume
|
|
FRDGTextureRef LightingCacheTexture = GSystemTextures.GetBlackDummy(GraphBuilder);
|
|
if (HeterogeneousVolumes::GetLightingCacheMode() != 0)
|
|
{
|
|
// TODO: Allow option for scalar transmittance to conserve bandwidth
|
|
HeterogeneousVolumes::FLODValue LODValue = HeterogeneousVolumes::CalcLOD(View, HeterogeneousVolume);
|
|
FIntVector LightingCacheResolution = HeterogeneousVolumes::GetLightingCacheResolution(HeterogeneousVolume, LODValue);
|
|
uint32 NumMips = FMath::Log2(float(FMath::Min(FMath::Min(LightingCacheResolution.X, LightingCacheResolution.Y), LightingCacheResolution.Z))) + 1;
|
|
FRDGTextureDesc LightingCacheDesc = FRDGTextureDesc::Create3D(
|
|
LightingCacheResolution,
|
|
!IsMetalPlatform(GShaderPlatformForFeatureLevel[View.FeatureLevel]) ? PF_FloatR11G11B10 : PF_FloatRGBA,
|
|
FClearValueBinding::Black,
|
|
TexCreate_ShaderResource | TexCreate_UAV | TexCreate_3DTiling,
|
|
NumMips
|
|
);
|
|
LightingCacheTexture = GraphBuilder.CreateTexture(LightingCacheDesc, TEXT("HeterogeneousVolumes.LightingCacheTexture"));
|
|
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(LightingCacheTexture), FLinearColor::Black);
|
|
}
|
|
|
|
// Material baking executes a pre-shading pipeline
|
|
if (CVarHeterogeneousVolumesPreshading.GetValueOnRenderThread())
|
|
{
|
|
RenderWithPreshading(
|
|
GraphBuilder,
|
|
SceneTextures,
|
|
Scene,
|
|
View, ViewIndex,
|
|
// Shadow Data
|
|
VisibleLightInfos,
|
|
VirtualShadowMapArray,
|
|
// Object data
|
|
HeterogeneousVolume,
|
|
MaterialRenderProxy,
|
|
PrimitiveId,
|
|
LocalBoxSphereBounds,
|
|
// Transmittance accleration
|
|
LightingCacheTexture,
|
|
// Output
|
|
HeterogeneousVolumeRadiance
|
|
);
|
|
}
|
|
// Otherwise execute a live-shading pipeline
|
|
else
|
|
{
|
|
RenderWithLiveShading(
|
|
GraphBuilder,
|
|
SceneTextures,
|
|
Scene,
|
|
View, ViewIndex,
|
|
// Shadow Data
|
|
VisibleLightInfos,
|
|
VirtualShadowMapArray,
|
|
// Object Data
|
|
HeterogeneousVolume,
|
|
MaterialRenderProxy,
|
|
PrimitiveId,
|
|
LocalBoxSphereBounds,
|
|
// Transmittance accleration
|
|
LightingCacheTexture,
|
|
// Output
|
|
HeterogeneousVolumeRadiance,
|
|
HeterogeneousVolumeVelocity,
|
|
HeterogeneousVolumeHoldout,
|
|
HeterogeneousVolumeBeerShadowMap
|
|
);
|
|
|
|
// Validate that a view state exists to store the AVSM
|
|
if (View.ViewState)
|
|
{
|
|
// Stash the temporary beer-law shadow map as a camera shadow if we aren't already creating one
|
|
if (HeterogeneousVolumes::GetTranslucencyCompositingMode() == HeterogeneousVolumes::ETranslucencyCompositingMode::BeerShadowMap)
|
|
{
|
|
// Resolution
|
|
FIntPoint ShadowMapResolution = HeterogeneousVolumes::GetDownsampledResolution(View.ViewRect.Size(), HeterogeneousVolumes::GetDownsampleFactor());
|
|
FRDGBufferRef VolumetricShadowIndirectionBuffer = GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FAVSMIndirectionPackedData));
|
|
FRDGBufferRef VolumetricShadowSampleBuffer = GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FAVSMSamplePackedData));
|
|
|
|
ConvertBeerLawShadowMapToVolumetricShadowMap(
|
|
GraphBuilder,
|
|
View,
|
|
//GroupCount,
|
|
ShadowMapResolution,
|
|
HeterogeneousVolumeBeerShadowMap,
|
|
VolumetricShadowIndirectionBuffer,
|
|
VolumetricShadowSampleBuffer
|
|
);
|
|
|
|
// Transform
|
|
const FMatrix ProjectionMatrix = View.ViewMatrices.GetProjectionMatrix();
|
|
float FOV = FMath::DegreesToRadians(View.FOV * 0.5);
|
|
FMatrix ViewToClip = FPerspectiveMatrix(
|
|
FOV,
|
|
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));
|
|
|
|
int32 NumShadowMatrices = 1;
|
|
FMatrix44f TranslatedWorldToShadow[] = {
|
|
FMatrix44f(View.ViewMatrices.GetTranslatedViewMatrix() * ViewToClip * ScreenMatrix)
|
|
};
|
|
FMatrix44f ShadowToTranslatedWorld[] = {
|
|
TranslatedWorldToShadow[0].Inverse()
|
|
};
|
|
FVector3f TranslatedWorldOrigin = ShadowToTranslatedWorld[0].GetOrigin();
|
|
|
|
// Generic data
|
|
int32 MaxSampleCount = 2;
|
|
FVector4f TranslatedWorldPlane = FVector4f::Zero();
|
|
float DownsampleFactor = HeterogeneousVolumes::GetDownsampleFactor();
|
|
bool bIsDirectionalLight = false;
|
|
FRDGBufferRef VolumetricShadowLinkedListBuffer = GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FAVSMLinkedListPackedData));
|
|
|
|
// Uniform buffer
|
|
CreateAdaptiveVolumetricShadowMapUniformBufferParameters(
|
|
GraphBuilder,
|
|
TranslatedWorldOrigin,
|
|
TranslatedWorldPlane,
|
|
TranslatedWorldToShadow,
|
|
ShadowMapResolution,
|
|
DownsampleFactor,
|
|
NumShadowMatrices,
|
|
MaxSampleCount,
|
|
bIsDirectionalLight,
|
|
VolumetricShadowLinkedListBuffer,
|
|
VolumetricShadowIndirectionBuffer,
|
|
VolumetricShadowSampleBuffer,
|
|
View.ViewState->AdaptiveVolumetricCameraMapUniformBufferParameters
|
|
);
|
|
}
|
|
|
|
// Append beauty image and build uniform buffer
|
|
if (ShouldCompositeHeterogeneousVolumesWithTranslucency() && View.ViewState->AdaptiveVolumetricCameraMapUniformBufferParameters)
|
|
{
|
|
// Append beauty image
|
|
View.ViewState->AdaptiveVolumetricCameraMapUniformBufferParameters->RadianceTexture = GraphBuilder.CreateSRV(HeterogeneousVolumeRadiance);
|
|
View.ViewState->AdaptiveVolumetricCameraMapUniformBufferParameters->TextureSampler = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
|
|
View.ViewState->AdaptiveVolumetricCameraMapUniformBuffer =
|
|
GraphBuilder.CreateUniformBuffer(View.ViewState->AdaptiveVolumetricCameraMapUniformBufferParameters);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
View.HeterogeneousVolumeRadiance = HeterogeneousVolumeRadiance;
|
|
View.HeterogeneousVolumeHoldout = HeterogeneousVolumeHoldout;
|
|
View.HeterogeneousVolumeBeerShadowMap = HeterogeneousVolumeBeerShadowMap;
|
|
}
|
|
}
|
|
}
|
|
|
|
class FHeterogeneousVolumesUpsampleCS : public FGlobalShader
|
|
{
|
|
DECLARE_GLOBAL_SHADER(FHeterogeneousVolumesUpsampleCS);
|
|
SHADER_USE_PARAMETER_STRUCT(FHeterogeneousVolumesUpsampleCS, FGlobalShader);
|
|
|
|
class FUpsampleMode : SHADER_PERMUTATION_INT("UPSAMPLE_MODE", 3);
|
|
using FPermutationDomain = TShaderPermutationDomain<FUpsampleMode>;
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureParameters, SceneTextures)
|
|
// Texture data
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture2D<float4>, HeterogeneousVolumeRadiance)
|
|
SHADER_PARAMETER_SAMPLER(SamplerState, TextureSampler)
|
|
SHADER_PARAMETER(FIntPoint, UpsampledResolution)
|
|
SHADER_PARAMETER(FIntPoint, UpsampledViewRect)
|
|
SHADER_PARAMETER(FIntPoint, UpsampledViewRectMin)
|
|
SHADER_PARAMETER(int, DownsampleFactor)
|
|
|
|
// Output
|
|
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, RWUpsampledTexture)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
public:
|
|
static bool ShouldCompilePermutation(
|
|
const FGlobalShaderPermutationParameters& Parameters
|
|
)
|
|
{
|
|
FPermutationDomain PermutationVector(Parameters.PermutationId);
|
|
if (PermutationVector.template Get<FUpsampleMode>() == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Apply conditional project settings for Heterogeneous volumes?
|
|
return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform);
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(
|
|
const FGlobalShaderPermutationParameters& Parameters,
|
|
FShaderCompilerEnvironment& OutEnvironment
|
|
)
|
|
{
|
|
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_1D"), GetThreadGroupSize1D());
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_2D"), GetThreadGroupSize2D());
|
|
|
|
// 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);
|
|
}
|
|
|
|
static int32 GetThreadGroupSize1D() { return GetThreadGroupSize2D() * GetThreadGroupSize2D(); }
|
|
static int32 GetThreadGroupSize2D() { return 8; }
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FHeterogeneousVolumesUpsampleCS, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesComposite.usf", "HeterogeneousVolumesUpsampleCS", SF_Compute);
|
|
|
|
class FHeterogeneousVolumesFilterCS : public FGlobalShader
|
|
{
|
|
DECLARE_GLOBAL_SHADER(FHeterogeneousVolumesFilterCS);
|
|
SHADER_USE_PARAMETER_STRUCT(FHeterogeneousVolumesFilterCS, FGlobalShader);
|
|
|
|
class FFilterMode : SHADER_PERMUTATION_INT("FILTER_MODE", 4);
|
|
using FPermutationDomain = TShaderPermutationDomain<FFilterMode>;
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
// Scene data
|
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureParameters, SceneTextures)
|
|
|
|
// Volume data
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture2D<float4>, HeterogeneousVolumeRadiance)
|
|
SHADER_PARAMETER_SAMPLER(SamplerState, TextureSampler)
|
|
SHADER_PARAMETER(FIntPoint, UpsampledResolution)
|
|
SHADER_PARAMETER(FIntPoint, UpsampledViewRect)
|
|
SHADER_PARAMETER(FIntPoint, UpsampledViewRectMin)
|
|
SHADER_PARAMETER(int, DownsampleFactor)
|
|
SHADER_PARAMETER(int, FilterWidth)
|
|
|
|
// Output
|
|
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, RWFilteredTexture)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
public:
|
|
static bool ShouldCompilePermutation(
|
|
const FGlobalShaderPermutationParameters& Parameters
|
|
)
|
|
{
|
|
FPermutationDomain PermutationVector(Parameters.PermutationId);
|
|
if (PermutationVector.template Get<FFilterMode>() == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Apply conditional project settings for Heterogeneous volumes?
|
|
return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform);
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(
|
|
const FGlobalShaderPermutationParameters& Parameters,
|
|
FShaderCompilerEnvironment& OutEnvironment
|
|
)
|
|
{
|
|
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_1D"), GetThreadGroupSize1D());
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_2D"), GetThreadGroupSize2D());
|
|
|
|
// 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);
|
|
}
|
|
|
|
static int32 GetThreadGroupSize1D() { return GetThreadGroupSize2D() * GetThreadGroupSize2D(); }
|
|
static int32 GetThreadGroupSize2D() { return 8; }
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FHeterogeneousVolumesFilterCS, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesComposite.usf", "HeterogeneousVolumesFilterCS", SF_Compute);
|
|
|
|
|
|
class FHeterogeneousVolumesCompositeCS : public FGlobalShader
|
|
{
|
|
DECLARE_GLOBAL_SHADER(FHeterogeneousVolumesCompositeCS);
|
|
SHADER_USE_PARAMETER_STRUCT(FHeterogeneousVolumesCompositeCS, FGlobalShader);
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
// Scene data
|
|
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureParameters, SceneTextures)
|
|
|
|
// Volume data
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture2D<float4>, HeterogeneousVolumeRadiance)
|
|
SHADER_PARAMETER_RDG_TEXTURE(Texture2D<float>, HeterogeneousVolumeHoldout)
|
|
|
|
// Transmittance structure
|
|
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FAdaptiveVolumetricShadowMapUniformBufferParameters, AVSM)
|
|
SHADER_PARAMETER(int32, bUseAVSM)
|
|
|
|
// Dispatch data
|
|
SHADER_PARAMETER(FIntVector, GroupCount)
|
|
SHADER_PARAMETER(int, DownsampleFactor)
|
|
|
|
// Output
|
|
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, RWColorTexture)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
public:
|
|
static bool ShouldCompilePermutation(
|
|
const FGlobalShaderPermutationParameters& Parameters
|
|
)
|
|
{
|
|
// Apply conditional project settings for Heterogeneous volumes?
|
|
return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform);
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(
|
|
const FGlobalShaderPermutationParameters& Parameters,
|
|
FShaderCompilerEnvironment& OutEnvironment
|
|
)
|
|
{
|
|
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_1D"), GetThreadGroupSize1D());
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_2D"), GetThreadGroupSize2D());
|
|
|
|
// 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);
|
|
}
|
|
|
|
static int32 GetThreadGroupSize1D() { return GetThreadGroupSize2D() * GetThreadGroupSize2D(); }
|
|
static int32 GetThreadGroupSize2D() { return 8; }
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FHeterogeneousVolumesCompositeCS, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesComposite.usf", "HeterogeneousVolumesCompositeCS", SF_Compute);
|
|
|
|
|
|
void FDeferredShadingSceneRenderer::CompositeHeterogeneousVolumes(
|
|
FRDGBuilder& GraphBuilder,
|
|
const FSceneTextures& SceneTextures
|
|
)
|
|
{
|
|
RDG_EVENT_SCOPE_STAT(GraphBuilder, HeterogeneousVolumesStat, "HeterogeneousVolumes");
|
|
RDG_GPU_STAT_SCOPE(GraphBuilder, HeterogeneousVolumesStat);
|
|
SCOPED_NAMED_EVENT(HeterogeneousVolumes, FColor::Emerald);
|
|
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
|
{
|
|
FViewInfo& View = Views[ViewIndex];
|
|
|
|
if (ShouldRenderHeterogeneousVolumesForView(Views, ViewIndex))
|
|
{
|
|
FRDGTextureDesc UpsampleDesc = View.HeterogeneousVolumeRadiance->Desc;
|
|
int32 DownsampleFactor = HeterogeneousVolumes::GetDownsampleFactor();
|
|
|
|
bool bIterativeUpsampling = HeterogeneousVolumes::GetUpsampleMode() != 0;
|
|
while (bIterativeUpsampling && (DownsampleFactor > 1))
|
|
{
|
|
int32 DownsampleIterationFactor = (DownsampleFactor % 3 == 0) ? 3 : 2;
|
|
|
|
FIntPoint DownsampledViewRect = FIntPoint::DivideAndRoundUp(View.ViewRect.Size(), DownsampleFactor);
|
|
FIntPoint DownsampledViewRectMin = FIntPoint::DivideAndRoundUp(View.ViewRect.Min, DownsampleFactor);
|
|
FIntPoint UpsampledViewRect = DownsampledViewRect * DownsampleIterationFactor;
|
|
FIntPoint UpsampledViewRectMin = DownsampledViewRectMin * DownsampleIterationFactor;
|
|
FIntVector GroupCount = FComputeShaderUtils::GetGroupCount(UpsampledViewRect, FHeterogeneousVolumesUpsampleCS::GetThreadGroupSize2D());
|
|
UpsampleDesc.Extent *= DownsampleIterationFactor;
|
|
|
|
// Upsample
|
|
{
|
|
FRDGTextureRef HeterogeneousVolumeUpsample = GraphBuilder.CreateTexture(UpsampleDesc, TEXT("HeterogeneousVolumeUpsample"));
|
|
|
|
FHeterogeneousVolumesUpsampleCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FHeterogeneousVolumesUpsampleCS::FParameters>();
|
|
{
|
|
// Scene data
|
|
PassParameters->View = View.ViewUniformBuffer;
|
|
PassParameters->SceneTextures = GetSceneTextureParameters(GraphBuilder, SceneTextures);
|
|
|
|
// Texture data
|
|
PassParameters->HeterogeneousVolumeRadiance = View.HeterogeneousVolumeRadiance;
|
|
PassParameters->TextureSampler = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
PassParameters->UpsampledResolution = UpsampleDesc.Extent;
|
|
PassParameters->UpsampledViewRect = UpsampledViewRect;
|
|
PassParameters->UpsampledViewRectMin = UpsampledViewRectMin;
|
|
PassParameters->DownsampleFactor = DownsampleIterationFactor;
|
|
|
|
// Output
|
|
PassParameters->RWUpsampledTexture = GraphBuilder.CreateUAV(HeterogeneousVolumeUpsample);
|
|
}
|
|
|
|
FHeterogeneousVolumesUpsampleCS::FPermutationDomain PermutationVector;
|
|
PermutationVector.Set<FHeterogeneousVolumesUpsampleCS::FUpsampleMode>(HeterogeneousVolumes::GetUpsampleMode());
|
|
TShaderRef<FHeterogeneousVolumesUpsampleCS> ComputeShader = View.ShaderMap->GetShader<FHeterogeneousVolumesUpsampleCS>(PermutationVector);
|
|
FComputeShaderUtils::AddPass(
|
|
GraphBuilder,
|
|
RDG_EVENT_NAME("FHeterogeneousVolumesUpsampleCS"),
|
|
ComputeShader,
|
|
PassParameters,
|
|
GroupCount);
|
|
|
|
View.HeterogeneousVolumeRadiance = HeterogeneousVolumeUpsample;
|
|
}
|
|
|
|
// Filter neighborhood
|
|
if (HeterogeneousVolumes::GetFilterMode() != 0)
|
|
{
|
|
FRDGTextureRef HeterogeneousVolumeFilter = GraphBuilder.CreateTexture(UpsampleDesc, TEXT("HeterogeneousVolumeFilter"));
|
|
|
|
FHeterogeneousVolumesFilterCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FHeterogeneousVolumesFilterCS::FParameters>();
|
|
{
|
|
// Scene data
|
|
PassParameters->View = View.ViewUniformBuffer;
|
|
PassParameters->SceneTextures = GetSceneTextureParameters(GraphBuilder, SceneTextures);
|
|
// Volume data
|
|
PassParameters->HeterogeneousVolumeRadiance = View.HeterogeneousVolumeRadiance;
|
|
PassParameters->TextureSampler = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
|
PassParameters->UpsampledResolution = UpsampleDesc.Extent;
|
|
PassParameters->UpsampledViewRect = UpsampledViewRect;
|
|
PassParameters->UpsampledViewRectMin = UpsampledViewRectMin;
|
|
PassParameters->DownsampleFactor = 1;
|
|
PassParameters->FilterWidth = HeterogeneousVolumes::GetFilterWidth();
|
|
|
|
// Output
|
|
PassParameters->RWFilteredTexture = GraphBuilder.CreateUAV(HeterogeneousVolumeFilter);
|
|
}
|
|
|
|
FHeterogeneousVolumesFilterCS::FPermutationDomain PermutationVector;
|
|
PermutationVector.Set<FHeterogeneousVolumesFilterCS::FFilterMode>(HeterogeneousVolumes::GetFilterMode());
|
|
TShaderRef<FHeterogeneousVolumesFilterCS> ComputeShader = View.ShaderMap->GetShader<FHeterogeneousVolumesFilterCS>(PermutationVector);
|
|
FComputeShaderUtils::AddPass(
|
|
GraphBuilder,
|
|
RDG_EVENT_NAME("FHeterogeneousVolumesFilterCS"),
|
|
ComputeShader,
|
|
PassParameters,
|
|
GroupCount);
|
|
|
|
View.HeterogeneousVolumeRadiance = HeterogeneousVolumeFilter;
|
|
}
|
|
|
|
DownsampleFactor /= DownsampleIterationFactor;
|
|
}
|
|
|
|
// Composite
|
|
{
|
|
FIntVector GroupCount = FComputeShaderUtils::GetGroupCount(View.ViewRect.Size(), FHeterogeneousVolumesUpsampleCS::GetThreadGroupSize2D());
|
|
FHeterogeneousVolumesCompositeCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FHeterogeneousVolumesCompositeCS::FParameters>();
|
|
{
|
|
// Scene data
|
|
PassParameters->View = View.ViewUniformBuffer;
|
|
PassParameters->SceneTextures = GetSceneTextureParameters(GraphBuilder, SceneTextures);
|
|
// Volume data
|
|
PassParameters->HeterogeneousVolumeRadiance = View.HeterogeneousVolumeRadiance;
|
|
PassParameters->HeterogeneousVolumeHoldout = View.HeterogeneousVolumeHoldout;
|
|
// Transmittance structure
|
|
PassParameters->bUseAVSM = ShouldCompositeHeterogeneousVolumesWithTranslucency();
|
|
PassParameters->AVSM = HeterogeneousVolumes::GetAdaptiveVolumetricCameraMapUniformBuffer(GraphBuilder, View.ViewState);
|
|
// Dispatch data
|
|
PassParameters->GroupCount = GroupCount;
|
|
//PassParameters->DownsampleFactor = HeterogeneousVolumes::GetDownsampleFactor();
|
|
PassParameters->DownsampleFactor = DownsampleFactor;
|
|
|
|
// Output
|
|
PassParameters->RWColorTexture = GraphBuilder.CreateUAV(SceneTextures.Color.Target);
|
|
}
|
|
|
|
TShaderRef<FHeterogeneousVolumesCompositeCS> ComputeShader = View.ShaderMap->GetShader<FHeterogeneousVolumesCompositeCS>();
|
|
FComputeShaderUtils::AddPass(
|
|
GraphBuilder,
|
|
RDG_EVENT_NAME("FHeterogeneousVolumesCompositeCS"),
|
|
ComputeShader,
|
|
PassParameters,
|
|
GroupCount);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace HeterogeneousVolumes
|
|
{
|
|
void PostRender(FScene& Scene, TArray<FViewInfo>& Views)
|
|
{
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
|
{
|
|
FSceneViewState* ViewState = Views[ViewIndex].ViewState;
|
|
if (ViewState)
|
|
{
|
|
ViewState->AdaptiveVolumetricCameraMapUniformBufferParameters = nullptr;
|
|
DestroyAdaptiveVolumetricShadowMapUniformBuffer(ViewState->AdaptiveVolumetricCameraMapUniformBuffer);
|
|
|
|
for (auto Itr = ViewState->AdaptiveVolumetricShadowMapUniformBufferMap.begin(); Itr != ViewState->AdaptiveVolumetricShadowMapUniformBufferMap.end(); ++Itr)
|
|
{
|
|
DestroyAdaptiveVolumetricShadowMapUniformBuffer(Itr->Value);
|
|
}
|
|
ViewState->AdaptiveVolumetricShadowMapUniformBufferMap.Empty();
|
|
}
|
|
}
|
|
}
|
|
}
|