347 lines
13 KiB
HLSL
347 lines
13 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
VolumetricFogVoxelization.usf
|
|
=============================================================================*/
|
|
|
|
// Change this to force recompilation of all shaders referencing this file.
|
|
#pragma message("UESHADERMETADATA_VERSION 43567765-38CF-4EC3-B288-49B6F14EF801")
|
|
|
|
// So vertex factories can interpolate dependencies of VertexFactoryGetTranslatedPrimitiveVolumeBounds
|
|
#define PASS_NEEDS_PRIMITIVE_VOLUME_BOUNDS 1
|
|
|
|
// Force inclusion of VertexId FVertexFactoryInput even if not otherwise required
|
|
#define PASS_NEEDS_VERTEX_ID 1
|
|
|
|
#include "Common.ush"
|
|
|
|
#if SUBSTRATE_ENABLED && !MATERIAL_IS_SUBSTRATE
|
|
#undef SUBSTRATE_ENABLED
|
|
#define SUBSTRATE_ENABLED 0
|
|
#endif
|
|
|
|
#define SceneTexturesStruct VoxelizeVolumePass.SceneTextures
|
|
|
|
#include "/Engine/Generated/Material.ush"
|
|
#include "/Engine/Generated/VertexFactory.ush"
|
|
|
|
#include "VolumetricCloudMaterialPixelCommon.ush"
|
|
|
|
#define VolumetricFog VoxelizeVolumePass.VolumetricFog
|
|
|
|
uint VoxelizationPassIndex;
|
|
|
|
float SampleExtinctionCoefficients(in FPixelMaterialInputs PixelMaterialInputs)
|
|
{
|
|
float Extinction = 0.0f;
|
|
#if SUBSTRATE_ENABLED
|
|
FSubstrateBSDF BSDF = PixelMaterialInputs.FrontMaterial.InlinedBSDF;
|
|
Extinction = VOLUMETRICFOGCLOUD_EXTINCTION(BSDF).r;
|
|
#else
|
|
#if !MATERIAL_SHADINGMODEL_UNLIT
|
|
Extinction = GetMaterialSubsurfaceDataRaw(PixelMaterialInputs).r;
|
|
#endif
|
|
#endif
|
|
return clamp(Extinction, 0.0f, 65000.0f);
|
|
}
|
|
|
|
float3 SampleEmissive(in FPixelMaterialInputs PixelMaterialInputs)
|
|
{
|
|
float3 EmissiveColor = 0.0f;
|
|
#if SUBSTRATE_ENABLED
|
|
FSubstrateBSDF BSDF = PixelMaterialInputs.FrontMaterial.InlinedBSDF;
|
|
EmissiveColor = BSDF_GETEMISSIVE(BSDF);
|
|
#else
|
|
EmissiveColor = GetMaterialEmissiveRaw(PixelMaterialInputs);
|
|
#endif
|
|
return clamp(EmissiveColor, 0.0f, 65000.0f);
|
|
}
|
|
|
|
float3 SampleAlbedo(in FPixelMaterialInputs PixelMaterialInputs, in ViewState InputView)
|
|
{
|
|
float3 Albedo = 0.0f;
|
|
#if SUBSTRATE_ENABLED
|
|
FSubstrateBSDF BSDF = PixelMaterialInputs.FrontMaterial.InlinedBSDF;
|
|
Albedo = VOLUMETRICFOGCLOUD_ALBEDO(BSDF);
|
|
#else
|
|
#if !MATERIAL_SHADINGMODEL_UNLIT
|
|
Albedo = GetMaterialBaseColor(PixelMaterialInputs);
|
|
#endif
|
|
#endif
|
|
return saturate(Albedo)* InputView.DiffuseOverrideParameter.w + InputView.DiffuseOverrideParameter.xyz;
|
|
}
|
|
|
|
struct FVoxelizeVolumePrimitiveVSToGS
|
|
{
|
|
FVertexFactoryInterpolantsVSToPS FactoryInterpolants;
|
|
float2 VertexQuadCoordinate : ATTRIBUTE0;
|
|
};
|
|
|
|
struct FVoxelizeVolumePrimitiveGSToPS
|
|
{
|
|
FVertexFactoryInterpolantsVSToPS FactoryInterpolants;
|
|
float4 OutPosition : SV_POSITION;
|
|
uint SliceIndex : SV_RenderTargetArrayIndex;
|
|
};
|
|
|
|
int ComputeZSliceFromDepth(float SceneDepth)
|
|
{
|
|
return (int)(log2(SceneDepth * VolumetricFog.GridZParams.x + VolumetricFog.GridZParams.y) * VolumetricFog.GridZParams.z);
|
|
}
|
|
|
|
float ComputeDepthFromZSlice(float ZSlice)
|
|
{
|
|
float SliceDepth = (exp2(ZSlice / VolumetricFog.GridZParams.z) - VolumetricFog.GridZParams.y) / VolumetricFog.GridZParams.x;
|
|
return SliceDepth;
|
|
}
|
|
|
|
#define PRIMITIVE_SPHERE_MODE 0
|
|
#define OBJECT_BOX_MODE 1
|
|
|
|
#ifndef MAX_SLICES_PER_VOXELIZATION_PASS
|
|
#define MAX_SLICES_PER_VOXELIZATION_PASS 1
|
|
#endif
|
|
|
|
#if USING_VERTEX_SHADER_LAYER
|
|
#define NUM_INPUT_VERTICES 1
|
|
void Voxelize(FVoxelizeVolumePrimitiveVSToGS Inputs[1], out FVoxelizeVolumePrimitiveGSToPS Output)
|
|
#else
|
|
#define NUM_INPUT_VERTICES 3
|
|
[maxvertexcount(MAX_SLICES_PER_VOXELIZATION_PASS * 3)]
|
|
void VoxelizeGS(triangle FVoxelizeVolumePrimitiveVSToGS Inputs[3], inout TriangleStream<FVoxelizeVolumePrimitiveGSToPS> OutStream)
|
|
#endif
|
|
{
|
|
#if !USING_VERTEX_SHADER_LAYER
|
|
ResolvedView = ResolveView();
|
|
#endif
|
|
|
|
int GridSizeZ = VolumetricFog.ViewGridSize.z;
|
|
float4 PrimitiveVolumeBounds = VertexFactoryGetTranslatedPrimitiveVolumeBounds(Inputs[0].FactoryInterpolants);
|
|
float3 ViewSpacePrimitiveVolumeOrigin = mul(float4(PrimitiveVolumeBounds.xyz, 1), ResolvedView.TranslatedWorldToView).xyz;
|
|
|
|
uint PrimitiveId = VertexFactoryGetPrimitiveId(Inputs[0].FactoryInterpolants);
|
|
float3 LocalObjectBoundsMin = GetPrimitiveData(PrimitiveId).LocalObjectBoundsMin;
|
|
float3 LocalObjectBoundsMax = GetPrimitiveData(PrimitiveId).LocalObjectBoundsMax;
|
|
FDFMatrix LocalToWorld = GetPrimitiveData(PrimitiveId).LocalToWorld;
|
|
|
|
float QuadDepth = ViewSpacePrimitiveVolumeOrigin.z;
|
|
|
|
int FurthestSliceIndexUnclamped = ComputeZSliceFromDepth(QuadDepth + PrimitiveVolumeBounds.w);
|
|
int ClosestSliceIndexUnclamped = ComputeZSliceFromDepth(QuadDepth - PrimitiveVolumeBounds.w);
|
|
|
|
// Clamp to valid range, start at first slice for the current pass
|
|
int ClosestSliceIndex = max(ClosestSliceIndexUnclamped, 0) + MAX_SLICES_PER_VOXELIZATION_PASS * VoxelizationPassIndex;
|
|
// Clamp to valid range, end at the last slice for the current pass
|
|
int FurthestSliceIndex = min(min(FurthestSliceIndexUnclamped, GridSizeZ - 1), ClosestSliceIndex + MAX_SLICES_PER_VOXELIZATION_PASS - 1);
|
|
|
|
if (ClosestSliceIndexUnclamped < GridSizeZ
|
|
&& ClosestSliceIndex <= FurthestSliceIndexUnclamped
|
|
&& FurthestSliceIndexUnclamped >= 0)
|
|
{
|
|
#if VOXELIZE_SHAPE_MODE == PRIMITIVE_SPHERE_MODE
|
|
|
|
float InvPrimitiveVolumeRadius = 1.0f / PrimitiveVolumeBounds.w;
|
|
float2 PrimitiveCenterToVertex[NUM_INPUT_VERTICES];
|
|
|
|
{
|
|
UNROLL
|
|
for (uint VertexIndex = 0; VertexIndex < NUM_INPUT_VERTICES; VertexIndex++)
|
|
{
|
|
PrimitiveCenterToVertex[VertexIndex] = (Inputs[VertexIndex].VertexQuadCoordinate - ViewSpacePrimitiveVolumeOrigin.xy) * InvPrimitiveVolumeRadius;
|
|
}
|
|
}
|
|
|
|
#elif VOXELIZE_SHAPE_MODE == OBJECT_BOX_MODE
|
|
float2 ViewSpaceBoundingBoxMin = 10000000;
|
|
float2 ViewSpaceBoundingBoxMax = -10000000;
|
|
|
|
for (int BoxVertex = 0; BoxVertex < 8; BoxVertex++)
|
|
{
|
|
float3 BoxVertexMask = float3((BoxVertex >> 0) & 1, (BoxVertex >> 1) & 1, (BoxVertex >> 2) & 1);
|
|
float3 BoxVertexLocalPosition = LocalObjectBoundsMin + (LocalObjectBoundsMax - LocalObjectBoundsMin) * BoxVertexMask;
|
|
float3 BoxVertexPosition = DFTransformLocalToTranslatedWorld(BoxVertexLocalPosition, LocalToWorld, ResolvedView.PreViewTranslation).xyz;
|
|
float3 ViewSpaceBoxVertex = mul(float4(BoxVertexPosition, 1), ResolvedView.TranslatedWorldToView).xyz;
|
|
ViewSpaceBoundingBoxMin = min(ViewSpaceBoundingBoxMin, ViewSpaceBoxVertex.xy);
|
|
ViewSpaceBoundingBoxMax = max(ViewSpaceBoundingBoxMax, ViewSpaceBoxVertex.xy);
|
|
}
|
|
#endif
|
|
|
|
// Clone the triangle to each slice
|
|
for (int SliceIndex = ClosestSliceIndex; SliceIndex <= FurthestSliceIndex; SliceIndex++)
|
|
{
|
|
float SliceDepth = ComputeDepthFromZSlice(SliceIndex + VoxelizeVolumePass.FrameJitterOffset0.z);
|
|
float SliceDepthOffset = abs(SliceDepth - ViewSpacePrimitiveVolumeOrigin.z);
|
|
|
|
BRANCH
|
|
if (PrimitiveVolumeBounds.w == 0 || SliceDepthOffset <= PrimitiveVolumeBounds.w)
|
|
{
|
|
float SliceRadius = sqrt(max(PrimitiveVolumeBounds.w * PrimitiveVolumeBounds.w - SliceDepthOffset * SliceDepthOffset, 0.0f));
|
|
|
|
float2 ViewSpaceVertices[NUM_INPUT_VERTICES];
|
|
|
|
{
|
|
UNROLL
|
|
for (uint VertexIndex = 0; VertexIndex < NUM_INPUT_VERTICES; VertexIndex++)
|
|
{
|
|
#if VOXELIZE_SHAPE_MODE == PRIMITIVE_SPHERE_MODE
|
|
// Use vertex positions that bound this slice of the sphere in view space
|
|
ViewSpaceVertices[VertexIndex] = ViewSpacePrimitiveVolumeOrigin.xy + PrimitiveCenterToVertex[VertexIndex] * SliceRadius;
|
|
#elif VOXELIZE_SHAPE_MODE == OBJECT_BOX_MODE
|
|
// Use vertex positions that bound the OBB in view space
|
|
//@todo - accurately intersect each slice with the OBB producing a 3-6 vertex polygon like "A Vertex Program for Efficient Box-Plane Intersection"
|
|
float2 CornerMask = Inputs[VertexIndex].VertexQuadCoordinate;
|
|
ViewSpaceVertices[VertexIndex] = ViewSpaceBoundingBoxMin + (ViewSpaceBoundingBoxMax - ViewSpaceBoundingBoxMin) * CornerMask;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
{
|
|
UNROLL
|
|
for (uint VertexIndex = 0; VertexIndex < NUM_INPUT_VERTICES; VertexIndex++)
|
|
{
|
|
#if !USING_VERTEX_SHADER_LAYER
|
|
FVoxelizeVolumePrimitiveGSToPS Output;
|
|
#endif
|
|
Output.SliceIndex = SliceIndex;
|
|
|
|
float3 ViewSpaceVertexPosition = float3(ViewSpaceVertices[VertexIndex], SliceDepth);
|
|
Output.OutPosition = mul(float4(ViewSpaceVertexPosition, 1), VoxelizeVolumePass.ViewToVolumeClip);
|
|
Output.FactoryInterpolants = Inputs[VertexIndex].FactoryInterpolants;
|
|
|
|
#if !USING_VERTEX_SHADER_LAYER
|
|
OutStream.Append(Output);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if !USING_VERTEX_SHADER_LAYER
|
|
OutStream.RestartStrip();
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void VoxelizeVS(
|
|
FVertexFactoryInput Input,
|
|
#if USING_VERTEX_SHADER_LAYER
|
|
out FVoxelizeVolumePrimitiveGSToPS Output
|
|
#else
|
|
out FVoxelizeVolumePrimitiveVSToGS Output
|
|
#endif
|
|
)
|
|
{
|
|
ResolvedView = ResolveView();
|
|
|
|
FVertexFactoryIntermediates VFIntermediates = GetVertexFactoryIntermediates(Input);
|
|
float4 TranslatedWorldPosition = VertexFactoryGetWorldPosition(Input, VFIntermediates);
|
|
float3x3 TangentToLocal = VertexFactoryGetTangentToLocal(Input, VFIntermediates);
|
|
|
|
// Warning: with OBJECT_BOX_MODE enabled, TranslatedWorldPosition here is wrong. Any VF interpolants based on it which are passed to other shader stages will also be wrong.
|
|
FMaterialVertexParameters VertexParameters = GetMaterialVertexParameters(Input, VFIntermediates, TranslatedWorldPosition.xyz, TangentToLocal);
|
|
Output.FactoryInterpolants = VertexFactoryGetInterpolantsVSToPS(Input, VFIntermediates, VertexParameters);
|
|
|
|
float3 ViewSpacePosition = mul(TranslatedWorldPosition, ResolvedView.TranslatedWorldToView).xyz;
|
|
|
|
#if USING_VERTEX_SHADER_LAYER
|
|
FVoxelizeVolumePrimitiveVSToGS Inputs[1];
|
|
Inputs[0].FactoryInterpolants = Output.FactoryInterpolants;
|
|
|
|
#if VOXELIZE_SHAPE_MODE == PRIMITIVE_SPHERE_MODE
|
|
Inputs[0].VertexQuadCoordinate = ViewSpacePosition.xy;
|
|
#elif VOXELIZE_SHAPE_MODE == OBJECT_BOX_MODE
|
|
Inputs[0].VertexQuadCoordinate = float2(Input.VertexId & 1, (Input.VertexId >> 1) & 1);
|
|
#endif
|
|
|
|
Voxelize(Inputs, Output);
|
|
#else
|
|
#if VOXELIZE_SHAPE_MODE == PRIMITIVE_SPHERE_MODE
|
|
Output.VertexQuadCoordinate = ViewSpacePosition.xy;
|
|
#elif VOXELIZE_SHAPE_MODE == OBJECT_BOX_MODE
|
|
Output.VertexQuadCoordinate = float2(Input.VertexId & 1, (Input.VertexId >> 1) & 1);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
float ComputeVolumeShapeMasking(float3 TranslatedWorldPosition, uint PrimitiveId, FVertexFactoryInterpolantsVSToPS FactoryInterpolants)
|
|
{
|
|
float ValidRegionMask = 1;
|
|
|
|
#if VOXELIZE_SHAPE_MODE == PRIMITIVE_SPHERE_MODE
|
|
|
|
float4 PrimitiveVolumeBounds = VertexFactoryGetTranslatedPrimitiveVolumeBounds(FactoryInterpolants);
|
|
float3 ConnectingVector = PrimitiveVolumeBounds.xyz - TranslatedWorldPosition;
|
|
|
|
FLATTEN
|
|
if (dot(ConnectingVector, ConnectingVector) > PrimitiveVolumeBounds.w * PrimitiveVolumeBounds.w)
|
|
{
|
|
ValidRegionMask = 0;
|
|
}
|
|
|
|
#elif VOXELIZE_SHAPE_MODE == OBJECT_BOX_MODE
|
|
|
|
float3 LocalObjectBoundsMin = GetPrimitiveData(PrimitiveId).LocalObjectBoundsMin;
|
|
float3 LocalObjectBoundsMax = GetPrimitiveData(PrimitiveId).LocalObjectBoundsMax;
|
|
|
|
float4x4 TranslatedWorldToLocal = DFFastToTranslatedWorld(GetPrimitiveData(PrimitiveId).WorldToLocal, ResolvedView.PreViewTranslation);
|
|
float3 LocalPosition = mul(float4(TranslatedWorldPosition, 1), TranslatedWorldToLocal).xyz;
|
|
|
|
FLATTEN
|
|
if (any((LocalPosition < LocalObjectBoundsMin || LocalPosition > LocalObjectBoundsMax)))
|
|
{
|
|
ValidRegionMask = 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
return ValidRegionMask;
|
|
}
|
|
|
|
void VoxelizePS(
|
|
FVoxelizeVolumePrimitiveGSToPS Interpolants,
|
|
out float4 OutVBufferA : SV_Target0,
|
|
out float4 OutVBufferB : SV_Target1
|
|
)
|
|
{
|
|
float4 SvPosition = Interpolants.OutPosition;
|
|
|
|
ResolvedView = ResolveView();
|
|
|
|
float ValidRegionMask = 1;
|
|
|
|
FMaterialPixelParameters MaterialParameters = GetMaterialPixelParameters(Interpolants.FactoryInterpolants, SvPosition);
|
|
|
|
#if CLOUD_LAYER_PIXEL_SHADER
|
|
FCloudLayerParameters CloudLayerParams = GetCloudLayerParams(
|
|
VoxelizeVolumePass.RenderVolumetricCloudParametersCloudLayerCenterKm, VoxelizeVolumePass.RenderVolumetricCloudParametersPlanetRadiusKm,
|
|
VoxelizeVolumePass.RenderVolumetricCloudParametersBottomRadiusKm, VoxelizeVolumePass.RenderVolumetricCloudParametersTopRadiusKm);
|
|
float3 TranslatedWorldPosition = SvPositionToTranslatedWorld(SvPosition);
|
|
// WorldPositionDDX/Y only need to be set when using analytical derivatives, which is not needed in pixel shaders
|
|
UpdateMaterialCloudParam(MaterialParameters, TranslatedWorldPosition, 0.0f /*WorldPositionDDX*/, 0.0f /*WorldPositionDDY*/, ResolvedView, CloudLayerParams, TRACING_SHADOW_DISTANCE_OFF, SPACE_SKIPPING_SLICE_DEPTH_OFF);
|
|
#endif
|
|
|
|
// Scale rendered position to account for frustum difference between fog voxelization and rendered scene
|
|
float4 AdjustedSvPosition = SvPosition * float4(VoxelizeVolumePass.ClipRatio, 1, 1);
|
|
|
|
FPixelMaterialInputs PixelMaterialInputs;
|
|
CalcMaterialParameters(MaterialParameters, PixelMaterialInputs, AdjustedSvPosition, true);
|
|
|
|
float3 EmissiveColor = SampleEmissive(PixelMaterialInputs);
|
|
float3 Albedo = SampleAlbedo(PixelMaterialInputs, ResolvedView);
|
|
float Extinction = SampleExtinctionCoefficients(PixelMaterialInputs);
|
|
|
|
float3 Scattering = Albedo * Extinction;
|
|
|
|
float FadeStart = .6f;
|
|
float SliceFadeAlpha = 1 - saturate((SvPosition.w / VolumetricFog.MaxDistance - FadeStart) / (1 - FadeStart));
|
|
|
|
float UnitScale = 1.0f / 100.0f;
|
|
float Scale = UnitScale * SliceFadeAlpha * SliceFadeAlpha * SliceFadeAlpha;
|
|
|
|
// Would be faster to branch around the whole material evaluation, but that would cause divergent flow control for gradient operations
|
|
Scale *= ComputeVolumeShapeMasking(MaterialParameters.WorldPosition_CamRelative, MaterialParameters.PrimitiveId, Interpolants.FactoryInterpolants);
|
|
|
|
OutVBufferA = float4(Scattering * Scale, Extinction * Scale);
|
|
OutVBufferB = float4(EmissiveColor * Scale, 0);
|
|
}
|