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

688 lines
25 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
// Change this to force shader recompilation
#pragma message("UESHADERMETADATA_VERSION 498251B5-BC1F-4250-A0A2-7BB218050A9A")
#include "HeterogeneousVolumesRayMarchingTypes.ush"
#include "HeterogeneousVolumesTracingUtils.ush"
#include "HeterogeneousVolumesTransmittanceVolumeUtils.ush"
#include "../ForwardShadowingCommon.ush"
#include "../VolumeLightingCommon.ush"
#include "../LightData.ush"
#include "../DeferredLightingCommon.ush"
#include "../SHCommon.ush"
#define FrontLayerTranslucencyReflectionsStruct LumenGIVolumeStruct
#define RadianceCacheInterpolation LumenGIVolumeStruct
#include "../Lumen/LumenTranslucencyVolumeShared.ush"
#ifndef CUSTOM_TRANSMITTANCE_FUNCTION
#define CUSTOM_TRANSMITTANCE_FUNCTION 0
#endif // CUSTOM_TRANSMITTANCE_FUNCTION
#ifndef HARDWARE_RAY_TRACING
#define HARDWARE_RAY_TRACING 0
#endif // HARDWARE_RAY_TRACING
#ifndef HARD_SURFACE_SHADOWING
#define HARD_SURFACE_SHADOWING 1
#endif // HARD_SURFACE_SHADOWING
#ifndef DIM_USE_ADAPTIVE_VOLUMETRIC_SHADOW_MAP
#define DIM_USE_ADAPTIVE_VOLUMETRIC_SHADOW_MAP 0
#endif // DIM_USE_ADAPTIVE_VOLUMETRIC_SHADOW_MAP
#if DIM_USE_ADAPTIVE_VOLUMETRIC_SHADOW_MAP
#include "HeterogeneousVolumesAdaptiveVolumetricShadowMapSampling.ush"
#include "HeterogeneousVolumesAdaptiveVolumetricShadowMapSampler.ush"
#endif // DIM_USE_ADAPTIVE_VOLUMETRIC_SHADOW_MAP
#include "../HeightFogCommon.ush"
float3 GetWorldVoxelSizeOfLightingCache()
{
float3 WorldVoxelSize = 0;
float3 LightingCacheResolutionAsFloat3 = GetLightingCacheResolution();
if (all(LightingCacheResolutionAsFloat3 > 0))
{
float3 VolumeBounds = 2.0f * GetLocalBoundsExtent();
float3 VoxelSize = VolumeBounds * rcp(LightingCacheResolutionAsFloat3);
WorldVoxelSize = mul(float4(VoxelSize, 0), GetLocalToWorld()).xyz;
}
return WorldVoxelSize;
}
float CalcShadowBias()
{
float WorldShadowBias = 0.0f;
float3 LightingCacheResolutionAsFloat3 = GetLightingCacheResolution();
if (all(LightingCacheResolutionAsFloat3 > 0))
{
float3 VolumeBounds = 2.0f * GetLocalBoundsExtent();
float3 VoxelSize = VolumeBounds * rcp(LightingCacheResolutionAsFloat3);
float3 WorldVoxelSize = mul(float4(VoxelSize, 0), GetLocalToWorld()).xyz;
float VoxelDiagonal = length(WorldVoxelSize);
WorldShadowBias = VoxelDiagonal * GetLightingCacheVoxelBias();
}
return WorldShadowBias;
}
float ComputeHardSurfaceShadowFactor(
float3 TranslatedWorldPosition,
FDeferredLightData LightData,
uint LightType
)
{
float HardSurfaceShadowFactor = 1.0;
#if HARD_SURFACE_SHADOWING
// Evaluate hard-surface shadow term
if (LightType == LIGHT_TYPE_DIRECTIONAL)
{
float SceneDepth = dot(TranslatedWorldPosition - PrimaryView.TranslatedWorldCameraOrigin, View.ViewForward);
bool bShadowingFromValidUVArea = false;
float ShadowFactor = ComputeDirectionalLightDynamicShadowing(TranslatedWorldPosition, SceneDepth, bShadowingFromValidUVArea);
if (bShadowingFromValidUVArea)
{
HardSurfaceShadowFactor *= ShadowFactor;
}
}
else // Local lights
{
bool bShadowingFromValidUVArea = false;
float ShadowFactor = ComputeVolumeShadowing(TranslatedWorldPosition, LightData.bRadialLight && !LightData.bSpotLight, LightData.bSpotLight, bShadowingFromValidUVArea);
if (bShadowingFromValidUVArea)
{
HardSurfaceShadowFactor *= ShadowFactor;
}
}
#if VIRTUAL_SHADOW_MAP
if (VirtualShadowMapId != INDEX_NONE)
{
FVirtualShadowMapSampleResult VirtualShadowMapSample = SampleVirtualShadowMapTranslatedWorld(VirtualShadowMapId, TranslatedWorldPosition);
HardSurfaceShadowFactor *= VirtualShadowMapSample.ShadowFactor;
}
#endif // VIRTUALSHADOW_MAP
#endif // HARD_SURFACE_SHADOWING
return HardSurfaceShadowFactor;
}
void RayMarchTransmittance(
inout FRayMarchingContext RayMarchingContext,
uint StepCount,
inout float3 Transmittance
)
{
for (uint StepIndex = 0; StepIndex < StepCount; ++StepIndex)
{
float LocalHitT = RayMarchingContext.LocalRayTMin + RayMarchingContext.StepSize * (RayMarchingContext.Jitter + StepIndex);
float3 LocalPosition = RayMarchingContext.LocalRayOrigin + RayMarchingContext.LocalRayDirection * LocalHitT;
float3 WorldPosition = RayMarchingContext.WorldRayOrigin + RayMarchingContext.WorldRayDirection * LocalHitT * RayMarchingContext.LocalToWorldScale;
#if USE_ANALYTIC_DERIVATIVES
float3 WorldPosition_DDX = RayMarchingContext.WorldRayOrigin + RayMarchingContext.WorldRayDirection_DDX * LocalHitT * RayMarchingContext.LocalToWorldScale;
float3 WorldPosition_DDY = RayMarchingContext.WorldRayOrigin + RayMarchingContext.WorldRayDirection_DDY * LocalHitT * RayMarchingContext.LocalToWorldScale;
WorldPosition_DDX -= WorldPosition;
WorldPosition_DDY -= WorldPosition;
#else
float3 WorldPosition_DDX = 0.0;
float3 WorldPosition_DDY = 0.0;
#endif // USE_ANALYTIC_DERIVATIVES
FVolumeSampleContext SampleContext = CreateVolumeSampleContext(LocalPosition, WorldPosition, WorldPosition_DDX, WorldPosition_DDY, RayMarchingContext.MipLevel);
float3 Extinction = SampleExtinction(SampleContext);
Transmittance *= exp(-Extinction * RayMarchingContext.StepSize);
float Epsilon = 1.0e-7;
if (all(Transmittance < Epsilon))
{
Transmittance = 0.0;
break;
}
}
}
float3 ComputeTransmittance(
inout FRayMarchingContext RayMarchingContext
)
{
#if CUSTOM_TRANSMITTANCE_FUNCTION
float WorldRayTMin = RayMarchingContext.LocalRayTMin * RayMarchingContext.LocalToWorldScale;
float WorldRayTMax = RayMarchingContext.LocalRayTMax * RayMarchingContext.LocalToWorldScale;
return TransmittanceFunction(RayMarchingContext.WorldRayOrigin, RayMarchingContext.WorldRayDirection, WorldRayTMin, WorldRayTMax);
#elif HARDWARE_RAY_TRACING
// TODO: Incorporpate ray jitter
return ComputeTransmittanceHardwareRayTracing(RayMarchingContext.WorldRayOrigin, RayMarchingContext.WorldRayDirection, RayMarchingContext.LocalRayTMin, RayMarchingContext.LocalRayTMax);
#else // HARDWARE_RAY_TRACING
float3 Transmittance = 1.0;
float3 LocalBoundsMin = GetLocalBoundsOrigin() - GetLocalBoundsExtent();
float3 LocalBoundsMax = GetLocalBoundsOrigin() + GetLocalBoundsExtent();
float2 HitT = IntersectAABB(RayMarchingContext.LocalRayOrigin, RayMarchingContext.LocalRayDirection, RayMarchingContext.LocalRayTMin, RayMarchingContext.LocalRayTMax,
LocalBoundsMin, LocalBoundsMax);
float HitSpan = HitT.y - HitT.x;
if (HitSpan > 0.0)
{
RayMarchingContext.LocalRayTMin = HitT.x;
RayMarchingContext.LocalRayTMax = HitT.y;
uint StepCount = CalcStepCount(RayMarchingContext);
RayMarchTransmittance(RayMarchingContext, StepCount, Transmittance);
}
return Transmittance;
#endif // HARDWARE_RAY_TRACING
}
float3 ComputeTransmittance(
float3 WorldRayOrigin,
float3 ToLight,
uint MaxStepCount
)
{
float3 Transmittance = 1.0;
#if DIM_USE_TRANSMITTANCE_VOLUME
float3 LocalRayOrigin = mul(float4(WorldRayOrigin, 1.0), GetWorldToLocal()).xyz;
float3 LocalBoundsMin = GetLocalBoundsOrigin() - GetLocalBoundsExtent();
float3 TransmittanceUVW = saturate((LocalRayOrigin - LocalBoundsMin) / (2.0 * GetLocalBoundsExtent()));
float MipLevel = 0;
Transmittance = SampleLightingCache(TransmittanceUVW, MipLevel);
#else
#if DIM_USE_ADAPTIVE_VOLUMETRIC_SHADOW_MAP
if (!AVSM.bIsEmpty)
{
float3 TranslatedWorldPosition = DFFastAddDemote(WorldRayOrigin, PrimaryView.PreViewTranslation);
float3 LightTranslatedWorldPosition = TranslatedWorldPosition + ToLight;
Transmittance = AVSM_SampleTransmittance(TranslatedWorldPosition, LightTranslatedWorldPosition);
}
else
#endif
{
float3 WorldRayEnd = WorldRayOrigin + ToLight;
float3 WorldRayDirection = normalize(ToLight);
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
);
Transmittance = ComputeTransmittance(ShadowRayMarchingContext);
}
#endif
return Transmittance;
}
float3 ComputeIndirectInscattering(
float3 WorldPosition,
float3 WorldRayDirection
)
{
FTwoBandSHVectorRGB TranslucencyGISH = GetTranslucencyGIVolumeLighting(DFPromote(WorldPosition), PrimaryView.WorldToClip, true);
FTwoBandSHVector RotatedHGZonalHarmonic;
float3 CameraVector = -WorldRayDirection;
float PhaseG = 0.0;
RotatedHGZonalHarmonic.V = float4(1.0f, CameraVector.y, CameraVector.z, CameraVector.x) * float4(1.0f, PhaseG, PhaseG, PhaseG);
float3 IndirectInscattering = max(DotSH(TranslucencyGISH, RotatedHGZonalHarmonic), 0) / PI;
IndirectInscattering *= GetIndirectInscatteringFactor();
// Note: Phase is accounted for in spherical hamonic calculation
return IndirectInscattering * EvalAmbientOcclusion(WorldPosition);
}
float3 ComputeInscattering(
float3 WorldPosition,
FDeferredLightData LightData,
uint LightType,
uint MaxStepCount,
float WorldShadowBias,
bool bApplyShadowTransmittance
)
{
float3 L = LightData.Direction;
//float3 ToLight = L * RayMarchingContext.MaxShadowTraceDistance;
float3 ToLight = L * 10000;
float LightAttenuation = 1.0;
float3 TranslatedWorldPosition = DFFastToTranslatedWorld(WorldPosition, PrimaryView.PreViewTranslation);
if (LightType != LIGHT_TYPE_DIRECTIONAL)
{
LightAttenuation = GetLocalLightAttenuation(TranslatedWorldPosition, LightData, ToLight, L);
if (LightData.bRectLight)
{
FRect Rect = GetRect(ToLight, LightData);
LightAttenuation *= IntegrateLight(Rect);
}
else
{
FCapsuleLight Capsule = GetCapsule(ToLight, LightData);
Capsule.DistBiasSqr = 0;
LightAttenuation *= IntegrateLight(Capsule, LightData.bInverseSquared);
}
}
float HardSurfaceShadowFactor = ComputeHardSurfaceShadowFactor(TranslatedWorldPosition, LightData, LightType);
float3 Inscattering = LightData.Color * LightAttenuation * HardSurfaceShadowFactor;
if (any(Inscattering > 0.0) && bApplyShadowTransmittance)
{
float3 BiasedWorldPosition = WorldPosition + L * WorldShadowBias;
Inscattering *= ComputeTransmittance(BiasedWorldPosition, ToLight, MaxStepCount);
}
if (GetIndirectLightingMode() == INDIRECT_LIGHTING_MODE_LIGHTING_CACHE)
{
float3 WorldRayDirection = normalize(WorldPosition - PrimaryView.TranslatedWorldCameraOrigin);
float3 IndirectInscattering = ComputeIndirectInscattering(WorldPosition, WorldRayDirection);
// Phase is already accounted for, so add a correction factor
float IsotropicPhaseRcp = 4.0 * PI;
Inscattering += IndirectInscattering * IsotropicPhaseRcp;
}
return Inscattering;
}
void RayMarchEmissionAbsorption(
inout FRayMarchingContext RayMarchingContext,
uint StepCount,
inout float3 Radiance,
inout float3 Transmittance
)
{
for (uint StepIndex = 0; StepIndex < StepCount; ++StepIndex)
{
float LocalHitT = RayMarchingContext.LocalRayTMin + RayMarchingContext.StepSize * (RayMarchingContext.Jitter + StepIndex);
float3 LocalPosition = RayMarchingContext.LocalRayOrigin + RayMarchingContext.LocalRayDirection * LocalHitT;
float3 WorldPosition = RayMarchingContext.WorldRayOrigin + RayMarchingContext.WorldRayDirection * LocalHitT * RayMarchingContext.LocalToWorldScale;
#if USE_ANALYTIC_DERIVATIVES
float3 WorldPosition_DDX = RayMarchingContext.WorldRayOrigin + RayMarchingContext.WorldRayDirection_DDX * LocalHitT * RayMarchingContext.LocalToWorldScale;
float3 WorldPosition_DDY = RayMarchingContext.WorldRayOrigin + RayMarchingContext.WorldRayDirection_DDY * LocalHitT * RayMarchingContext.LocalToWorldScale;
WorldPosition_DDX -= WorldPosition;
WorldPosition_DDY -= WorldPosition;
#else
float3 WorldPosition_DDX = 0.0;
float3 WorldPosition_DDY = 0.0;
#endif // USE_ANALYTIC_DERIVATIVES
FVolumeSampleContext SampleContext = CreateVolumeSampleContext(LocalPosition, WorldPosition, WorldPosition_DDX, WorldPosition_DDY, RayMarchingContext.MipLevel);
float3 Extinction = SampleExtinction(SampleContext);
float3 Emission = SampleEmission(SampleContext);
Radiance += Emission * RayMarchingContext.StepSize * Transmittance;
// Accumulate transmittance for the next evaluation
Transmittance *= exp(-Extinction * RayMarchingContext.StepSize);
}
}
void RayMarchSingleScattering(
inout FRayMarchingContext RayMarchingContext,
#if USE_CAMERA_AVSM
inout FAVSM_Sampler4 TransmittanceSampler,
#endif
FDeferredLightData LightData,
uint LightType,
uint StepCount,
inout float LocalScatterTMin,
inout float3 Radiance,
inout float3 Transmittance
)
{
LocalScatterTMin = RayMarchingContext.LocalRayTMax;
float3 FogRadiance = 0;
float3 PrevTransmittance = Transmittance;
float4 PrevCombinedFog = 0;
float WorldFogHitT[] = {
RayMarchingContext.LocalRayTMin * RayMarchingContext.LocalToWorldScale,
RayMarchingContext.LocalRayTMax * RayMarchingContext.LocalToWorldScale
};
float4 FogHitRadiance[] = {
float4(0.0, 0.0, 0.0, 0.0),
float4(0.0, 0.0, 0.0, 0.0)
};
if (GetFogInscatteringMode() == FOG_INSCATTERING_MODE_LINEAR_APPROX)
{
UNROLL
for (int i = 0; i < 2; ++i)
{
float3 CameraRelative_WorldPosition = RayMarchingContext.WorldRayDirection * WorldFogHitT[i];
float4 HeightFog = float4(0, 0, 0, 1);
if (ShouldApplyHeightFog())
{
HeightFog = CalculateHeightFog(CameraRelative_WorldPosition);
}
float4 CombinedFog = HeightFog;
if (ShouldApplyVolumetricFog() && FogStruct.ApplyVolumetricFog > 0)
{
float3 WorldPosition = CameraRelative_WorldPosition + RayMarchingContext.WorldRayOrigin;
const uint EyeIndex = 0;
float3 VolumeUV = ComputeVolumeUV_DEPRECATED(WorldPosition, DFHackToFloat(PrimaryView.WorldToClip));
CombinedFog = CombineVolumetricFog(HeightFog, VolumeUV, EyeIndex, WorldFogHitT[i]);
}
FogHitRadiance[i] = CombinedFog;
}
}
float WorldShadowBias = CalcShadowBias();
//float WorldShadowBias = View.GeneralPurposeTweak;
float StepSize = RayMarchingContext.StepSize;
float JitterSize = StepSize * RayMarchingContext.Jitter;
// Reuse jitter as random sample
float RandomSample = RayMarchingContext.Jitter;
bool bFirstScatterEvent = true;
for (uint StepIndex = 0; StepIndex < StepCount; ++StepIndex)
{
// Correct the final step size
float LocalHitT = RayMarchingContext.LocalRayTMin + RayMarchingContext.StepSize * (RayMarchingContext.Jitter + StepIndex);
if (StepIndex == StepCount - 1)
{
float UnjitteredLocalHitT = LocalHitT - JitterSize;
StepSize = RayMarchingContext.LocalRayTMax - UnjitteredLocalHitT;
}
float WorldHitT = LocalHitT * RayMarchingContext.LocalToWorldScale;
float3 LocalPosition = RayMarchingContext.LocalRayOrigin + RayMarchingContext.LocalRayDirection * LocalHitT;
float3 WorldPosition = RayMarchingContext.WorldRayOrigin + RayMarchingContext.WorldRayDirection * WorldHitT;
FVolumeSampleContext SampleContext;
if (IsOfflineRender())
{
float3 FilterWidth = 1.0;
float Rand = RandomSequence_GenerateSample1D(RayMarchingContext.RandSequence);
int StochasticFilteringMode = GetStochasticFilteringMode();
SampleContext = CreateVolumeSampleContext(LocalPosition, WorldPosition, FilterWidth, RayMarchingContext.MipLevel, Rand, StochasticFilteringMode);
}
else
{
#if USE_ANALYTIC_DERIVATIVES
float3 WorldPosition_DDX = RayMarchingContext.WorldRayOrigin + RayMarchingContext.WorldRayDirection_DDX * WorldHitT;
float3 WorldPosition_DDY = RayMarchingContext.WorldRayOrigin + RayMarchingContext.WorldRayDirection_DDY * WorldHitT;
WorldPosition_DDX -= WorldPosition;
WorldPosition_DDY -= WorldPosition;
#else
float3 WorldPosition_DDX = 0.0;
float3 WorldPosition_DDY = 0.0;
#endif // USE_ANALYTIC_DERIVATIVES
SampleContext = CreateVolumeSampleContext(LocalPosition, WorldPosition, WorldPosition_DDX, WorldPosition_DDY, RayMarchingContext.MipLevel);
}
if (RayMarchingContext.bApplyEmissionAndTransmittance)
{
float3 Emission = SampleEmission(SampleContext);
Radiance += Emission * StepSize * Transmittance;
if (GetIndirectLightingMode() == INDIRECT_LIGHTING_MODE_SINGLE_SCATTERING)
{
float3 IndirectInscattering = ComputeIndirectInscattering(WorldPosition, RayMarchingContext.WorldRayDirection);
float3 Extinction = SampleExtinction(SampleContext);
float3 Albedo = SampleAlbedo(SampleContext);
float3 ScatteringCoefficient = Albedo * Extinction;
Radiance += IndirectInscattering * ScatteringCoefficient * Transmittance * StepSize;
}
}
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))
{
#if DIM_USE_INSCATTERING_VOLUME
float3 LocalShadowRayOrigin = mul(float4(WorldPosition, 1.0), GetWorldToLocal()).xyz;
float3 LocalBoundsMin = GetLocalBoundsOrigin() - GetLocalBoundsExtent();
float3 TransmittanceUVW = saturate((LocalShadowRayOrigin - LocalBoundsMin) / (2.0 * GetLocalBoundsExtent()));
float MipLevel = 0;
float3 Inscattering = SampleLightingCache(TransmittanceUVW, MipLevel) * View.OneOverPreExposure;
#else
float3 Inscattering = ComputeInscattering(WorldPosition, LightData, LightType, RayMarchingContext.MaxStepCount, WorldShadowBias, RayMarchingContext.bApplyShadowTransmittance);
#endif
float3 ScatteringCoefficient = Albedo * Extinction;
float IsotropicPhase = 1.0 / (4.0 * PI);
RadianceSample = Inscattering * ScatteringCoefficient * IsotropicPhase * Transmittance * StepSize;
}
#if 0
// Stochastically decide to record scatter position
if (any(Transmittance < RandomSample))
{
LocalScatterTMin = min(LocalHitT, LocalScatterTMin);
}
#endif
if (GetFogInscatteringMode() == FOG_INSCATTERING_MODE_REFERENCE)
{
float WorldRayT = LocalHitT * RayMarchingContext.LocalToWorldScale;
float3 CameraRelative_WorldPosition = RayMarchingContext.WorldRayDirection * WorldRayT;
float4 HeightFog = float4(0, 0, 0, 1);
if (ShouldApplyHeightFog())
{
HeightFog = CalculateHeightFog(CameraRelative_WorldPosition);
}
float4 CombinedFog = HeightFog;
if (ShouldApplyVolumetricFog() && FogStruct.ApplyVolumetricFog > 0)
{
float3 WorldPosition = CameraRelative_WorldPosition + RayMarchingContext.WorldRayOrigin;
const uint EyeIndex = 0;
float3 VolumeUV = ComputeVolumeUV_DEPRECATED(WorldPosition, DFHackToFloat(PrimaryView.WorldToClip));
CombinedFog = CombineVolumetricFog(HeightFog, VolumeUV, EyeIndex, WorldRayT);
}
// Attenuate RadianceSample
RadianceSample *= CombinedFog.a;
// Add FogRadiance
float3 DeltaTransmittance = clamp(Transmittance / PrevTransmittance, 0.0, 1.0);
float3 DeltaFogRadiance = CombinedFog.rgb - PrevCombinedFog.rgb;
FogRadiance += DeltaFogRadiance * DeltaTransmittance;
PrevCombinedFog = CombinedFog;
}
else if (GetFogInscatteringMode() == FOG_INSCATTERING_MODE_LINEAR_APPROX)
{
float WorldRayT = LocalHitT * RayMarchingContext.LocalToWorldScale;
float Weight = saturate((WorldRayT - WorldFogHitT[0]) / (WorldFogHitT[1] - WorldFogHitT[0]));
float4 CombinedFog = lerp(FogHitRadiance[0], FogHitRadiance[1], Weight);
// Attenuate RadianceSample
RadianceSample *= CombinedFog.a;
// Add FogRadiance
float3 DeltaTransmittance = clamp(Transmittance / PrevTransmittance, 0.0, 1.0);
float3 DeltaFogRadiance = CombinedFog.rgb - PrevCombinedFog.rgb;
FogRadiance += DeltaFogRadiance * DeltaTransmittance;
PrevCombinedFog = CombinedFog;
}
// Accumulate radiance
Radiance += RadianceSample;
}
#if USE_CAMERA_AVSM
//Transmittance = AVSM_Sampler4_Eval_NoInterpolation(TransmittanceSampler, WorldHitT);
Transmittance = AVSM_Sampler4_Eval(TransmittanceSampler, WorldHitT);
#else
if (any(Extinction > 0.0))
{
// NOTE: Trimming the end point is too aggressive for exponential interpolation
//RayMarchingContext.LocalScatterT1 = LocalHitT;
RayMarchingContext.LocalScatterT1 = RayMarchingContext.LocalRayTMax;
if (bFirstScatterEvent)
{
RayMarchingContext.LocalScatterT0 = LocalScatterTMin = LocalHitT;
RayMarchingContext.Transmittance0 = Transmittance;
bFirstScatterEvent = false;
}
}
// Accumulate transmittance for the next evaluation
Transmittance *= exp(-Extinction * StepSize);
#endif
float Epsilon = 1.0e-7;
if (all(Transmittance < Epsilon))
{
Transmittance = 0.0;
break;
}
LocalHitT += StepSize;
}
if (GetFogInscatteringMode() != FOG_INSCATTERING_MODE_OFF)
{
// Simplifying terms to avoid vector division:
// float3 DeltaTransmittance = clamp(Transmittance / PrevTransmittance, 0.0, 1.0);
// float3 DeltaOpacity = 1.0 - DeltaTransmittance;
// float3 FogAlpha = PrevTransmittance * DeltaOpacity;
float3 FogAlpha = clamp(PrevTransmittance - Transmittance, 0.0, 1.0);
// Fogging in-scattering contribution is not currently attenuated by Heterogeneous Volumes
// for elements behind the subject. To prevent doubled contribution of in-scattering for
// regions of low density (eg: silhouette edges), blending is interpreted as alpha-masking instead.
Radiance += FogRadiance.rgb * FogAlpha;
}
}
void RayMarchSingleScatteringReferenceFastPath(
inout FRayMarchingContext RayMarchingContext,
#if USE_CAMERA_AVSM
inout FAVSM_Sampler4 TransmittanceSampler,
#endif
uint StepCount,
inout float3 Radiance,
inout float3 Transmittance
)
{
for (uint StepIndex = 0; StepIndex < StepCount; ++StepIndex)
{
float StepSize = RayMarchingContext.StepSize;
float LocalHitT = RayMarchingContext.LocalRayTMin + RayMarchingContext.StepSize * (RayMarchingContext.Jitter + StepIndex);
float WorldHitT = LocalHitT * RayMarchingContext.LocalToWorldScale;
float3 LocalPosition = RayMarchingContext.LocalRayOrigin + RayMarchingContext.LocalRayDirection * LocalHitT;
float3 WorldPosition = RayMarchingContext.WorldRayOrigin + RayMarchingContext.WorldRayDirection * WorldHitT;
#if USE_ANALYTIC_DERIVATIVES
float3 WorldPosition_DDX = RayMarchingContext.WorldRayOrigin + RayMarchingContext.WorldRayDirection_DDX * WorldHitT;
float3 WorldPosition_DDY = RayMarchingContext.WorldRayOrigin + RayMarchingContext.WorldRayDirection_DDY * WorldHitT;
WorldPosition_DDX -= WorldPosition;
WorldPosition_DDY -= WorldPosition;
#else
float3 WorldPosition_DDX = 0.0;
float3 WorldPosition_DDY = 0.0;
#endif // USE_ANALYTIC_DERIVATIVES
FVolumeSampleContext SampleContext = CreateVolumeSampleContext(LocalPosition, WorldPosition, WorldPosition_DDX, WorldPosition_DDY, RayMarchingContext.MipLevel);
// Radiance from emission
if (RayMarchingContext.bApplyEmissionAndTransmittance)
{
float3 Emission = SampleEmission(SampleContext);
Radiance += Emission * StepSize * Transmittance;
#if DIM_USE_LUMEN_GI
FTwoBandSHVectorRGB TranslucencyGISH = GetTranslucencyGIVolumeLighting(DFPromote(WorldPosition), PrimaryView.WorldToClip, true);
FTwoBandSHVector RotatedHGZonalHarmonic;
float3 CameraVector = normalize(RayMarchingContext.WorldRayDirection);
float PhaseG = 0.0;
RotatedHGZonalHarmonic.V = float4(1.0f, CameraVector.y, CameraVector.z, CameraVector.x) * float4(1.0f, PhaseG, PhaseG, PhaseG);
float3 IndirectInscattering = max(DotSH(TranslucencyGISH, RotatedHGZonalHarmonic), 0) / PI;
IndirectInscattering *= GetIndirectInscatteringFactor();
float3 Extinction = SampleExtinction(SampleContext);
float3 Albedo = SampleAlbedo(SampleContext);
float3 ScatteringCoefficient = Albedo * Extinction;
float AmbientOcclusion = EvalAmbientOcclusion(WorldPosition);
// Note: Phase is accounted for in spherical hamonic calculation
Radiance += IndirectInscattering * AmbientOcclusion * ScatteringCoefficient * Transmittance * StepSize;
#endif // DIM_USE_LUMEN_GI
}
// 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;
}
// Accumulate transmittance for the next evaluation
#if USE_CAMERA_AVSM
Transmittance = AVSM_Sampler4_Eval(TransmittanceSampler, WorldHitT);
#else
Transmittance *= exp(-Extinction * StepSize);
#endif
float Epsilon = 1.0e-7;
if (all(Transmittance < Epsilon))
{
Transmittance = 0.0;
break;
}
}
}