993 lines
28 KiB
HLSL
993 lines
28 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "HeterogeneousVolumesSparseVoxelUtils.ush"
|
|
#include "../Common.ush"
|
|
|
|
#define SUPPORT_CONTACT_SHADOWS 0
|
|
#define EYE_ADAPTATION_LOOSE_PARAMETERS 1
|
|
|
|
#define DYNAMICALLY_SHADOWED 1
|
|
#define TREAT_MAXDEPTH_UNSHADOWED 1
|
|
|
|
#define SHADOW_QUALITY 2
|
|
#define NO_TRANSLUCENCY_AVAILABLE
|
|
|
|
#ifndef SUPPORT_OVERLAPPING_VOLUMES
|
|
#define SUPPORT_OVERLAPPING_VOLUMES 0
|
|
#endif //
|
|
|
|
#ifndef ADAPTIVE_MARCH
|
|
#define ADAPTIVE_MARCH 0
|
|
#endif //
|
|
|
|
#ifndef REFERENCE_FAST_PATH
|
|
#define REFERENCE_FAST_PATH 0
|
|
#endif //
|
|
|
|
#ifndef DIM_USE_ADAPTIVE_VOLUMETRIC_SHADOW_MAP
|
|
#define DIM_USE_ADAPTIVE_VOLUMETRIC_SHADOW_MAP (AVSM_SAMPLE_MODE != 0)
|
|
#endif //
|
|
|
|
#ifndef USE_ANALYTIC_DERIVATIVES
|
|
#define USE_ANALYTIC_DERIVATIVES 0
|
|
#endif //
|
|
|
|
#define USE_CAMERA_AVSM (DIM_USE_ADAPTIVE_VOLUMETRIC_SHADOW_MAP && (SUPPORT_OVERLAPPING_VOLUMES != 0))
|
|
|
|
#if SUPPORT_OVERLAPPING_VOLUMES
|
|
#define AVSM AVSMs.AVSM
|
|
#define CameraAVSM AVSMs.CameraAVSM
|
|
#endif // SUPPORT_OVERLAPPING_VOLUMES
|
|
|
|
#include "/Engine/Generated/Material.ush"
|
|
#include "HeterogeneousVolumesStochasticFiltering.ush"
|
|
#include "HeterogeneousVolumesLiveShadingUtils.ush"
|
|
#include "HeterogeneousVolumesTracingUtils.ush"
|
|
#include "HeterogeneousVolumesLightingUtils.ush"
|
|
#include "../BlueNoise.ush"
|
|
|
|
#if VIRTUAL_SHADOW_MAP
|
|
#include "../VirtualShadowMaps/VirtualShadowMapProjectionCommon.ush"
|
|
#endif
|
|
|
|
#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
|
|
|
|
// Object data
|
|
float4x4 LocalToWorld;
|
|
float4x4 WorldToLocal;
|
|
float3 LocalBoundsOrigin;
|
|
float3 LocalBoundsExtent;
|
|
int PrimitiveId;
|
|
|
|
// Volume data
|
|
int3 VoxelResolution;
|
|
int3 VoxelMin;
|
|
int3 VoxelMax;
|
|
|
|
// Lighting
|
|
int bHoldout;
|
|
int bApplyEmissionAndTransmittance;
|
|
int bApplyDirectLighting;
|
|
int bApplyShadowTransmittance;
|
|
int LightType;
|
|
float VolumetricScatteringIntensity;
|
|
|
|
// Shadowing
|
|
uint VirtualShadowMapId;
|
|
float ShadowStepSize;
|
|
float ShadowStepFactor;
|
|
|
|
// Atmosphere
|
|
int bApplyHeightFog;
|
|
int bApplyVolumetricFog;
|
|
int bCreateBeerShadowMap;
|
|
|
|
// Indirect
|
|
float IndirectInscatteringFactor;
|
|
|
|
// Ray data
|
|
float MaxTraceDistance;
|
|
float MaxShadowTraceDistance;
|
|
float StepSize;
|
|
float StepFactor;
|
|
int MaxStepCount;
|
|
int bJitter;
|
|
int StochasticFilteringMode;
|
|
|
|
// Dispatch data
|
|
int3 GroupCount;
|
|
int DownsampleFactor;
|
|
|
|
// Optional cinematic features
|
|
int bUseLightingCacheForInscattering;
|
|
int IndirectLightingMode;
|
|
int bWriteVelocity;
|
|
int AVSMSampleMode;
|
|
int bSupportsOverlappingVolumes;
|
|
int bIsOfflineRender;
|
|
int FogInscatteringMode;
|
|
int bUseAnalyticDerivatives;
|
|
int bUseReferenceFastPath;
|
|
|
|
// Output
|
|
RWTexture2D<float4> RWLightingTexture;
|
|
RWTexture2D<float4> RWVelocityTexture;
|
|
RWTexture2D<float> RWHoldoutTexture;
|
|
RWTexture2D<float4> RWBeerShadowMapTexture;
|
|
|
|
struct FDebugOutput
|
|
{
|
|
int PrimitiveId;
|
|
float3 LocalObjectBoundsMin;
|
|
float3 LocalObjectBoundsMax;
|
|
int Paddington;
|
|
float4x4 LocalToWorld;
|
|
float4x4 WorldToLocal;
|
|
};
|
|
|
|
RWStructuredBuffer<FDebugOutput> RWDebugOutputBuffer;
|
|
|
|
/*FDebugOutput CreateDebugOutput(
|
|
inout FMaterialPixelParameters MaterialParameters,
|
|
inout FPixelMaterialInputs PixelMaterialInputs)
|
|
{
|
|
FDebugOutput DebugOutput = (FDebugOutput)0;
|
|
|
|
FPrimitiveSceneData PrimitiveData = GetPrimitiveData(MaterialParameters);
|
|
|
|
DebugOutput.PrimitiveId = MaterialParameters.PrimitiveId;
|
|
DebugOutput.LocalObjectBoundsMin = PrimitiveData.LocalObjectBoundsMin.xyz;
|
|
DebugOutput.LocalObjectBoundsMax = PrimitiveData.LocalObjectBoundsMax.xyz;
|
|
DebugOutput.LocalToWorld = DFDemote(PrimitiveData.LocalToWorld);
|
|
DebugOutput.WorldToLocal = DFDemote(PrimitiveData.WorldToLocal);
|
|
|
|
return DebugOutput;
|
|
}*/
|
|
|
|
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;
|
|
}
|
|
|
|
int ShouldApplyHeightFog()
|
|
{
|
|
return bApplyHeightFog;
|
|
}
|
|
|
|
int ShouldApplyVolumetricFog()
|
|
{
|
|
return bApplyVolumetricFog;
|
|
}
|
|
|
|
float GetIndirectInscatteringFactor()
|
|
{
|
|
return IndirectInscatteringFactor;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
int3 AmbientOcclusionResolution;
|
|
Texture3D AmbientOcclusionTexture;
|
|
|
|
float EvalAmbientOcclusion(float3 WorldPosition)
|
|
{
|
|
float3 LocalPosition = mul(float4(WorldPosition, 1.0), GetWorldToLocal()).xyz;
|
|
float3 LocalBoundsMin = GetLocalBoundsOrigin() - GetLocalBoundsExtent();
|
|
float3 UVW = saturate((LocalPosition - LocalBoundsMin) / (2.0 * GetLocalBoundsExtent()));
|
|
float MipLevel = 0;
|
|
|
|
return Texture3DSampleLevel(
|
|
AmbientOcclusionTexture,
|
|
View.SharedTrilinearClampedSampler,
|
|
UVW,
|
|
MipLevel).r;
|
|
}
|
|
|
|
#define HV_SCALABILITY_MODE_LOW 0
|
|
#define HV_SCALABILITY_MODE_HIGH 1
|
|
#define HV_SCALABILITY_MODE_EPIC 2
|
|
#define HV_SCALABILITY_MODE_CINEMATIC 3
|
|
|
|
#ifndef HV_SCALABILITY_MODE
|
|
#define HV_SCALABILITY_MODE HV_SCALABILITY_MODE_EPIC
|
|
#endif
|
|
|
|
// Cinematic feature options
|
|
bool UseInscatteringVolume()
|
|
{
|
|
#if HV_SCALABILITY_MODE == HV_SCALABILITY_MODE_CINEMATIC
|
|
return bUseLightingCacheForInscattering;
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
#define INDIRECT_LIGHTING_MODE_OFF 0
|
|
#define INDIRECT_LIGHTING_MODE_LIGHTING_CACHE 1
|
|
#define INDIRECT_LIGHTING_MODE_SINGLE_SCATTERING 2
|
|
|
|
int GetIndirectLightingMode()
|
|
{
|
|
#if HV_SCALABILITY_MODE == HV_SCALABILITY_MODE_CINEMATIC
|
|
return IndirectLightingMode;
|
|
#elif HV_SCALABILITY_MODE == HV_SCALABILITY_MODE_EPIC
|
|
return INDIRECT_LIGHTING_MODE_LIGHTING_CACHE;
|
|
#else
|
|
return INDIRECT_LIGHTING_MODE_OFF;
|
|
#endif
|
|
}
|
|
|
|
bool ShouldWriteVelocity()
|
|
{
|
|
#if HV_SCALABILITY_MODE == HV_SCALABILITY_MODE_CINEMATIC
|
|
return bWriteVelocity;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool IsOfflineRender()
|
|
{
|
|
#if HV_SCALABILITY_MODE == HV_SCALABILITY_MODE_CINEMATIC
|
|
return bIsOfflineRender;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
#define FOG_INSCATTERING_MODE_OFF 0
|
|
#define FOG_INSCATTERING_MODE_REFERENCE 1
|
|
#define FOG_INSCATTERING_MODE_LINEAR_APPROX 2
|
|
|
|
int GetFogInscatteringMode()
|
|
{
|
|
#if HV_SCALABILITY_MODE == HV_SCALABILITY_MODE_CINEMATIC
|
|
return FogInscatteringMode;
|
|
#else
|
|
return FOG_INSCATTERING_MODE_LINEAR_APPROX;
|
|
#endif
|
|
}
|
|
|
|
#include "HeterogeneousVolumesRayMarchingUtils.ush"
|
|
|
|
uint bUseExistenceMask;
|
|
Texture3D ExistenceMaskTexture;
|
|
RWTexture3D<float3> RWLightingCacheTexture;
|
|
|
|
bool UseExistenceMask()
|
|
{
|
|
#if HV_SCALABILITY_MODE == HV_SCALABILITY_MODE_CINEMATIC
|
|
return bUseExistenceMask;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
[numthreads(THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D)]
|
|
void RenderLightingCacheWithLiveShadingCS(
|
|
uint3 GroupId : SV_GroupID,
|
|
uint3 GroupThreadId : SV_GroupThreadID,
|
|
uint3 DispatchThreadId : SV_DispatchThreadID
|
|
)
|
|
{
|
|
float3 VoxelIndex = DispatchThreadId + VoxelMin;
|
|
if (any(DispatchThreadId > VoxelMax))
|
|
{
|
|
return;
|
|
}
|
|
|
|
float3 UVW = (VoxelIndex + 0.5) / 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;
|
|
|
|
// Existence culling
|
|
float MipLevel = 0.0f;
|
|
if (UseExistenceMask())
|
|
{
|
|
float Existence = ExistenceMaskTexture.SampleLevel(View.SharedBilinearClampedSampler, UVW, MipLevel).r;
|
|
if (Existence <= 0.0)
|
|
{
|
|
#if DIM_LIGHTING_CACHE_MODE == LIGHTING_CACHE_TRANSMITTANCE
|
|
RWLightingCacheTexture[VoxelIndex] = 1.0f;
|
|
#elif DIM_LIGHTING_CACHE_MODE == LIGHTING_CACHE_INSCATTERING
|
|
RWLightingCacheTexture[VoxelIndex] = 0.0f;
|
|
#endif // DIM_LIGHTING_CACHE_MODE
|
|
return;
|
|
}
|
|
}
|
|
|
|
FVolumeSampleContext SampleContext;
|
|
if (IsOfflineRender())
|
|
{
|
|
float3 FilterWidth = 1.0;
|
|
RandomSequence RandSequence;
|
|
int LinearIndex = VoxelIndex.x * (GetLightingCacheResolution().y * GetLightingCacheResolution().z) + VoxelIndex.y * GetLightingCacheResolution().z + VoxelIndex.z;
|
|
RandomSequence_Initialize(RandSequence, LinearIndex, View.StateFrameIndex);
|
|
float Rand = RandomSequence_GenerateSample1D(RandSequence);
|
|
int StochasticFilteringMode = GetStochasticFilteringMode();
|
|
SampleContext = CreateVolumeSampleContext(LocalRayOrigin, WorldRayOrigin, FilterWidth, MipLevel, Rand, StochasticFilteringMode);
|
|
}
|
|
else
|
|
{
|
|
SampleContext = CreateVolumeSampleContext(LocalRayOrigin, WorldRayOrigin, MipLevel);
|
|
}
|
|
|
|
float3 Extinction = SampleExtinction(SampleContext);
|
|
float Epsilon = 1.0e-7;
|
|
if (all(Extinction < Epsilon))
|
|
{
|
|
#if DIM_LIGHTING_CACHE_MODE == LIGHTING_CACHE_TRANSMITTANCE
|
|
RWLightingCacheTexture[VoxelIndex] = 1.0f;
|
|
|
|
#elif DIM_LIGHTING_CACHE_MODE == LIGHTING_CACHE_INSCATTERING
|
|
RWLightingCacheTexture[VoxelIndex] = 0.0f;
|
|
#endif // DIM_LIGHTING_CACHE_MODE
|
|
return;
|
|
}
|
|
|
|
const FDeferredLightData LightData = InitDeferredLightFromUniforms(LightType, VolumetricScatteringIntensity);
|
|
#if DIM_LIGHTING_CACHE_MODE == LIGHTING_CACHE_TRANSMITTANCE
|
|
float3 L = LightData.Direction;
|
|
float3 ToLight = L * GetMaxShadowTraceDistance();
|
|
if (LightType != LIGHT_TYPE_DIRECTIONAL)
|
|
{
|
|
// Note: this function also permutes ToLight and L
|
|
float3 TranslatedWorldPosition = DFFastToTranslatedWorld(WorldRayOrigin, PrimaryView.PreViewTranslation);
|
|
GetLocalLightAttenuation(TranslatedWorldPosition, LightData, ToLight, L);
|
|
|
|
if (LightData.bRectLight)
|
|
{
|
|
FRect Rect = GetRect(ToLight, LightData);
|
|
}
|
|
else
|
|
{
|
|
FCapsuleLight Capsule = GetCapsule(ToLight, LightData);
|
|
}
|
|
}
|
|
|
|
float3 Transmittance = ComputeTransmittance(WorldRayOrigin, ToLight, MaxStepCount);
|
|
RWLightingCacheTexture[VoxelIndex] = Transmittance;
|
|
|
|
#elif DIM_LIGHTING_CACHE_MODE == LIGHTING_CACHE_INSCATTERING
|
|
float3 Inscattering = ComputeInscattering(WorldRayOrigin, LightData, LightType, MaxStepCount, CalcShadowBias(), bApplyShadowTransmittance);
|
|
RWLightingCacheTexture[VoxelIndex] += Inscattering * View.PreExposure;
|
|
#endif // DIM_LIGHTING_CACHE_MODE
|
|
}
|
|
|
|
int2 GetDownsampledResolution(int2 Resolution, int Factor)
|
|
{
|
|
return (Resolution + Factor - 1) / Factor;
|
|
}
|
|
|
|
int2 GetScaledViewRect()
|
|
{
|
|
return GetDownsampledResolution(View.ViewSizeAndInvSize.xy, DownsampleFactor);
|
|
}
|
|
|
|
#if USE_CAMERA_AVSM
|
|
|
|
void RayMarchSingleScatteringAdaptive(
|
|
inout FRayMarchingContext RayMarchingContext,
|
|
inout FAVSM_Sampler4 TransmittanceSampler,
|
|
uint StepCount,
|
|
inout float3 Radiance,
|
|
inout float3 Transmittance
|
|
)
|
|
{
|
|
FAVSMSampleData SampleData = AVSM_Sampler4_GetSampleData0(TransmittanceSampler, 0);
|
|
// Clip against first stored sample
|
|
float WorldRayTMin = max(RayMarchingContext.LocalRayTMin * RayMarchingContext.LocalToWorldScale, SampleData.X);
|
|
SampleData = AVSM_CreateSampleData(WorldRayTMin, AVSM_Sampler4_Eval(TransmittanceSampler, WorldRayTMin));
|
|
|
|
float WorldShadowBias = CalcShadowBias();
|
|
FAVSMSampleData PrevSampleData = AVSM_CreateSampleData(SampleData.X - WorldShadowBias, SampleData.Tau);
|
|
|
|
FAVSMSampleData LastSampleData = AVSM_Sampler4_Last(TransmittanceSampler);
|
|
// TODO: Sentinel values prevent efficient clipping against last stored sample
|
|
//float WorldRayTMax = min(RayMarchingContext.LocalRayTMax * RayMarchingContext.LocalToWorldScale, LastSampleData.X);
|
|
//LastSampleData = AVSM_CreateSampleData(WorldRayTMax, AVSM_Sampler4_Eval(TransmittanceSampler, WorldRayTMax));
|
|
|
|
// Clip step count to stored transmittance range
|
|
StepCount *= (SampleData.Tau - LastSampleData.Tau);
|
|
for (uint StepIndex = 0; StepIndex < StepCount; ++StepIndex)
|
|
{
|
|
float StepSize = (SampleData.X - PrevSampleData.X) / RayMarchingContext.LocalToWorldScale;
|
|
float WorldHitT = SampleData.X;
|
|
float LocalHitT = WorldHitT / RayMarchingContext.LocalToWorldScale;
|
|
|
|
float3 LocalPosition = RayMarchingContext.LocalRayOrigin + RayMarchingContext.LocalRayDirection * LocalHitT;
|
|
float3 WorldPosition = RayMarchingContext.WorldRayOrigin + RayMarchingContext.WorldRayDirection * WorldHitT;
|
|
FVolumeSampleContext SampleContext = CreateVolumeSampleContext(LocalPosition, WorldPosition, RayMarchingContext.MipLevel);
|
|
|
|
// Evaluate transmittance
|
|
Transmittance = PrevSampleData.Tau;
|
|
|
|
// Radiance from emission
|
|
if (RayMarchingContext.bApplyEmissionAndTransmittance)
|
|
{
|
|
float3 Emission = SampleEmission(SampleContext);
|
|
Radiance += Emission * StepSize * Transmittance;
|
|
}
|
|
|
|
// Radiance from in-scattering
|
|
float3 Extinction = SampleExtinction(SampleContext);
|
|
if (RayMarchingContext.bApplyDirectLighting && (any(Extinction > 0)))
|
|
{
|
|
float3 RadianceSample = 0;
|
|
|
|
// Evaluate in-scattering
|
|
float3 Albedo = SampleAlbedo(SampleContext);
|
|
if (any(Albedo > 0.0))
|
|
{
|
|
// Evaluate lighting cache
|
|
float3 LocalBoundsMin = GetLocalBoundsOrigin() - GetLocalBoundsExtent();
|
|
float3 UVW = saturate((LocalPosition - LocalBoundsMin) / (2.0 * GetLocalBoundsExtent()));
|
|
float3 Inscattering = SampleLightingCache(UVW, 0) * View.OneOverPreExposure;
|
|
|
|
// Evaluate scattering
|
|
float3 ScatteringCoefficient = Albedo * Extinction;
|
|
float IsotropicPhase = 1.0 / (4.0 * PI);
|
|
RadianceSample = Inscattering * ScatteringCoefficient * IsotropicPhase * Transmittance * StepSize;
|
|
}
|
|
|
|
Radiance += RadianceSample;
|
|
}
|
|
|
|
float Epsilon = 1.0e-7;
|
|
if (all(Transmittance < Epsilon))
|
|
{
|
|
Transmittance = 0.0;
|
|
break;
|
|
}
|
|
|
|
PrevSampleData = SampleData;
|
|
float DeltaTau = rcp(StepCount);
|
|
SampleData = AVSM_Sampler4_EvalInverse(TransmittanceSampler, SampleData.Tau - DeltaTau);
|
|
|
|
// Guard against steps finer than shadowing bias
|
|
if (SampleData.X - PrevSampleData.X < WorldShadowBias)
|
|
{
|
|
SampleData.X = PrevSampleData.X + WorldShadowBias;
|
|
SampleData.Tau = AVSM_Sampler4_Eval(TransmittanceSampler, SampleData.X);
|
|
}
|
|
|
|
// Guard against adaptively stepping beyond t-max
|
|
float WorldRayTMax = RayMarchingContext.LocalRayTMax * RayMarchingContext.LocalToWorldScale;
|
|
if (SampleData.X > WorldRayTMax)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // USE_CAMERA_AVSM
|
|
|
|
struct FBeerShadowMapData
|
|
{
|
|
float T[2];
|
|
float Tau[2];
|
|
};
|
|
|
|
FBeerShadowMapData InitializeBeerShadowMapData(float Transmittance)
|
|
{
|
|
FBeerShadowMapData Data;
|
|
Data.T[0] = GetMaxTraceDistance();
|
|
Data.T[1] = 0.0;
|
|
Data.Tau[0] = Transmittance;
|
|
Data.Tau[1] = Transmittance;
|
|
return Data;
|
|
}
|
|
|
|
FBeerShadowMapData UnpackBeerShadowMapData(float4 PackedData)
|
|
{
|
|
FBeerShadowMapData Data;
|
|
Data.T[0] = PackedData.r;
|
|
Data.T[1] = PackedData.g;
|
|
Data.Tau[0] = PackedData.b;
|
|
Data.Tau[1] = PackedData.a;
|
|
return Data;
|
|
}
|
|
|
|
float4 PackBeerShadowMapData(FBeerShadowMapData Data)
|
|
{
|
|
float4 PackedData;
|
|
PackedData.r = Data.T[0];
|
|
PackedData.g = Data.T[1];
|
|
PackedData.b = Data.Tau[0];
|
|
PackedData.a = Data.Tau[1];
|
|
return PackedData;
|
|
}
|
|
|
|
#define BEER_SHADOW_MAP_COMBINE_METHOD_FIRST 0
|
|
#define BEER_SHADOW_MAP_COMBINE_METHOD_EXPAND 1
|
|
|
|
FBeerShadowMapData ExpandBeerShadowMapData(FBeerShadowMapData Data0, FBeerShadowMapData Data1)
|
|
{
|
|
FBeerShadowMapData Result;
|
|
Result.T[0] = min(Data0.T[0], Data1.T[0]);
|
|
Result.T[1] = max(Data0.T[1], Data1.T[1]);
|
|
Result.Tau[0] = Data0.Tau[0];
|
|
Result.Tau[1] = Data1.Tau[1];
|
|
return Result;
|
|
}
|
|
|
|
FBeerShadowMapData FirstBeerShadowMapData(FBeerShadowMapData Data0, FBeerShadowMapData Data1)
|
|
{
|
|
bool bIsValid0 = Data0.T[1] > Data0.T[0];
|
|
bool bIsValid1 = Data1.T[1] > Data1.T[0];
|
|
|
|
if (bIsValid0 && bIsValid1)
|
|
{
|
|
if (Data0.T[0] < Data1.T[0])
|
|
{
|
|
return Data0;
|
|
}
|
|
else
|
|
{
|
|
return Data1;
|
|
}
|
|
}
|
|
else if (bIsValid0)
|
|
{
|
|
return Data0;
|
|
}
|
|
// else
|
|
return Data1;
|
|
}
|
|
|
|
FBeerShadowMapData CombineBeerShadowMapData(FBeerShadowMapData Data0, FBeerShadowMapData Data1, int CombineMethod)
|
|
{
|
|
FBeerShadowMapData Result;
|
|
|
|
if (CombineMethod == BEER_SHADOW_MAP_COMBINE_METHOD_FIRST)
|
|
{
|
|
Result = FirstBeerShadowMapData(Data0, Data1);
|
|
}
|
|
else // if (CombineMethod == BEER_SHADOW_MAP_COMBINE_METHOD_EXPAND)
|
|
{
|
|
Result = ExpandBeerShadowMapData(Data0, Data1);
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
void RenderSingleScatteringWithLiveShading(
|
|
uint2 DispatchThreadId
|
|
)
|
|
{
|
|
FDebugOutput DebugOutput = (FDebugOutput)0;
|
|
|
|
// Create screen ray
|
|
if (any(DispatchThreadId.xy >= GetScaledViewRect()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
uint LinearIndex = DispatchThreadId.y * GroupCount.x * THREADGROUP_SIZE_2D + DispatchThreadId.x;
|
|
uint2 ViewPixelCoord = DispatchThreadId.xy * DownsampleFactor + View.ViewRectMin.xy;
|
|
uint2 PixelCoord = DispatchThreadId.xy + View.ViewRectMin.xy / DownsampleFactor;
|
|
|
|
//float Epsilon = 1.0e-5;
|
|
//if (RWLightingTexture[PixelCoord].a < Epsilon)
|
|
//if (RWLightingTexture[PixelCoord].a <= 0.0)
|
|
//{
|
|
// return;
|
|
//}
|
|
float3 Radiance = 0.0;
|
|
float PrevTransmittance = RWLightingTexture[PixelCoord].a;
|
|
float PrevOpacity = Luminance(1.0 - PrevTransmittance);
|
|
|
|
FBeerShadowMapData BeerShadowMapData = InitializeBeerShadowMapData(PrevTransmittance);
|
|
|
|
// Extract depth
|
|
float DeviceZ = SceneDepthTexture.Load(int3(ViewPixelCoord, 0)).r;
|
|
#if HAS_INVERTED_Z_BUFFER
|
|
DeviceZ = max(0.000000000001, DeviceZ);
|
|
#endif // HAS_INVERTED_Z_BUFFER
|
|
|
|
// Clip trace distance
|
|
float SceneDepth = min(ConvertFromDeviceZ(DeviceZ), GetMaxTraceDistance());
|
|
DeviceZ = ConvertToDeviceZ(SceneDepth);
|
|
|
|
float Jitter = bJitter ? BlueNoiseScalar(ViewPixelCoord, View.StateFrameIndexMod8) : 0.0;
|
|
float2 ScreenJitter = IsOfflineRender() ? BlueNoiseVec2(ViewPixelCoord, View.StateFrameIndex) : View.TemporalAAJitter.xy;
|
|
|
|
// Intersect ray with bounding volume
|
|
// TODO: LWC integration..
|
|
float3 WorldRayOrigin = DFHackToFloat(DFFastSubtract(GetTranslatedWorldCameraPosFromView(ViewPixelCoord + ScreenJitter), PrimaryView.PreViewTranslation));
|
|
float3 WorldRayEnd = DFHackToFloat(SvPositionToWorld(float4(ViewPixelCoord + ScreenJitter, DeviceZ, 1)));
|
|
float3 WorldRayDirection = WorldRayEnd - WorldRayOrigin;
|
|
float WorldRayLength = length(WorldRayDirection);
|
|
WorldRayDirection /= WorldRayLength;
|
|
|
|
// TODO: Analytical derivate evaluation is not currently supported under orthographic projection
|
|
float3 WorldRayEnd_DDX = DFHackToFloat(SvPositionToWorld(float4(ViewPixelCoord + ScreenJitter + float2(1.0, 0.0), DeviceZ, 1)));
|
|
float3 WorldRayDirection_DDX = normalize(WorldRayEnd_DDX - WorldRayOrigin);
|
|
float3 WorldRayEnd_DDY = DFHackToFloat(SvPositionToWorld(float4(ViewPixelCoord + ScreenJitter + float2(0.0, 1.0), DeviceZ, 1)));
|
|
float3 WorldRayDirection_DDY = normalize(WorldRayEnd_DDY - WorldRayOrigin);
|
|
|
|
float3 LocalRayOrigin = mul(float4(WorldRayOrigin, 1.0), WorldToLocal).xyz;
|
|
float3 LocalRayEnd = mul(float4(WorldRayEnd, 1.0), WorldToLocal).xyz;
|
|
float3 LocalRayDirection = LocalRayEnd - LocalRayOrigin;
|
|
float LocalRayLength = length(LocalRayDirection);
|
|
LocalRayDirection /= LocalRayLength;
|
|
|
|
float3 LocalRayEnd_DDX = mul(float4(WorldRayEnd_DDX, 1.0), WorldToLocal).xyz;
|
|
float3 LocalRayDirection_DDX = normalize(LocalRayEnd_DDX - LocalRayOrigin);
|
|
float3 LocalRayEnd_DDY = mul(float4(WorldRayEnd_DDY, 1.0), WorldToLocal).xyz;
|
|
float3 LocalRayDirection_DDY = normalize(LocalRayEnd_DDY - LocalRayOrigin);
|
|
|
|
float3 LocalBoundsMin = LocalBoundsOrigin - LocalBoundsExtent;
|
|
float3 LocalBoundsMax = LocalBoundsOrigin + LocalBoundsExtent;
|
|
|
|
// Intersect bounding volume
|
|
float2 HitT = IntersectAABB(LocalRayOrigin, LocalRayDirection, 0.0, LocalRayLength, LocalBoundsMin, LocalBoundsMax);
|
|
float HitSpan = HitT.y - HitT.x;
|
|
if (HitSpan > 0.0)
|
|
{
|
|
// March
|
|
FRayMarchingContext RayMarchingContext = CreateRayMarchingContext(
|
|
// Local-space
|
|
LocalRayOrigin,
|
|
LocalRayDirection,
|
|
LocalRayDirection_DDX,
|
|
LocalRayDirection_DDY,
|
|
HitT.x,
|
|
HitT.y,
|
|
// World-space
|
|
WorldRayOrigin,
|
|
WorldRayDirection,
|
|
WorldRayDirection_DDX,
|
|
WorldRayDirection_DDY,
|
|
// Ray-step attributes
|
|
Jitter,
|
|
CalcStepSize(LocalRayDirection),
|
|
MaxStepCount,
|
|
bApplyEmissionAndTransmittance,
|
|
bApplyDirectLighting,
|
|
bApplyShadowTransmittance
|
|
);
|
|
|
|
RandomSequence RandSequence;
|
|
RandomSequence_Initialize(RandSequence, LinearIndex, View.StateFrameIndex);
|
|
RayMarchingContext.RandSequence = RandSequence;
|
|
|
|
const FDeferredLightData LightData = InitDeferredLightFromUniforms(LightType, VolumetricScatteringIntensity);
|
|
uint StepCount = CalcStepCount(RayMarchingContext);
|
|
|
|
float ScatterHitT = HitT.y;
|
|
|
|
float3 Transmittance = float3(PrevTransmittance, PrevTransmittance, PrevTransmittance);
|
|
|
|
#if USE_CAMERA_AVSM
|
|
FAdaptiveVolumetricShadowMap GlobalTransmittanceMap = GetAdaptiveVolumetricShadowMapForCamera();
|
|
if (GlobalTransmittanceMap.bIsEmpty)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Define AVSM Sampler
|
|
float MidpointT = (HitT.x + HitT.y) * 0.5;
|
|
float3 WorldPosition = RayMarchingContext.WorldRayOrigin + RayMarchingContext.WorldRayDirection * MidpointT;
|
|
float3 TranslatedWorldPosition = DFHackToFloat(DFFastAdd(WorldPosition, PrimaryView.PreViewTranslation));
|
|
|
|
int Face = 0;
|
|
float2 Pixel = 0;
|
|
float DepthOffset = 0;
|
|
if (!AVSM_PixelAndDepth(GlobalTransmittanceMap, Face, TranslatedWorldPosition, Pixel, DepthOffset))
|
|
{
|
|
return;
|
|
}
|
|
|
|
FAVSM_Sampler4 TransmittanceSampler = AVSM_Sampler4_Create(GlobalTransmittanceMap, Face, Pixel, DepthOffset);
|
|
|
|
#if ADAPTIVE_MARCH
|
|
StepCount = View.GeneralPurposeTweak2;
|
|
RayMarchSingleScatteringAdaptive(
|
|
RayMarchingContext,
|
|
TransmittanceSampler,
|
|
StepCount,
|
|
Radiance,
|
|
Transmittance
|
|
);
|
|
#elif REFERENCE_FAST_PATH
|
|
RayMarchSingleScatteringReferenceFastPath(
|
|
RayMarchingContext,
|
|
TransmittanceSampler,
|
|
StepCount,
|
|
Radiance,
|
|
Transmittance
|
|
);
|
|
#else
|
|
RayMarchSingleScattering(
|
|
RayMarchingContext,
|
|
TransmittanceSampler,
|
|
LightData,
|
|
LightType,
|
|
StepCount,
|
|
ScatterHitT,
|
|
Radiance,
|
|
Transmittance
|
|
);
|
|
#endif
|
|
// Replace transmittance at WorldRayTMax with global transmittance at MaxTraceDistance
|
|
//Transmittance = AVSM_Sampler4_Eval_NoInterpolation(TransmittanceSampler, WorldRayLength);
|
|
Transmittance = AVSM_Sampler4_Eval(TransmittanceSampler, WorldRayLength);
|
|
|
|
#else
|
|
// #if REFERENCE_FAST_PATH
|
|
// RayMarchSingleScatteringReferenceFastPath(
|
|
// RayMarchingContext,
|
|
// StepCount,
|
|
// Radiance,
|
|
// Transmittance
|
|
// );
|
|
// #else
|
|
RayMarchSingleScattering(
|
|
RayMarchingContext,
|
|
LightData,
|
|
LightType,
|
|
StepCount,
|
|
ScatterHitT,
|
|
Radiance,
|
|
Transmittance
|
|
);
|
|
// #endif
|
|
#endif
|
|
|
|
// Radiance
|
|
#if SUPPORT_PRIMITIVE_ALPHA_HOLDOUT
|
|
if (bHoldout && View.bPrimitiveAlphaHoldoutEnabled)
|
|
{
|
|
Radiance *= 0;
|
|
}
|
|
#endif
|
|
RWLightingTexture[PixelCoord].rgb += Radiance * View.PreExposure;
|
|
|
|
if (bApplyEmissionAndTransmittance)
|
|
{
|
|
float Opacity = Luminance(1.0 - Transmittance);
|
|
RWLightingTexture[PixelCoord].a = Luminance(Transmittance);
|
|
|
|
#if SUPPORT_PRIMITIVE_ALPHA_HOLDOUT
|
|
if (bHoldout && View.bPrimitiveAlphaHoldoutEnabled)
|
|
{
|
|
RWHoldoutTexture[PixelCoord] += Luminance(PrevTransmittance) - Luminance(Transmittance);
|
|
}
|
|
#endif
|
|
|
|
if (ShouldWriteVelocity())
|
|
{
|
|
bool bWriteVelocityLocal =
|
|
(ScatterHitT < HitT.y) &&
|
|
(PrevOpacity < 0.5) &&
|
|
(Opacity > 0.5);
|
|
|
|
if (bWriteVelocityLocal)
|
|
{
|
|
float WorldScatterHitT = ScatterHitT * RayMarchingContext.LocalToWorldScale;
|
|
float3 Velocity = Calculate3DVelocityAtWorldPos(WorldRayOrigin, WorldRayDirection, WorldScatterHitT);
|
|
RWVelocityTexture[PixelCoord] = EncodeVelocityToTexture(Velocity);
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: Conditionalize around CameraAVSM
|
|
if (bCreateBeerShadowMap)
|
|
{
|
|
BeerShadowMapData.T[0] = RayMarchingContext.LocalScatterT0 * RayMarchingContext.LocalToWorldScale;
|
|
BeerShadowMapData.T[1] = RayMarchingContext.LocalScatterT1 * RayMarchingContext.LocalToWorldScale;
|
|
BeerShadowMapData.Tau[0] = Luminance(RayMarchingContext.Transmittance0);
|
|
BeerShadowMapData.Tau[1] = Luminance(Transmittance);
|
|
}
|
|
|
|
// Debug output
|
|
//RWDebugOutputBuffer[LinearIndex] = DebugOutput;
|
|
}
|
|
|
|
if (bCreateBeerShadowMap)
|
|
{
|
|
RWBeerShadowMapTexture[PixelCoord] = PackBeerShadowMapData(
|
|
CombineBeerShadowMapData(
|
|
UnpackBeerShadowMapData(RWBeerShadowMapTexture[PixelCoord]),
|
|
BeerShadowMapData,
|
|
BEER_SHADOW_MAP_COMBINE_METHOD_EXPAND)
|
|
// BEER_SHADOW_MAP_COMBINE_METHOD_FIRST)
|
|
);
|
|
}
|
|
}
|
|
|
|
[numthreads(THREADGROUP_SIZE_2D, THREADGROUP_SIZE_2D, 1)]
|
|
void RenderSingleScatteringWithLiveShadingCS(
|
|
uint2 GroupThreadId : SV_GroupThreadID,
|
|
uint2 DispatchThreadId : SV_DispatchThreadID
|
|
)
|
|
{
|
|
RenderSingleScatteringWithLiveShading(DispatchThreadId);
|
|
}
|
|
|
|
struct FScreenTile
|
|
{
|
|
uint Id;
|
|
};
|
|
|
|
StructuredBuffer<FScreenTile> ScreenTileBuffer;
|
|
|
|
[numthreads(THREADGROUP_SIZE_2D, THREADGROUP_SIZE_2D, 1)]
|
|
void RenderSingleScatteringWithLiveShadingIndirectCS(
|
|
uint GroupId : SV_GroupID,
|
|
uint2 GroupThreadId : SV_GroupThreadID
|
|
)
|
|
{
|
|
FScreenTile ScreenTile = ScreenTileBuffer[GroupId];
|
|
uint2 GroupId_2D = uint2(ScreenTile.Id % GroupCount.x, ScreenTile.Id / GroupCount.x);
|
|
uint2 DispatchThreadId = GroupId_2D * THREADGROUP_SIZE_2D + GroupThreadId;
|
|
|
|
RenderSingleScatteringWithLiveShading(DispatchThreadId);
|
|
}
|
|
|