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

924 lines
39 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "HeterogeneousVolumes.h"
#include "HeterogeneousVolumeInterface.h"
#include "LightRendering.h"
#include "PixelShaderUtils.h"
#include "RayTracingDefinitions.h"
#include "RayTracingInstance.h"
#include "RayTracingInstanceBufferUtil.h"
#include "RayTracingPayloadType.h"
#include "RendererPrivate.h"
#include "ScenePrivate.h"
#include "PrimitiveDrawingUtils.h"
#include "VolumeLighting.h"
#include "VolumetricFog.h"
#if RHI_RAYTRACING
FIntVector GetVoxelCoord(uint32 VoxelIndex, FIntVector VolumeResolution)
{
FIntVector VoxelCoord;
uint32 SliceSize = VolumeResolution.X * VolumeResolution.Y;
uint32 SliceIndex = VoxelIndex / SliceSize;
uint32 SliceCoord = VoxelIndex - SliceIndex * SliceSize;
VoxelCoord.X = SliceCoord % VolumeResolution.X;
VoxelCoord.Y = SliceCoord / VolumeResolution.X;
VoxelCoord.Z = SliceIndex;
return VoxelCoord;
}
FBox GetVoxelBounds(uint32 VoxelIndex, FIntVector VolumeResolution, FVector LocalBoundsOrigin, FVector LocalBoundsExtent)
{
FBox VoxelBounds;
FIntVector VoxelCoord = GetVoxelCoord(VoxelIndex, VolumeResolution);
FVector VoxelSize = (LocalBoundsExtent * 2.0) / FVector(VolumeResolution);
VoxelBounds.Min = LocalBoundsOrigin - LocalBoundsExtent + FVector(VoxelCoord) * VoxelSize;
VoxelBounds.Max = VoxelBounds.Min + VoxelSize;
return VoxelBounds;
}
class FCreateSparseVoxelBLAS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FCreateSparseVoxelBLAS);
SHADER_USE_PARAMETER_STRUCT(FCreateSparseVoxelBLAS, FGlobalShader);
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform);
}
static void ModifyCompilationEnvironment(
const FGlobalShaderPermutationParameters& Parameters,
FShaderCompilerEnvironment& OutEnvironment
)
{
OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads);
}
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
// Input
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSparseVoxelUniformBufferParameters, SparseVoxelUniformBuffer)
// Output
// Using RWStructuredBuffer<float> instead of RWStructuredBuffer<float3> to overcome Vulkan alignment error:
// error: cannot instantiate RWStructuredBuffer with given packed alignment; 'VK_EXT_scalar_block_layout' not supported
// SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<FVector>, RWPositionBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<float>, RWPositionBuffer)
// Indirect args
RDG_BUFFER_ACCESS(IndirectArgs, ERHIAccess::IndirectArgs)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FCreateSparseVoxelBLAS, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesSparseVoxelPipeline.usf", "CreateSparseVoxelBLAS", SF_Compute);
void CreateSparseVoxelBLAS(
FRDGBuilder& GraphBuilder,
// Scene data
const FViewInfo& View,
// Sparse voxel data
TRDGUniformBufferRef<FSparseVoxelUniformBufferParameters> SparseVoxelUniformBuffer,
FRDGBufferRef NumVoxelsBuffer,
// Output
FRDGBufferRef PositionBuffer
)
{
FCreateSparseVoxelBLAS::FParameters* PassParameters = GraphBuilder.AllocParameters<FCreateSparseVoxelBLAS::FParameters>();
{
PassParameters->SparseVoxelUniformBuffer = SparseVoxelUniformBuffer;
PassParameters->RWPositionBuffer = GraphBuilder.CreateUAV(PositionBuffer);
PassParameters->IndirectArgs = NumVoxelsBuffer;
}
TShaderRef<FCreateSparseVoxelBLAS> ComputeShader = View.ShaderMap->GetShader<FCreateSparseVoxelBLAS>();
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("CreateSparseVoxelBLAS"),
ERDGPassFlags::Compute,
ComputeShader,
PassParameters,
PassParameters->IndirectArgs,
0);
}
void GenerateRayTracingGeometryInstance(
FRDGBuilder& GraphBuilder,
// Scene data
const FScene* Scene,
const FViewInfo& View,
// Object data
const IHeterogeneousVolumeInterface* HeterogeneousVolumeInterface,
// Volume data
// Sparse voxel data
FRDGBufferRef NumVoxelsBuffer,
TRDGUniformBufferRef<FSparseVoxelUniformBufferParameters> SparseVoxelUniformBuffer,
// Output
TArray<FRayTracingGeometryRHIRef, SceneRenderingAllocator>& RayTracingGeometries,
TArray<FMatrix>& RayTracingTransforms
)
{
FIntVector MipVolumeResolution = HeterogeneousVolumes::GetMipVolumeResolution(SparseVoxelUniformBuffer->GetParameters()->VolumeResolution, SparseVoxelUniformBuffer->GetParameters()->MipLevel);
uint32 MipVoxelCount = HeterogeneousVolumes::GetVoxelCount(MipVolumeResolution);
TRefCountPtr<FRDGPooledBuffer> PooledVertexBuffer;
{
#if 0
TResourceArray<FVector3f> PositionData;
PositionData.SetNumUninitialized(MipVoxelCount * 2);
for (uint32 MipVoxelIndex = 0; MipVoxelIndex < MipVoxelCount; ++MipVoxelIndex)
{
FBox VoxelBounds = GetVoxelBounds(MipVoxelIndex, MipVolumeResolution, FVector(SparseVoxelUniformBuffer->GetParameters()->LocalBoundsOrigin), FVector(SparseVoxelUniformBuffer->GetParameters()->LocalBoundsExtent));
uint32 BoundsIndex = MipVoxelIndex * 2;
PositionData[BoundsIndex] = FVector3f(VoxelBounds.Min);
PositionData[BoundsIndex + 1] = FVector3f(VoxelBounds.Max);
}
FRHIResourceCreateInfo CreateInfo(TEXT("HeterogeneousVolumesVB"));
CreateInfo.ResourceArray = &PositionData;
#endif
FRDGBufferRef VertexBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateStructuredDesc(sizeof(FVector3f), MipVoxelCount * 2),
TEXT("CreateSparseVoxelBLAS.VertexBuffer"));
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(VertexBuffer), 0.0);
PooledVertexBuffer = GraphBuilder.ConvertToExternalBuffer(VertexBuffer);
}
// Morphs the dense-voxel topology into the sparse-voxel topology.
CreateSparseVoxelBLAS(GraphBuilder, View, SparseVoxelUniformBuffer, NumVoxelsBuffer, GraphBuilder.RegisterExternalBuffer(PooledVertexBuffer));
FRayTracingGeometryInitializer GeometryInitializer;
GeometryInitializer.DebugName = TEXT(" (HeterogeneousVolume)"); // TODO: Include resource name ie: *PrimitiveSceneProxy->GetResourceName().ToString();
//GeometryInitializer.IndexBuffer = EmptyIndexBuffer;
GeometryInitializer.GeometryType = RTGT_Procedural;
GeometryInitializer.bFastBuild = false;
FRayTracingGeometrySegment Segment;
Segment.NumPrimitives = MipVoxelCount;
Segment.MaxVertices = MipVoxelCount * 2;
Segment.VertexBufferStride = 2u * sizeof(FVector3f);
Segment.VertexBuffer = PooledVertexBuffer->GetRHI();
GeometryInitializer.Segments.Add(Segment);
GeometryInitializer.TotalPrimitiveCount = Segment.NumPrimitives;
RayTracingGeometries.Add(RHICreateRayTracingGeometry(GeometryInitializer));
RayTracingTransforms.Add(HeterogeneousVolumeInterface->GetLocalToWorld());
}
BEGIN_SHADER_PARAMETER_STRUCT(FBuildBLASPassParams, )
RDG_BUFFER_ACCESS(ScratchBuffer, ERHIAccess::UAVCompute)
END_SHADER_PARAMETER_STRUCT()
BEGIN_SHADER_PARAMETER_STRUCT(FBuildTLASPassParams, )
RDG_BUFFER_ACCESS(RayTracingSceneScratchBuffer, ERHIAccess::UAVCompute)
RDG_BUFFER_ACCESS(RayTracingSceneInstanceBuffer, ERHIAccess::SRVCompute)
RDG_BUFFER_ACCESS(RayTracingSceneBuffer, ERHIAccess::BVHWrite)
END_SHADER_PARAMETER_STRUCT()
void GenerateRayTracingScene(
FRDGBuilder& GraphBuilder,
// Scene data
const FScene* Scene,
const FViewInfo& View,
// Ray tracing data
TConstArrayView<FRayTracingGeometryRHIRef> RayTracingGeometries,
TConstArrayView<FMatrix> RayTracingTransforms,
// Output
FRayTracingScene& RayTracingScene
)
{
RayTracingScene.Reset(false);
TArray<FRayTracingGeometryBuildParams> BuildParams;
uint32 BLASScratchSize = 0;
// Collect instances
for (int32 GeometryIndex = 0; GeometryIndex < RayTracingGeometries.Num(); ++GeometryIndex)
{
FRHIRayTracingGeometry* RayTracingGeometry = RayTracingGeometries[GeometryIndex];
checkf(RayTracingGeometry, TEXT("RayTracingGeometryInstance not created."));
FRayTracingGeometryBuildParams Params;
Params.Geometry = RayTracingGeometry;
Params.BuildMode = EAccelerationStructureBuildMode::Build;
BuildParams.Add(Params);
const FRayTracingGeometryInitializer& Initializer = RayTracingGeometry->GetInitializer();
FRayTracingAccelerationStructureSize SizeInfo = RHICalcRayTracingGeometrySize(Initializer);
BLASScratchSize = Align(BLASScratchSize + SizeInfo.BuildScratchSize, GRHIRayTracingScratchBufferAlignment);
FRayTracingGeometryInstance RayTracingGeometryInstance = {};
RayTracingGeometryInstance.GeometryRHI = RayTracingGeometry;
RayTracingGeometryInstance.NumTransforms = 1;
RayTracingGeometryInstance.Transforms = MakeArrayView(&RayTracingTransforms[GeometryIndex], 1);
RayTracingScene.AddInstance(RayTracingGeometryInstance, ERayTracingSceneLayer::Base);
}
FRDGBufferDesc ScratchBufferDesc;
ScratchBufferDesc.Usage = EBufferUsageFlags::RayTracingScratch | EBufferUsageFlags::StructuredBuffer;
ScratchBufferDesc.BytesPerElement = GRHIRayTracingScratchBufferAlignment;
ScratchBufferDesc.NumElements = FMath::DivideAndRoundUp(BLASScratchSize, GRHIRayTracingScratchBufferAlignment);
FRDGBufferRef ScratchBuffer = GraphBuilder.CreateBuffer(ScratchBufferDesc, TEXT("HeterogeneousVolumes.BLASSharedScratchBuffer"));
// Build instance BLAS
FBuildBLASPassParams* PassParamsBLAS = GraphBuilder.AllocParameters<FBuildBLASPassParams>();
PassParamsBLAS->ScratchBuffer = ScratchBuffer;
GraphBuilder.AddPass(
RDG_EVENT_NAME("BuildRayTracingGeometries"),
PassParamsBLAS,
ERDGPassFlags::Compute | ERDGPassFlags::NeverCull | ERDGPassFlags::NeverParallel,
[
PassParamsBLAS,
BuildParams = MoveTemp(BuildParams)
](FRDGAsyncTask, FRHICommandList& RHICmdList)
{
FRHIBufferRange ScratchBufferRange;
ScratchBufferRange.Buffer = PassParamsBLAS->ScratchBuffer->GetRHI();
ScratchBufferRange.Offset = 0;
RHICmdList.BuildAccelerationStructures(BuildParams, ScratchBufferRange);
}
);
// Create RayTracingScene
const FGPUScene* EmptyGPUScene = nullptr;
RayTracingScene.Create(GraphBuilder, View, EmptyGPUScene, ERDGPassFlags::Compute);
RayTracingScene.Build(GraphBuilder, ERDGPassFlags::Compute | ERDGPassFlags::NeverCull | ERDGPassFlags::NeverParallel, nullptr);
GraphBuilder.AddDispatchHint();
}
IMPLEMENT_RT_PAYLOAD_TYPE(ERayTracingPayloadType::SparseVoxel, 28);
class FHeterogeneousVolumesSparseVoxelsHitGroup : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FHeterogeneousVolumesSparseVoxelsHitGroup)
SHADER_USE_ROOT_PARAMETER_STRUCT(FHeterogeneousVolumesSparseVoxelsHitGroup, FGlobalShader)
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSparseVoxelUniformBufferParameters, SparseVoxelUniformBuffer)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return ShouldCompileRayTracingShadersForProject(Parameters.Platform) && DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform) &&
FDataDrivenShaderPlatformInfo::GetSupportsRayTracingProceduralPrimitive(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
}
static ERayTracingPayloadType GetRayTracingPayloadType(const int32 PermutationId)
{
return ERayTracingPayloadType::SparseVoxel;
}
};
IMPLEMENT_GLOBAL_SHADER(FHeterogeneousVolumesSparseVoxelsHitGroup, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesHardwareRayTracing.usf", "closesthit=SparseVoxelsClosestHitShader anyhit=SparseVoxelsAnyHitShader intersection=SparseVoxelsIntersectionShader", SF_RayHitGroup);
class FHeterogeneousVolumesSparseVoxelMS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FHeterogeneousVolumesSparseVoxelMS)
SHADER_USE_ROOT_PARAMETER_STRUCT(FHeterogeneousVolumesSparseVoxelMS, FGlobalShader)
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return ShouldCompileRayTracingShadersForProject(Parameters.Platform) && DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform) &&
FDataDrivenShaderPlatformInfo::GetSupportsRayTracingProceduralPrimitive(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
}
static ERayTracingPayloadType GetRayTracingPayloadType(const int32 PermutationId)
{
return ERayTracingPayloadType::SparseVoxel;
}
using FParameters = FEmptyShaderParameters;
};
IMPLEMENT_GLOBAL_SHADER(FHeterogeneousVolumesSparseVoxelMS, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesHardwareRayTracing.usf", "SparseVoxelsMissShader", SF_RayMiss);
class FRenderLightingCacheWithPreshadingRGS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FRenderLightingCacheWithPreshadingRGS)
SHADER_USE_ROOT_PARAMETER_STRUCT(FRenderLightingCacheWithPreshadingRGS, FGlobalShader)
class FLightingCacheMode : SHADER_PERMUTATION_INT("DIM_LIGHTING_CACHE_MODE", 2);
class FIndirectLightingMode : SHADER_PERMUTATION_INT("INDIRECT_LIGHTING_MODE", 3);
using FPermutationDomain = TShaderPermutationDomain<FLightingCacheMode, FIndirectLightingMode>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
// Scene
SHADER_PARAMETER_RDG_BUFFER_SRV(RaytracingAccelerationStructure, TLAS)
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer)
SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureParameters, SceneTextures)
// Lighting data
SHADER_PARAMETER(int, bApplyEmissionAndTransmittance)
SHADER_PARAMETER(int, bApplyDirectLighting)
SHADER_PARAMETER(int, bApplyShadowTransmittance)
SHADER_PARAMETER(int, LightType)
SHADER_PARAMETER_STRUCT_REF(FDeferredLightUniformStruct, DeferredLight)
SHADER_PARAMETER(float, VolumetricScatteringIntensity)
// Shadow data
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FForwardLightUniformParameters, ForwardLightStruct)
SHADER_PARAMETER_STRUCT_INCLUDE(FVolumeShadowingShaderParameters, VolumeShadowingShaderParameters)
SHADER_PARAMETER_STRUCT_INCLUDE(FVirtualShadowMapSamplingParameters, VirtualShadowMapSamplingParameters)
SHADER_PARAMETER(int32, VirtualShadowMapId)
// Global illumination data
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenTranslucencyLightingUniforms, LumenGIVolumeStruct)
// TODO: Ambient occlusion pipeline
SHADER_PARAMETER_RDG_TEXTURE(Texture3D, AmbientOcclusionTexture)
SHADER_PARAMETER(FIntVector, AmbientOcclusionResolution)
SHADER_PARAMETER(float, IndirectInscatteringFactor)
// Sparse Volume
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSparseVoxelUniformBufferParameters, SparseVoxelUniformBuffer)
// Volume
SHADER_PARAMETER(int, MipLevel)
// Ray
SHADER_PARAMETER(float, MaxTraceDistance)
SHADER_PARAMETER(int, MaxStepCount)
SHADER_PARAMETER(int, bJitter)
// Transmittance volume data
SHADER_PARAMETER_STRUCT_INCLUDE(FLightingCacheParameters, LightingCache)
// Output
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, RWLightingCacheTexture)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
FPermutationDomain PermutationVector(Parameters.PermutationId);
if (PermutationVector.template Get<FIndirectLightingMode>() != static_cast<int32>(HeterogeneousVolumes::EIndirectLightingMode::LightingCachePass))
{
return false;
}
return ShouldCompileRayTracingShadersForProject(Parameters.Platform) && DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform) &&
FDataDrivenShaderPlatformInfo::GetSupportsRayTracingProceduralPrimitive(Parameters.Platform);
}
static void ModifyCompilationEnvironment(
const FGlobalShaderPermutationParameters& Parameters,
FShaderCompilerEnvironment& OutEnvironment
)
{
FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
bool bSupportVirtualShadowMap = IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
if (bSupportVirtualShadowMap)
{
OutEnvironment.SetDefine(TEXT("VIRTUAL_SHADOW_MAP"), 1);
FVirtualShadowMapArray::SetShaderDefines(OutEnvironment);
}
}
static ERayTracingPayloadType GetRayTracingPayloadType(const int32 PermutationId)
{
return ERayTracingPayloadType::SparseVoxel;
}
};
IMPLEMENT_GLOBAL_SHADER(FRenderLightingCacheWithPreshadingRGS, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesHardwareRayTracing.usf", "RenderLightingCacheWithPreshadingRGS", SF_RayGen);
class FRenderSingleScatteringWithPreshadingRGS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FRenderSingleScatteringWithPreshadingRGS)
SHADER_USE_ROOT_PARAMETER_STRUCT(FRenderSingleScatteringWithPreshadingRGS, FGlobalShader)
class FApplyShadowTransmittanceDim : SHADER_PERMUTATION_BOOL("DIM_APPLY_SHADOW_TRANSMITTANCE");
//class FUseTransmittanceVolume : SHADER_PERMUTATION_BOOL("DIM_USE_TRANSMITTANCE_VOLUME");
class FUseInscatteringVolume : SHADER_PERMUTATION_BOOL("DIM_USE_INSCATTERING_VOLUME");
class FUseLumenGI : SHADER_PERMUTATION_BOOL("DIM_USE_LUMEN_GI");
using FPermutationDomain = TShaderPermutationDomain<FApplyShadowTransmittanceDim, FUseInscatteringVolume, FUseLumenGI>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
// Scene
SHADER_PARAMETER_RDG_BUFFER_SRV(RaytracingAccelerationStructure, TLAS)
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer)
SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureParameters, SceneTextures)
// Lighting data
SHADER_PARAMETER(int, bApplyEmissionAndTransmittance)
SHADER_PARAMETER(int, bApplyDirectLighting)
SHADER_PARAMETER(int, LightType)
SHADER_PARAMETER_STRUCT_REF(FDeferredLightUniformStruct, DeferredLight)
SHADER_PARAMETER(float, VolumetricScatteringIntensity)
// Shadow data
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FForwardLightUniformParameters, ForwardLightStruct)
SHADER_PARAMETER_STRUCT_INCLUDE(FVolumeShadowingShaderParameters, VolumeShadowingShaderParameters)
SHADER_PARAMETER_STRUCT_INCLUDE(FVirtualShadowMapSamplingParameters, VirtualShadowMapSamplingParameters)
SHADER_PARAMETER(int32, VirtualShadowMapId)
// Atmosphere
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FFogUniformParameters, FogStruct)
// Indirect Lighting
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLumenTranslucencyLightingUniforms, LumenGIVolumeStruct)
// Sparse Volume
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSparseVoxelUniformBufferParameters, SparseVoxelUniformBuffer)
// Volume
SHADER_PARAMETER(int, MipLevel)
// Transmittance volume
SHADER_PARAMETER_STRUCT_INCLUDE(FLightingCacheParameters, LightingCache)
// Ray
SHADER_PARAMETER(float, MaxTraceDistance)
SHADER_PARAMETER(int, MaxStepCount)
SHADER_PARAMETER(int, bJitter)
// Output
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, RWLightingTexture)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return ShouldCompileRayTracingShadersForProject(Parameters.Platform) && DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform) &&
FDataDrivenShaderPlatformInfo::GetSupportsRayTracingProceduralPrimitive(Parameters.Platform);
}
static void ModifyCompilationEnvironment(
const FGlobalShaderPermutationParameters& Parameters,
FShaderCompilerEnvironment& OutEnvironment
)
{
FMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
bool bSupportVirtualShadowMap = IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
if (bSupportVirtualShadowMap)
{
OutEnvironment.SetDefine(TEXT("VIRTUAL_SHADOW_MAP"), 1);
FVirtualShadowMapArray::SetShaderDefines(OutEnvironment);
}
}
static ERayTracingPayloadType GetRayTracingPayloadType(const int32 PermutationId)
{
return ERayTracingPayloadType::SparseVoxel;
}
};
IMPLEMENT_GLOBAL_SHADER(FRenderSingleScatteringWithPreshadingRGS, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesHardwareRayTracing.usf", "RenderSingleScatteringWithPreshadingRGS", SF_RayGen);
FRayTracingLocalShaderBindings* BuildRayTracingMaterialBindings(
FRHICommandList& RHICmdList,
const FViewInfo& View,
FRHIUniformBuffer* UniformBuffer,
TConstArrayView<FRayTracingGeometryRHIRef> RayTracingGeometries
)
{
auto Alloc = [&](uint32 Size, uint32 Align)
{
return RHICmdList.Bypass()
? FMemStack::Get().Alloc(Size, Align)
: RHICmdList.Alloc(Size, Align);
};
// Allocate bindings
const uint32 NumBindings = 1;
FRayTracingLocalShaderBindings* Bindings = (FRayTracingLocalShaderBindings*)Alloc(sizeof(FRayTracingLocalShaderBindings) * NumBindings, alignof(FRayTracingLocalShaderBindings));
// Allocate and assign uniform buffers
const uint32 NumUniformBuffers = 1;
FRHIUniformBuffer** UniformBufferArray = (FRHIUniformBuffer**)Alloc(sizeof(FRHIUniformBuffer*) * NumUniformBuffers, alignof(FRHIUniformBuffer*));
UniformBufferArray[0] = UniformBuffer;
// Fill bindings
for (uint32 BindingIndex = 0; BindingIndex < NumBindings; ++BindingIndex)
{
// TODO: Declare useful user-data??
uint32 UserData = 0;
FRayTracingLocalShaderBindings Binding = {};
Binding.RecordIndex = 0;
Binding.Geometry = RayTracingGeometries[BindingIndex];
Binding.SegmentIndex = 0;
Binding.UserData = UserData;
Binding.UniformBuffers = UniformBufferArray;
Binding.NumUniformBuffers = NumUniformBuffers;
Bindings[BindingIndex] = Binding;
}
return Bindings;
}
FRayTracingPipelineState* BuildRayTracingPipelineState(
FRHICommandList& RHICmdList,
const FViewInfo& View,
FRHIRayTracingShader* RayGenerationShader,
uint32& OutMaxLocalBindingDataSize
)
{
FRayTracingPipelineStateInitializer Initializer;
Initializer.MaxPayloadSizeInBytes = GetRayTracingPayloadTypeMaxSize(ERayTracingPayloadType::SparseVoxel);
// Get the ray tracing materials
auto HitGroupShaders = View.ShaderMap->GetShader<FHeterogeneousVolumesSparseVoxelsHitGroup>();
FRHIRayTracingShader* HitShaderTable[] = {
HitGroupShaders.GetRayTracingShader()
};
Initializer.SetHitGroupTable(HitShaderTable);
auto MissShader = View.ShaderMap->GetShader<FHeterogeneousVolumesSparseVoxelMS>();
FRHIRayTracingShader* MissShaderTable[] = {
MissShader.GetRayTracingShader()
};
Initializer.SetMissShaderTable(MissShaderTable);
FRHIRayTracingShader* RayGenShaderTable[] = {
RayGenerationShader
};
Initializer.SetRayGenShaderTable(RayGenShaderTable);
OutMaxLocalBindingDataSize = Initializer.GetMaxLocalBindingDataSize();
FRayTracingPipelineState* RayTracingPipelineState = PipelineStateCache::GetAndOrCreateRayTracingPipelineState(RHICmdList, Initializer);
return RayTracingPipelineState;
}
void RenderLightingCacheWithPreshadingHardwareRayTracing(
FRDGBuilder& GraphBuilder,
// Scene data
const FScene* Scene,
const FViewInfo& View, int32 ViewIndex,
const FSceneTextures& SceneTextures,
// Light data
bool bApplyEmissionAndTransmittance,
bool bApplyDirectLighting,
bool bApplyShadowTransmittance,
uint32 LightType,
const FLightSceneInfo* LightSceneInfo,
// Shadow data
const FVisibleLightInfo* VisibleLightInfo,
const FVirtualShadowMapArray& VirtualShadowMapArray,
// Object data
const IHeterogeneousVolumeInterface* HeterogeneousVolumeInterface,
// Sparse voxel data
TRDGUniformBufferRef<FSparseVoxelUniformBufferParameters> SparseVoxelUniformBuffer,
// Ray tracing data
FRayTracingScene& RayTracingScene,
TConstArrayView<FRayTracingGeometryRHIRef> RayTracingGeometries,
// Output
FRDGTextureRef& LightingCacheTexture
)
{
// Note must be done in the same scope as we add the pass otherwise the UB lifetime will not be guaranteed
FDeferredLightUniformStruct DeferredLightUniform = GetDeferredLightParameters(View, *LightSceneInfo);
TUniformBufferRef<FDeferredLightUniformStruct> DeferredLightUB = CreateUniformBufferImmediate(DeferredLightUniform, UniformBuffer_SingleDraw);
FRenderLightingCacheWithPreshadingRGS::FParameters* PassParameters = GraphBuilder.AllocParameters<FRenderLightingCacheWithPreshadingRGS::FParameters>();
{
// Scene
PassParameters->TLAS = RayTracingScene.GetLayerView(ERayTracingSceneLayer::Base);
PassParameters->ViewUniformBuffer = View.ViewUniformBuffer;
PassParameters->SceneTextures = GetSceneTextureParameters(GraphBuilder, SceneTextures);
// Light data
check(LightSceneInfo != nullptr);
PassParameters->bApplyEmissionAndTransmittance = bApplyEmissionAndTransmittance;
PassParameters->bApplyDirectLighting = bApplyDirectLighting;
PassParameters->bApplyShadowTransmittance = bApplyShadowTransmittance;
PassParameters->DeferredLight = DeferredLightUB;
PassParameters->LightType = LightType;
PassParameters->VolumetricScatteringIntensity = LightSceneInfo->Proxy->GetVolumetricScatteringIntensity();
// Sparse Voxel data
PassParameters->SparseVoxelUniformBuffer = SparseVoxelUniformBuffer;
// Transmittance volume
HeterogeneousVolumes::FLODValue LODValue = HeterogeneousVolumes::CalcLOD(View, HeterogeneousVolumeInterface);
PassParameters->LightingCache.LightingCacheResolution = HeterogeneousVolumes::GetLightingCacheResolution(HeterogeneousVolumeInterface, LODValue);
PassParameters->LightingCache.LightingCacheVoxelBias = HeterogeneousVolumeInterface->GetShadowBiasFactor();
//PassParameters->LightingCache.LightingCacheTexture = GraphBuilder.CreateSRV(LightingCacheTexture);
// Ray data
PassParameters->MaxTraceDistance = HeterogeneousVolumes::GetMaxTraceDistance();
PassParameters->MaxStepCount = HeterogeneousVolumes::GetMaxStepCount();
PassParameters->bJitter = HeterogeneousVolumes::ShouldJitter();
PassParameters->MipLevel = HeterogeneousVolumes::GetMipLevel();
// Shadow data
PassParameters->ForwardLightStruct = View.ForwardLightingResources.ForwardLightUniformBuffer;
if (VisibleLightInfo != nullptr)
{
const FProjectedShadowInfo* ProjectedShadowInfo = GetShadowForInjectionIntoVolumetricFog(*VisibleLightInfo);
bool bDynamicallyShadowed = ProjectedShadowInfo != NULL;
if (bDynamicallyShadowed)
{
GetVolumeShadowingShaderParameters(GraphBuilder, View, LightSceneInfo, ProjectedShadowInfo, PassParameters->VolumeShadowingShaderParameters);
}
else
{
SetVolumeShadowingDefaultShaderParametersGlobal(GraphBuilder, PassParameters->VolumeShadowingShaderParameters);
}
PassParameters->VirtualShadowMapId = VisibleLightInfo->GetVirtualShadowMapId(&View);
}
else
{
SetVolumeShadowingDefaultShaderParametersGlobal(GraphBuilder, PassParameters->VolumeShadowingShaderParameters);
PassParameters->VirtualShadowMapId = -1;
}
PassParameters->VirtualShadowMapSamplingParameters = VirtualShadowMapArray.GetSamplingParameters(GraphBuilder, ViewIndex);
// Global illumination data
auto* LumenUniforms = GraphBuilder.AllocParameters<FLumenTranslucencyLightingUniforms>();
LumenUniforms->Parameters = GetLumenTranslucencyLightingParameters(GraphBuilder, View.GetLumenTranslucencyGIVolume(), View.LumenFrontLayerTranslucency);
PassParameters->LumenGIVolumeStruct = GraphBuilder.CreateUniformBuffer(LumenUniforms);
// TODO: Ambient occlusion pipeline
//PassParameters->AmbientOcclusionTexture = AmbientOcclusionTexture;
//PassParameters->AmbientOcclusionResolution = HeterogeneousVolumes::GetAmbientOcclusionResolution(HeterogeneousVolumeInterface, LODValue);
PassParameters->IndirectInscatteringFactor = HeterogeneousVolumes::GetIndirectLightingFactor();
// Output
PassParameters->RWLightingCacheTexture = GraphBuilder.CreateUAV(LightingCacheTexture);
}
FString PassName;
#if WANTS_DRAW_MESH_EVENTS
if (GetEmitDrawEvents())
{
FString LightName = TEXT("none");
if (LightSceneInfo != nullptr)
{
FSceneRenderer::GetLightNameForDrawEvent(LightSceneInfo->Proxy, LightName);
}
FString ModeName = HeterogeneousVolumes::UseLightingCacheForInscattering() ? TEXT("In-Scattering") : TEXT("Transmittance");
PassName = FString::Printf(TEXT("RenderLightingCacheWithPreshadingRGS [%s] (Light = %s)"), *ModeName, *LightName);
}
#endif // WANTS_DRAW_MESH_EVENTS
// Indirect lighting accumulation is coupled with directional light, because it doesn't voxel cull. It is assumed to exist and shadow.
int32 IndirectLightingMode = LightType == LightType_Directional ? static_cast<int32>(HeterogeneousVolumes::GetIndirectLightingMode()) : 0;
FRenderLightingCacheWithPreshadingRGS::FPermutationDomain PermutationVector;
PermutationVector.Set<FRenderLightingCacheWithPreshadingRGS::FLightingCacheMode>(HeterogeneousVolumes::GetLightingCacheMode() - 1);
PermutationVector.Set<FRenderLightingCacheWithPreshadingRGS::FIndirectLightingMode>(IndirectLightingMode);
TShaderRef<FRenderLightingCacheWithPreshadingRGS> RayGenerationShader = View.ShaderMap->GetShader<FRenderLightingCacheWithPreshadingRGS>(PermutationVector);
HeterogeneousVolumes::FLODValue LODValue = HeterogeneousVolumes::CalcLOD(View, HeterogeneousVolumeInterface);
FIntVector VolumeResolution = HeterogeneousVolumes::GetLightingCacheResolution(HeterogeneousVolumeInterface, LODValue);
FIntPoint DispatchResolution = FIntPoint(VolumeResolution.X, VolumeResolution.Y * VolumeResolution.Z);
GraphBuilder.AddPass(
RDG_EVENT_NAME("%s %ux%u", *PassName, DispatchResolution.X, DispatchResolution.Y),
PassParameters,
ERDGPassFlags::Compute | ERDGPassFlags::NeverCull,
[
PassParameters,
&View,
&RayTracingScene,
RayTracingGeometries,
RayGenerationShader,
DispatchResolution
](FRHICommandList& RHICmdList)
{
// Set ray-gen bindings
FRHIBatchedShaderParameters& GlobalResources = RHICmdList.GetScratchShaderParameters();
SetShaderParameters(GlobalResources, RayGenerationShader, *PassParameters);
// Create pipeline
uint32 MaxLocalBindingDataSize = 0;
FRayTracingPipelineState* RayTracingPipelineState = BuildRayTracingPipelineState(RHICmdList, View, RayGenerationShader.GetRayTracingShader(), MaxLocalBindingDataSize);
FRayTracingShaderBindingTableInitializer SBTInitializer;
// WARNING: Currently hit-group indexing is required to bind uniform buffers to hit-group shaders.
SBTInitializer.HitGroupIndexingMode = ERayTracingHitGroupIndexingMode::Allow;
SBTInitializer.ShaderBindingMode = ERayTracingShaderBindingMode::RTPSO;
SBTInitializer.NumGeometrySegments = 1;
SBTInitializer.NumShaderSlotsPerGeometrySegment = RAY_TRACING_NUM_SHADER_SLOTS;
SBTInitializer.NumMissShaderSlots = 1;
SBTInitializer.NumCallableShaderSlots = 0;
SBTInitializer.LocalBindingDataSize = MaxLocalBindingDataSize;
FShaderBindingTableRHIRef SBT = RHICmdList.CreateRayTracingShaderBindingTable(SBTInitializer);
// Set hit-group bindings
const uint32 NumBindings = 1;
FRayTracingLocalShaderBindings* Bindings = BuildRayTracingMaterialBindings(RHICmdList, View, PassParameters->SparseVoxelUniformBuffer->GetRHI(), RayTracingGeometries);
RHICmdList.SetRayTracingHitGroups(SBT, RayTracingPipelineState, NumBindings, Bindings);
RHICmdList.SetRayTracingMissShaders(SBT, RayTracingPipelineState, NumBindings, Bindings);
RHICmdList.CommitShaderBindingTable(SBT);
// Dispatch
RHICmdList.RayTraceDispatch(
RayTracingPipelineState,
RayGenerationShader.GetRayTracingShader(),
SBT,
GlobalResources,
DispatchResolution.X, DispatchResolution.Y);
}
);
}
void RenderSingleScatteringWithPreshadingHardwareRayTracing(
FRDGBuilder& GraphBuilder,
// Scene data
const FScene* Scene,
const FViewInfo& View, int32 ViewIndex,
const FSceneTextures& SceneTextures,
// Light data
bool bApplyEmissionAndTransmittance,
bool bApplyDirectLighting,
bool bApplyShadowTransmittance,
uint32 LightType,
const FLightSceneInfo* LightSceneInfo,
// Shadow data
const FVisibleLightInfo* VisibleLightInfo,
const FVirtualShadowMapArray& VirtualShadowMapArray,
// Object data
const IHeterogeneousVolumeInterface* HeterogeneousVolumeInterface,
// Sparse voxel data
TRDGUniformBufferRef<FSparseVoxelUniformBufferParameters> SparseVoxelUniformBuffer,
// Ray tracing data
FRayTracingScene& RayTracingScene,
TConstArrayView<FRayTracingGeometryRHIRef> RayTracingGeometries,
// Transmittance volume
FRDGTextureRef LightingCacheTexture,
// Output
FRDGTextureRef& HeterogeneousVolumeTexture
)
{
// Note must be done in the same scope as we add the pass otherwise the UB lifetime will not be guaranteed
FDeferredLightUniformStruct DeferredLightUniform;
if (bApplyDirectLighting && (LightSceneInfo != nullptr))
{
DeferredLightUniform = GetDeferredLightParameters(View, *LightSceneInfo);
}
TUniformBufferRef<FDeferredLightUniformStruct> DeferredLightUB = CreateUniformBufferImmediate(DeferredLightUniform, UniformBuffer_SingleDraw);
FRenderSingleScatteringWithPreshadingRGS::FParameters* PassParameters = GraphBuilder.AllocParameters<FRenderSingleScatteringWithPreshadingRGS::FParameters>();
{
// Scene
PassParameters->TLAS = RayTracingScene.GetLayerView(ERayTracingSceneLayer::Base);
PassParameters->ViewUniformBuffer = View.ViewUniformBuffer;
PassParameters->SceneTextures = GetSceneTextureParameters(GraphBuilder, SceneTextures);
// Light data
PassParameters->bApplyEmissionAndTransmittance = bApplyEmissionAndTransmittance;
PassParameters->bApplyDirectLighting = bApplyDirectLighting;
if (PassParameters->bApplyDirectLighting && (LightSceneInfo != nullptr))
{
PassParameters->VolumetricScatteringIntensity = LightSceneInfo->Proxy->GetVolumetricScatteringIntensity();
}
PassParameters->DeferredLight = DeferredLightUB;
PassParameters->LightType = LightType;
// Shadow data
PassParameters->ForwardLightStruct = View.ForwardLightingResources.ForwardLightUniformBuffer;
if (VisibleLightInfo != nullptr)
{
const FProjectedShadowInfo* ProjectedShadowInfo = GetShadowForInjectionIntoVolumetricFog(*VisibleLightInfo);
bool bDynamicallyShadowed = ProjectedShadowInfo != NULL;
if (bDynamicallyShadowed)
{
GetVolumeShadowingShaderParameters(GraphBuilder, View, LightSceneInfo, ProjectedShadowInfo, PassParameters->VolumeShadowingShaderParameters);
}
else
{
SetVolumeShadowingDefaultShaderParametersGlobal(GraphBuilder, PassParameters->VolumeShadowingShaderParameters);
}
PassParameters->VirtualShadowMapId = VisibleLightInfo->GetVirtualShadowMapId(&View);
}
else
{
SetVolumeShadowingDefaultShaderParametersGlobal(GraphBuilder, PassParameters->VolumeShadowingShaderParameters);
PassParameters->VirtualShadowMapId = -1;
}
PassParameters->VirtualShadowMapSamplingParameters = VirtualShadowMapArray.GetSamplingParameters(GraphBuilder, ViewIndex);
TRDGUniformBufferRef<FFogUniformParameters> FogBuffer = CreateFogUniformBuffer(GraphBuilder, View);
PassParameters->FogStruct = FogBuffer;
// Indirect lighting data
auto* LumenUniforms = GraphBuilder.AllocParameters<FLumenTranslucencyLightingUniforms>();
LumenUniforms->Parameters = GetLumenTranslucencyLightingParameters(GraphBuilder, View.GetLumenTranslucencyGIVolume(), View.LumenFrontLayerTranslucency);
PassParameters->LumenGIVolumeStruct = GraphBuilder.CreateUniformBuffer(LumenUniforms);
// Sparse Voxel data
PassParameters->SparseVoxelUniformBuffer = SparseVoxelUniformBuffer;
// Volume data
PassParameters->MipLevel = HeterogeneousVolumes::GetMipLevel();
// Transmittance volume
if ((HeterogeneousVolumes::UseLightingCacheForTransmittance() && bApplyShadowTransmittance) || HeterogeneousVolumes::UseLightingCacheForInscattering())
{
HeterogeneousVolumes::FLODValue LODValue = HeterogeneousVolumes::CalcLOD(View, HeterogeneousVolumeInterface);
PassParameters->LightingCache.LightingCacheResolution = HeterogeneousVolumes::GetLightingCacheResolution(HeterogeneousVolumeInterface, LODValue);
PassParameters->LightingCache.LightingCacheVoxelBias = HeterogeneousVolumeInterface->GetShadowBiasFactor();
PassParameters->LightingCache.LightingCacheTexture = LightingCacheTexture;
}
else
{
PassParameters->LightingCache.LightingCacheResolution = FIntVector::ZeroValue;
PassParameters->LightingCache.LightingCacheVoxelBias = 0.0f;
PassParameters->LightingCache.LightingCacheTexture = FRDGSystemTextures::Get(GraphBuilder).VolumetricBlack;
}
// Ray data
PassParameters->MaxTraceDistance = HeterogeneousVolumes::GetMaxTraceDistance();
PassParameters->MaxStepCount = HeterogeneousVolumes::GetMaxStepCount();
PassParameters->bJitter = HeterogeneousVolumes::ShouldJitter();
// Output
PassParameters->RWLightingTexture = GraphBuilder.CreateUAV(HeterogeneousVolumeTexture);
}
FRenderSingleScatteringWithPreshadingRGS::FPermutationDomain PermutationVector;
PermutationVector.Set<FRenderSingleScatteringWithPreshadingRGS::FApplyShadowTransmittanceDim>(bApplyShadowTransmittance);
//PermutationVector.Set<FRenderSingleScatteringWithPreshadingRGS::FUseTransmittanceVolume>(HeterogeneousVolumes::UseLightingCacheForTransmittance());
PermutationVector.Set<FRenderSingleScatteringWithPreshadingRGS::FUseInscatteringVolume>(HeterogeneousVolumes::UseLightingCacheForInscattering());
PermutationVector.Set<FRenderSingleScatteringWithPreshadingRGS::FUseLumenGI>(HeterogeneousVolumes::UseIndirectLighting() && View.GetLumenTranslucencyGIVolume().Texture0 != nullptr);
TShaderRef<FRenderSingleScatteringWithPreshadingRGS> RayGenerationShader = View.ShaderMap->GetShader<FRenderSingleScatteringWithPreshadingRGS>(PermutationVector);
FIntPoint DispatchResolution = View.ViewRect.Size();
FString LightName = TEXT("none");
if (LightSceneInfo != nullptr)
{
FSceneRenderer::GetLightNameForDrawEvent(LightSceneInfo->Proxy, LightName);
}
GraphBuilder.AddPass(
RDG_EVENT_NAME("RenderSingleScatteringWithPreshadingRGS (Light = %s) %ux%u", *LightName, DispatchResolution.X, DispatchResolution.Y),
PassParameters,
ERDGPassFlags::Compute | ERDGPassFlags::NeverCull,
[
PassParameters,
&View,
&RayTracingScene,
RayTracingGeometries,
RayGenerationShader,
DispatchResolution
](FRHICommandList& RHICmdList)
{
// Set ray-gen bindings
FRHIBatchedShaderParameters& GlobalResources = RHICmdList.GetScratchShaderParameters();
SetShaderParameters(GlobalResources, RayGenerationShader, *PassParameters);
// Create pipeline
uint32 MaxLocalBindingDataSize = 0;
FRayTracingPipelineState* RayTracingPipelineState = BuildRayTracingPipelineState(RHICmdList, View, RayGenerationShader.GetRayTracingShader(), MaxLocalBindingDataSize);
FRayTracingShaderBindingTableInitializer SBTInitializer;
// WARNING: Currently hit-group indexing is required to bind uniform buffers to hit-group shaders.
SBTInitializer.HitGroupIndexingMode = ERayTracingHitGroupIndexingMode::Allow;
SBTInitializer.ShaderBindingMode = ERayTracingShaderBindingMode::RTPSO;
SBTInitializer.NumGeometrySegments = 1;
SBTInitializer.NumShaderSlotsPerGeometrySegment = RAY_TRACING_NUM_SHADER_SLOTS;
SBTInitializer.NumMissShaderSlots = 0;
SBTInitializer.NumCallableShaderSlots = 0;
SBTInitializer.LocalBindingDataSize = MaxLocalBindingDataSize;
FShaderBindingTableRHIRef SBT = RHICmdList.CreateRayTracingShaderBindingTable(SBTInitializer);
// Set hit-group bindings
const uint32 NumBindings = 1;
FRayTracingLocalShaderBindings* Bindings = BuildRayTracingMaterialBindings(RHICmdList, View, PassParameters->SparseVoxelUniformBuffer->GetRHI(), RayTracingGeometries);
RHICmdList.SetRayTracingHitGroups(SBT, RayTracingPipelineState, NumBindings, Bindings);
RHICmdList.SetRayTracingMissShaders(SBT, RayTracingPipelineState, NumBindings, Bindings);
RHICmdList.CommitShaderBindingTable(SBT);
// Dispatch
RHICmdList.RayTraceDispatch(
RayTracingPipelineState,
RayGenerationShader.GetRayTracingShader(),
SBT,
GlobalResources,
DispatchResolution.X, DispatchResolution.Y);
}
);
}
#endif // RHI_RAYTRACING