Files
UnrealEngine/Engine/Shaders/Private/HeterogeneousVolumes/HeterogeneousVolumesAmbientOcclusionPipeline.usf
2025-05-18 13:04:45 +08:00

416 lines
11 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#include "../Common.ush"
#define SUPPORT_CONTACT_SHADOWS 0
#include "/Engine/Generated/Material.ush"
#include "HeterogeneousVolumesStochasticFiltering.ush"
#include "HeterogeneousVolumesLiveShadingUtils.ush"
#include "HeterogeneousVolumesTracingUtils.ush"
#include "HeterogeneousVolumesLightingUtils.ush"
#include "../BlueNoise.ush"
#ifndef THREADGROUP_SIZE_1D
#define THREADGROUP_SIZE_1D 1
#endif // THREADGROUP_SIZE_1D
#ifndef THREADGROUP_SIZE_2D
#define THREADGROUP_SIZE_2D 1
#endif // THREADGROUP_SIZE_2D
#ifndef THREADGROUP_SIZE_3D
#define THREADGROUP_SIZE_3D 1
#endif // THREADGROUP_SIZE_3D
#ifndef USE_EXISTENCE_MASK
#define USE_EXISTENCE_MASK 1
#endif //
// Object data
float4x4 LocalToWorld;
float4x4 WorldToLocal;
float3 LocalBoundsOrigin;
float3 LocalBoundsExtent;
int PrimitiveId;
// Volume data
int3 VoxelResolution;
int3 VoxelMin;
int3 VoxelMax;
// Shadowing
float ShadowStepSize;
float ShadowStepFactor;
// Ray data
float MaxTraceDistance;
float MaxShadowTraceDistance;
float StepSize;
float StepFactor;
int MaxStepCount;
int bJitter;
int StochasticFilteringMode;
Texture3D ExistenceMaskTexture;
// Optional cinematic features
int bIsOfflineRender;
// AO data
int2 NumRays;
FPrimitiveSceneData GetPrimitiveData(FMaterialVertexParameters Parameters)
{
return GetPrimitiveData(Parameters, LocalToWorld, WorldToLocal, LocalBoundsOrigin, LocalBoundsExtent);
}
FPrimitiveSceneData GetPrimitiveData(FMaterialPixelParameters Parameters)
{
return GetPrimitiveData(Parameters, LocalToWorld, WorldToLocal, LocalBoundsOrigin, LocalBoundsExtent);
}
float4x4 GetLocalToWorld()
{
return LocalToWorld;
}
float4x4 GetWorldToLocal()
{
return WorldToLocal;
}
float3 GetLocalBoundsOrigin()
{
return LocalBoundsOrigin;
}
float3 GetLocalBoundsExtent()
{
return LocalBoundsExtent;
}
int3 GetVolumeResolution()
{
return VoxelResolution;
}
float GetStepSize()
{
return StepSize;
}
float GetShadowStepSize()
{
return ShadowStepSize;
}
float GetStepFactor()
{
return StepFactor;
}
float GetShadowStepFactor()
{
return ShadowStepFactor;
}
float GetMaxTraceDistance()
{
return MaxTraceDistance;
}
float GetMaxShadowTraceDistance()
{
return MaxShadowTraceDistance;
}
int GetStochasticFilteringMode()
{
return StochasticFilteringMode;
}
float EvalAmbientOcclusion(float3 WorldPosition)
{
return 0.0;
}
float GetIndirectInscatteringFactor()
{
return 0.0;
}
// Material sampling hooks..
struct FVolumeSampleContext
{
FMaterialPixelParameters MaterialParameters;
FPixelMaterialInputs PixelMaterialInputs;
};
FVolumeSampleContext CreateVolumeSampleContext(float3 LocalPosition, float3 WorldPosition, float3 WorldPosition_DDX, float3 WorldPosition_DDY, float MipLevel)
{
FVolumeSampleContext VolumeSampleContext;
VolumeSampleContext.MaterialParameters = MakeInitializedMaterialPixelParameters();
//VolumeSampleContext.MaterialParameters.PrimitiveId = PrimitiveId;
VolumeSampleContext.MaterialParameters.AbsoluteWorldPosition = DFPromote(WorldPosition);
VolumeSampleContext.MaterialParameters.LWCData.AbsoluteWorldPosition = WSPromote(WorldPosition);
// TODO: Add object centroid to LWC.ObjectWorldPosition
VolumeSampleContext.MaterialParameters.LWCData.LocalToWorld = WSPromote(GetLocalToWorld());
VolumeSampleContext.MaterialParameters.LWCData.WorldToLocal = WSPromoteInverse(GetWorldToLocal());
#if USE_ANALYTIC_DERIVATIVES
VolumeSampleContext.MaterialParameters.WorldPosition_DDX = WorldPosition_DDX;
VolumeSampleContext.MaterialParameters.WorldPosition_DDY = WorldPosition_DDY;
CalcPixelMaterialInputsAnalyticDerivatives(VolumeSampleContext.MaterialParameters, VolumeSampleContext.PixelMaterialInputs);
#else
CalcPixelMaterialInputs(VolumeSampleContext.MaterialParameters, VolumeSampleContext.PixelMaterialInputs);
#endif
return VolumeSampleContext;
}
FVolumeSampleContext CreateVolumeSampleContext(float3 LocalPosition, float3 WorldPosition, float MipLevel)
{
float3 WorldPosition_DDX = 0.0;
float3 WorldPosition_DDY = 0.0;
return CreateVolumeSampleContext(
LocalPosition,
WorldPosition,
WorldPosition_DDX,
WorldPosition_DDY,
MipLevel
);
}
FVolumeSampleContext CreateVolumeSampleContext(float3 LocalPosition, float3 WorldPosition, float3 FilterWidth, float MipLevel, float Rand, int StochasticFilteringMode)
{
// TODO: Use FilterWidth determine MipLevel
//float3 WorldPosition_DDX = mul(float4(LocalPosition + float3(1.0, 0.0, 0.0), 1.0), GetLocalToWorld()).xyz;
//float3 WorldPosition_DDY = mul(float4(LocalPosition + float3(0.0, 1.0, 0.0), 1.0), GetLocalToWorld()).xyz;
//float3 WorldPosition_DDZ = mul(float4(LocalPosition + float3(0.0, 0.0, 1.0), 1.0), GetLocalToWorld()).xyz;
if (StochasticFilteringMode == STOCHASTIC_FILTERING_CONSTANT)
{
LocalPosition = StochasticFilteringConstant(LocalPosition, Rand);
}
else if (StochasticFilteringMode == STOCHASTIC_FILTERING_TRILINEAR)
{
LocalPosition = StochasticFilteringTrilinear(LocalPosition, Rand);
}
else if (StochasticFilteringMode == STOCHASTIC_FILTERING_TRICUBIC)
{
LocalPosition = StochasticFilteringTricubic(LocalPosition, Rand);
}
WorldPosition = mul(float4(LocalPosition, 1.0), GetLocalToWorld()).xyz;
return CreateVolumeSampleContext(LocalPosition, WorldPosition, MipLevel);
}
float3 SampleExtinction(inout FVolumeSampleContext Context)
{
float3 Extinction = SampleExtinctionCoefficients(Context.PixelMaterialInputs);
return Extinction;
}
float3 SampleEmission(inout FVolumeSampleContext Context)
{
float3 Emission = SampleEmissive(Context.PixelMaterialInputs);
return Emission;
}
float3 SampleAlbedo(inout FVolumeSampleContext Context)
{
float3 Albedo = SampleAlbedo(Context.PixelMaterialInputs);
return Albedo;
}
// Cinematic feature options
#define INDIRECT_LIGHTING_MODE_OFF 0
#define INDIRECT_LIGHTING_MODE_LIGHTING_CACHE 1
#define INDIRECT_LIGHTING_MODE_SINGLE_SCATTERING 2
int GetIndirectLightingMode()
{
return INDIRECT_LIGHTING_MODE_OFF;
}
bool IsOfflineRender()
{
#if HV_SCALABILITY_MODE == HV_SCALABILITY_MODE_CINEMATIC
return bIsOfflineRender;
#else
return false;
#endif
}
int ShouldApplyHeightFog()
{
return false;
}
int ShouldApplyVolumetricFog()
{
return false;
}
#define FOG_INSCATTERING_MODE_OFF 0
#define FOG_INSCATTERING_MODE_REFERENCE 1
#define FOG_INSCATTERING_MODE_LINEAR_APPROX 2
int GetFogInscatteringMode()
{
return FOG_INSCATTERING_MODE_OFF;
}
#include "HeterogeneousVolumesRayMarchingUtils.ush"
// Output
RWTexture3D<uint> RWAmbientOcclusionUIntTexture;
FRayMarchingContext CreateShadowRayMarchingContext(
float3 WorldRayOrigin,
float3 WorldToLight,
uint MaxStepCount
)
{
float3 WorldRayEnd = WorldRayOrigin + WorldToLight;
float3 WorldRayDirection = normalize(WorldToLight);
float3 LocalRayOrigin = mul(float4(WorldRayOrigin, 1.0), GetWorldToLocal()).xyz;
float3 LocalRayEnd = mul(float4(WorldRayEnd, 1.0), GetWorldToLocal()).xyz;
float3 LocalRayDirection = LocalRayEnd - LocalRayOrigin;
float LocalRayTMin = 0.0;
float LocalRayTMax = length(LocalRayDirection);
LocalRayDirection /= LocalRayTMax;
float ShadowBias = 0.5;
float ShadowStepSize = CalcShadowStepSize(LocalRayDirection);
int bApplyEmissionAndTransmittance = 0;
int bApplyDirectLighting = 0;
int bApplyShadowTransmittance = 0;
FRayMarchingContext ShadowRayMarchingContext = CreateRayMarchingContext(
LocalRayOrigin,
LocalRayDirection,
LocalRayTMin,
LocalRayTMax,
WorldRayOrigin,
WorldRayDirection,
ShadowBias,
ShadowStepSize,
MaxStepCount,
bApplyEmissionAndTransmittance,
bApplyDirectLighting,
bApplyShadowTransmittance
//RayMarchingContext.MaxShadowTraceDistance
);
return ShadowRayMarchingContext;
}
[numthreads(THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D)]
void RenderAmbientOcclusionWithLiveShadingCS(
uint3 DispatchThreadId : SV_DispatchThreadID
)
{
int3 VoxelCount = VoxelMax - VoxelMin + 1;
int3 VoxelIndex = float3(DispatchThreadId.x, DispatchThreadId.y, DispatchThreadId.z % VoxelCount.z);
if (any(VoxelIndex >= VoxelCount))
{
return;
}
VoxelIndex += VoxelMin;
int LinearRayIndex = DispatchThreadId.z / VoxelCount.z;
int2 RayIndex = int2(LinearRayIndex % NumRays.x, LinearRayIndex / NumRays.x);
if (any(RayIndex >= NumRays))
{
return;
}
float3 VoxelJitter = 0.5;
if (bJitter)
{
uint3 Rand32Bits = Rand4DPCG32(uint4(VoxelIndex, View.StateFrameIndexMod8)).xyz;
VoxelJitter = float3(Rand32Bits) / float(uint(0xffffffff));
}
float3 UVW = (VoxelIndex + VoxelJitter) / float3(GetLightingCacheResolution());
float3 LocalBoundsMin = GetLocalBoundsOrigin() - GetLocalBoundsExtent();
float3 LocalBoundsMax = GetLocalBoundsOrigin() + GetLocalBoundsExtent();
float3 LocalRayOrigin = UVW * GetLocalBoundsExtent() * 2.0 + LocalBoundsMin;
float3 WorldRayOrigin = mul(float4(LocalRayOrigin, 1.0), GetLocalToWorld()).xyz;
// Construct ray direction
float2 RayUV = float2(RayIndex + 0.5) / NumRays;
float3 WorldRayDirection = EquiAreaSphericalMapping(RayUV);
// Evaluate transmittance based on MaxShadowDistance
FRayMarchingContext RayMarchingContext = CreateShadowRayMarchingContext(WorldRayOrigin, WorldRayDirection * MaxShadowTraceDistance, MaxStepCount);
float AmbientOcclusion = 1.0;
#if USE_EXISTENCE_MASK
float Existence = ExistenceMaskTexture.SampleLevel(View.SharedBilinearClampedSampler, UVW, 0).r;
if (Existence > 0.0)
#endif
{
float3 Transmittance = ComputeTransmittance(RayMarchingContext);
AmbientOcclusion = Luminance(Transmittance) * rcp(NumRays.x * NumRays.y);
}
// Convert to fixed-point and accumulate
int AmbientOcclusionFixedPoint = AmbientOcclusion * 65535.0;
InterlockedAdd(RWAmbientOcclusionUIntTexture[VoxelIndex], AmbientOcclusionFixedPoint);
}
RWTexture3D<float> RWExistenceMaskTexture;
[numthreads(THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D)]
void RenderExistenceMaskWithLiveShadingCS(
uint3 DispatchThreadId : SV_DispatchThreadID
)
{
int3 VoxelCount = VoxelMax - VoxelMin + 1;
int3 VoxelIndex = DispatchThreadId;
if (any(VoxelIndex > VoxelCount))
{
return;
}
VoxelIndex += VoxelMin;
float3 VoxelJitter = 0.5;
if (bJitter)
{
uint3 Rand32Bits = Rand4DPCG32(uint4(VoxelIndex, View.StateFrameIndexMod8)).xyz;
VoxelJitter = float3(Rand32Bits) / float(uint(0xffffffff));
}
float3 UVW = (VoxelIndex + VoxelJitter) / float3(VoxelResolution);
float3 LocalBoundsMin = GetLocalBoundsOrigin() - GetLocalBoundsExtent();
float3 LocalBoundsMax = GetLocalBoundsOrigin() + GetLocalBoundsExtent();
float3 LocalPosition = UVW * GetLocalBoundsExtent() * 2.0 + LocalBoundsMin;
float3 WorldPosition = mul(float4(LocalPosition, 1.0), GetLocalToWorld()).xyz;
int MipLevel = 0;
FVolumeSampleContext SampleContext;
if (IsOfflineRender())
{
float3 FilterWidth = 1.0;
RandomSequence RandSequence;
int LinearIndex = VoxelIndex.x * (VoxelResolution.y * VoxelResolution.z) + VoxelIndex.y * VoxelResolution.z + VoxelIndex.z;
RandomSequence_Initialize(RandSequence, LinearIndex, View.StateFrameIndex);
float Rand = RandomSequence_GenerateSample1D(RandSequence);
int StochasticFilteringMode = GetStochasticFilteringMode();
SampleContext = CreateVolumeSampleContext(LocalPosition, WorldPosition, FilterWidth, MipLevel, Rand, StochasticFilteringMode);
}
else
{
SampleContext = CreateVolumeSampleContext(LocalPosition, WorldPosition, MipLevel);
}
float3 Extinction = SampleExtinction(SampleContext);
RWExistenceMaskTexture[VoxelIndex] = any(Extinction > 0.0) ? 1.0 : 0.0;
}