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

718 lines
20 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#include "HeterogeneousVolumesSparseVoxelUtils.ush"
#include "../Common.ush"
#define SUPPORT_CONTACT_SHADOWS 0
#define DYNAMICALLY_SHADOWED 1
#define TREAT_MAXDEPTH_UNSHADOWED 1
#define SHADOW_QUALITY 2
#define NO_TRANSLUCENCY_AVAILABLE
#if VIRTUAL_SHADOW_MAP
#include "../VirtualShadowMaps/VirtualShadowMapProjectionCommon.ush"
#endif
#include "HeterogeneousVolumesSparseVoxelUniformBufferUtils.ush"
#include "HeterogeneousVolumesTracingUtils.ush"
#include "HeterogeneousVolumesLightingUtils.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 DIM_SPARSE_VOXEL_TRACING
#define DIM_SPARSE_VOXEL_TRACING 1
#endif // DIM_SPARSE_VOXEL_TRACING
#ifndef DIM_VOXEL_CULLING
#define DIM_VOXEL_CULLING 1
#endif // DIM_VOXEL_CULLING
#ifndef DIM_APPLY_SHADOW_TRANSMITTANCE
#define DIM_APPLY_SHADOW_TRANSMITTANCE 0
#endif // DIM_APPLY_SHADOW_TRANSMITTANCE
// Lighting
int bApplyEmissionAndTransmittance;
int bApplyDirectLighting;
int bApplyShadowTransmittance;
int LightType;
float VolumetricScatteringIntensity;
// Shadowing
uint VirtualShadowMapId;
// Ray data
float MaxTraceDistance;
float MaxShadowTraceDistance;
float StepSize;
int MaxStepCount;
int bJitter;
// Dispatch data
int3 GroupCount;
int DownsampleFactor;
// Volume data
int MipLevel;
// Output
RWTexture2D<float4> RWLightingTexture;
RWTexture2D<float4> RWVelocityTexture;
int3 TextureResolution;
Texture3D InputTexture;
RWTexture3D<float3> RWOutputTexture;
[numthreads(THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D)]
void CopyTexture3D(
uint3 DispatchThreadId : SV_DispatchThreadID
)
{
uint3 Index = DispatchThreadId;
if (all(Index < TextureResolution))
{
RWOutputTexture[Index] = InputTexture[Index].xyz;
}
}
SamplerState TextureSampler;
[numthreads(THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D)]
void GenerateMips3D(
uint3 GroupThreadId : SV_GroupThreadID,
uint3 DispatchThreadId : SV_DispatchThreadID
)
{
uint3 Index = DispatchThreadId;
if (all(Index < TextureResolution))
{
#if 1
float3 UVW = (Index + 0.5) / float3(TextureResolution);
RWOutputTexture[Index] = InputTexture.SampleLevel(TextureSampler, UVW, 0).rgb;
#else
float3 Value = 0;
UNROLL for (uint i = 0; i < 8; ++i)
{
uint3 BaseIndex = Index * 2 + float3((i & 4u) >> 2, (i & 2u) >> 1, (i & 1u));
BaseIndex = min(BaseIndex, TextureResolution * 2);
Value += InputTexture[BaseIndex].xyz;
}
Value *= 0.125;
RWOutputTexture[Index] = Value;
#endif
}
}
[numthreads(THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D)]
void GenerateMin3D(
uint3 GroupThreadId : SV_GroupThreadID,
uint3 DispatchThreadId : SV_DispatchThreadID
)
{
uint3 Index = DispatchThreadId;
if (all(Index < TextureResolution))
{
float3 Value = 65535.0f;
UNROLL for (uint i = 0; i < 8; ++i)
{
int3 BaseIndex = Index * 2 + float3((i & 4u) >> 2, (i & 2u) >> 1, (i & 1u));
BaseIndex = min(BaseIndex, TextureResolution * 2);
Value = min(Value, Luminance(InputTexture[BaseIndex].xyz));
}
RWOutputTexture[Index] = Value;
}
}
StructuredBuffer<FVoxelDataPacked> VoxelBuffer;
Buffer<uint> NumVoxelsBuffer;
// A screen-tile is an 8x8 block of computation for ray marching
struct FRayMarchingTile
{
uint2 PixelOffset;
uint Voxels[2];
// TODO: For tracking purposes..
uint Id;
uint Padding[3];
};
FRayMarchingTile CreateRayMarchingTile(uint TileId, uint2 PixelOffset, uint VoxelMin, uint VoxelMax)
{
FRayMarchingTile Tile = (FRayMarchingTile) 0;
Tile.PixelOffset = PixelOffset;
Tile.Voxels[0] = VoxelMin;
Tile.Voxels[1] = VoxelMax;
Tile.Id = TileId;
return Tile;
}
RWStructuredBuffer<FRayMarchingTile> RWRayMarchingTilesBuffer;
RWBuffer<uint> RWNumRayMarchingTilesBuffer;
RWStructuredBuffer<FVoxelDataPacked> RWVoxelsPerTileBuffer;
RWBuffer<uint> RWNumVoxelsPerTileBuffer;
struct FRayMarchingDebug
{
float4 Planes[5];
float4 BBox[2];
float Padding[4];
};
RWStructuredBuffer<FRayMarchingDebug> RWRayMarchingDebugBuffer;
float4 CreatePlane(
float3 P_000,
float3 P_100,
float3 P_010
)
{
float3 X = P_100 - P_000;
float3 Y = P_010 - P_000;
float3 N = normalize(cross(X, Y));
float d = -dot(N, P_000);
return float4(N, d);
}
float CalcSignedDistance(float3 P, float4 Plane)
{
return dot(P, Plane.xyz) + Plane.w;
}
bool PointIntersectsFrustum(float3 P, float4 Planes[5])
{
for (int i = 0; i < 5; ++i)
{
if (CalcSignedDistance(P, Planes[i]) > 0.0)
{
return false;
}
}
return true;
}
bool BoxIntersectsFrustum(float3 BoxOrigin, float3 BoxExtent, float4 Planes[5])
{
for (int i = 0; i < 5; ++i)
{
float Distance = CalcSignedDistance(BoxOrigin, Planes[i]);
float ProjectedExtent = dot(BoxExtent, abs(Planes[i].xyz));
if (Distance > ProjectedExtent)
{
return false;
}
}
return true;
}
[numthreads(THREADGROUP_SIZE_2D, THREADGROUP_SIZE_2D, 1)]
void GenerateRayMarchingTiles(
uint2 DispatchThreadId : SV_DispatchThreadID
)
{
if (!any(DispatchThreadId))
{
RWNumRayMarchingTilesBuffer[1] = 1;
RWNumRayMarchingTilesBuffer[2] = 1;
}
uint4 Viewport = uint4(DispatchThreadId.xy * THREADGROUP_SIZE_2D, (DispatchThreadId.xy + 1) * THREADGROUP_SIZE_2D);
uint2 PixelOffset = Viewport.xy;
// Scale to full-res
Viewport *= DownsampleFactor;
uint2 ViewPixelOffset = Viewport.xy;
if (any(ViewPixelOffset >= View.ViewSizeAndInvSize.xy))
{
return;
}
// Build tile-frustum
float Epsilon = 1.0e-6;
#if HAS_INVERTED_Z_BUFFER
float NearDepth = 1.0;
float FarDepth = Epsilon;
#else
float NearDepth = 0.0;
float FarDepth = 1.0 - Epsilon;
#endif // HAS_INVERTED_Z_BUFFER
FDFInverseMatrix WorldToLocal = DFPromoteInverse(GetWorldToLocal()); // LWC_TODO
float4x4 TranslatedWorldToLocal = DFFastToTranslatedWorld(WorldToLocal, PrimaryView.PreViewTranslation);
float3 W_000 = SvPositionToTranslatedWorld(float4(Viewport.xw, NearDepth, 1));
float3 W_100 = SvPositionToTranslatedWorld(float4(Viewport.zw, NearDepth, 1));
float3 W_010 = SvPositionToTranslatedWorld(float4(Viewport.xy, NearDepth, 1));
float3 W_110 = SvPositionToTranslatedWorld(float4(Viewport.zy, NearDepth, 1));
float3 W_001 = SvPositionToTranslatedWorld(float4(Viewport.xw, FarDepth, 1));
float3 W_101 = SvPositionToTranslatedWorld(float4(Viewport.zw, FarDepth, 1));
float3 W_011 = SvPositionToTranslatedWorld(float4(Viewport.xy, FarDepth, 1));
float3 W_111 = SvPositionToTranslatedWorld(float4(Viewport.zy, FarDepth, 1));
float3 L_000 = mul(float4(W_000, 1), TranslatedWorldToLocal).xyz;
float3 L_100 = mul(float4(W_100, 1), TranslatedWorldToLocal).xyz;
float3 L_010 = mul(float4(W_010, 1), TranslatedWorldToLocal).xyz;
float3 L_110 = mul(float4(W_110, 1), TranslatedWorldToLocal).xyz;
float3 L_001 = mul(float4(W_001, 1), TranslatedWorldToLocal).xyz;
float3 L_101 = mul(float4(W_101, 1), TranslatedWorldToLocal).xyz;
float3 L_011 = mul(float4(W_011, 1), TranslatedWorldToLocal).xyz;
float3 L_111 = mul(float4(W_111, 1), TranslatedWorldToLocal).xyz;
float4 Planes[5] = {
// Front
CreatePlane(L_000, L_010, L_100),
// Back
//CreatePlane(L_101, L_001, L_111),
// +X
CreatePlane(L_100, L_110, L_101),
// +Y
CreatePlane(L_010, L_011, L_110),
// -X
CreatePlane(L_000, L_001, L_010),
// -Y
CreatePlane(L_000, L_100, L_001)
};
uint DebugIndex = DispatchThreadId.y * THREADGROUP_SIZE_2D + DispatchThreadId.x;
#if DIM_DEBUG
for (uint i = 0; i < 5; ++i)
{
RWRayMarchingDebugBuffer[DebugIndex].Planes[i] = Planes[i];
}
RWRayMarchingDebugBuffer[DebugIndex].BBox[0] = float4(GetLocalBoundsOrigin() - GetLocalBoundsExtent(), 0.0);
RWRayMarchingDebugBuffer[DebugIndex].BBox[1] = float4(GetLocalBoundsOrigin() + GetLocalBoundsExtent(), 0.0);
#endif // DIM_DEBUG
// Intersect frustum against bounding volume
if (!BoxIntersectsFrustum(GetLocalBoundsOrigin(), GetLocalBoundsExtent(), Planes))
{
return;
}
#if DIM_VOXEL_CULLING
float3 LocalBoundsMin = GetLocalBoundsOrigin() - GetLocalBoundsExtent();
float3 LocalBoundsMax = GetLocalBoundsOrigin() + GetLocalBoundsExtent();
// Intersect against voxels to determine allocation
uint VoxelCount = 0;
for (uint VoxelIndex = 0; VoxelIndex < GetNumVoxels(); ++VoxelIndex)
{
FVoxelData VoxelData = DecodeVoxelData(GetVoxelDataPacked(VoxelIndex), GetVolumeResolution());
FBox VoxelBoundingBox = GetVoxelBoundingBox(VoxelData, GetVolumeResolution(), LocalBoundsMin, LocalBoundsMax);
float3 BoxOrigin = (VoxelBoundingBox.Max + VoxelBoundingBox.Min) / 2.0;
float3 BoxExtent = VoxelBoundingBox.Max - VoxelBoundingBox.Min;
if (BoxIntersectsFrustum(BoxOrigin, BoxExtent, Planes))
{
VoxelCount++;
}
}
// Allocate contiguous span of voxel indices
uint VoxelEntryIndex;
if (VoxelCount > 0)
{
InterlockedAdd(RWNumVoxelsPerTileBuffer[0], VoxelCount, VoxelEntryIndex);
// Intersect and store voxels
VoxelCount = 0;
for (uint VoxelIndex = 0; VoxelIndex < GetNumVoxels(); ++VoxelIndex)
{
FVoxelData VoxelData = DecodeVoxelData(GetVoxelDataPacked(VoxelIndex), GetVolumeResolution());
FBox VoxelBoundingBox = GetVoxelBoundingBox(VoxelData, GetVolumeResolution(), LocalBoundsMin, LocalBoundsMax);
float3 BoxOrigin = (VoxelBoundingBox.Max + VoxelBoundingBox.Min) / 2.0;
float3 BoxExtent = VoxelBoundingBox.Max - VoxelBoundingBox.Min;
if (BoxIntersectsFrustum(BoxOrigin, BoxExtent, Planes))
{
RWVoxelsPerTileBuffer[VoxelEntryIndex + VoxelCount] = GetVoxelDataPacked(VoxelIndex);
VoxelCount++;
}
}
}
#else
uint VoxelEntryIndex = 0;
uint VoxelCount = GetNumVoxels();
#endif
// Allocate tile
if (VoxelCount > 0)
{
uint TileId;
InterlockedAdd(RWNumRayMarchingTilesBuffer[0], 1, TileId);
RWRayMarchingTilesBuffer[TileId] = CreateRayMarchingTile(TileId, PixelOffset, VoxelEntryIndex, VoxelEntryIndex + VoxelCount);
}
}
struct FTraceVoxelResult
{
FVoxelData VoxelData;
float2 HitT;
};
FTraceVoxelResult TraceVoxels(float3 LocalRayOrigin, float3 LocalRayDirection, float LocalRayTMin, float LocalRayTMax,
float3 LocalBoundsMin, float3 LocalBoundsMax)
{
FTraceVoxelResult TraceVoxelResult;
TraceVoxelResult.HitT.x = LocalRayTMax;
TraceVoxelResult.HitT.y = LocalRayTMax;
for (uint VoxelIndex = 0; VoxelIndex < GetNumVoxels(); ++VoxelIndex)
{
FVoxelData VoxelData = DecodeVoxelData(GetVoxelDataPacked(VoxelIndex), GetVolumeResolution());
FBox VoxelBoundingBox = GetVoxelBoundingBox(VoxelData, GetVolumeResolution(), LocalBoundsMin, LocalBoundsMax);
float2 HitT = IntersectAABB(LocalRayOrigin, LocalRayDirection, LocalRayTMin, LocalRayTMax, VoxelBoundingBox.Min, VoxelBoundingBox.Max);
bool bIsHit = (HitT.y - HitT.x) > 0.0;
if (bIsHit && (HitT.x < TraceVoxelResult.HitT.x))
{
TraceVoxelResult.HitT = HitT;
TraceVoxelResult.VoxelData = VoxelData;
}
}
return TraceVoxelResult;
}
StructuredBuffer<FVoxelDataPacked> VoxelsPerTileBuffer;
FTraceVoxelResult TraceVoxels(float3 LocalRayOrigin, float3 LocalRayDirection, float LocalRayTMin, float LocalRayTMax,
float3 LocalBoundsMin, float3 LocalBoundsMax,
uint VoxelStartIndex, uint VoxelEndIndex)
{
FTraceVoxelResult TraceVoxelResult;
TraceVoxelResult.HitT.x = LocalRayTMax;
TraceVoxelResult.HitT.y = LocalRayTMax;
for (uint VoxelIndex = VoxelStartIndex; VoxelIndex < VoxelEndIndex; ++VoxelIndex)
{
FVoxelData VoxelData = DecodeVoxelData(VoxelsPerTileBuffer[VoxelIndex], GetVolumeResolution());
FBox VoxelBoundingBox = GetVoxelBoundingBox(VoxelData, GetVolumeResolution(), LocalBoundsMin, LocalBoundsMax);
float2 HitT = IntersectAABB(LocalRayOrigin, LocalRayDirection, LocalRayTMin, LocalRayTMax, VoxelBoundingBox.Min, VoxelBoundingBox.Max);
bool bIsHit = (HitT.y - HitT.x) > 0.0;
if (bIsHit && (HitT.x < TraceVoxelResult.HitT.x))
{
TraceVoxelResult.HitT = HitT;
TraceVoxelResult.VoxelData = VoxelData;
}
}
return TraceVoxelResult;
}
// TODO: Implement ambient occlusion for preshading pipeline
float EvalAmbientOcclusion(float3 WorldPosition)
{
return 1.0;
}
// Cinematic feature options
bool UseInscatteringVolume()
{
return true;
}
#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 ShouldWriteVelocity()
{
return false;
}
bool IsOfflineRender()
{
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_LINEAR_APPROX;
}
#include "HeterogeneousVolumesRayMarchingUtils.ush"
RWTexture3D<float3> RWLightingCacheTexture;
[numthreads(THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D)]
void RenderLightingCacheWithPreshadingCS(
uint3 GroupId : SV_GroupID,
uint3 GroupThreadId : SV_GroupThreadID,
uint3 DispatchThreadId : SV_DispatchThreadID
)
{
float3 VoxelIndex = DispatchThreadId;
if (any(VoxelIndex >= LightingCacheResolution))
{
return;
}
float3 VoxelJitter = 0.5;
if (bJitter)
{
uint3 Rand32Bits = Rand4DPCG32(uint4(VoxelIndex, View.StateFrameIndexMod8)).xyz;
VoxelJitter = float3(Rand32Bits) / float(uint(0xffffffff));
}
//float3 UVW = (VoxelIndex + VectorJitter) / float3(GetLightingCacheResolution());
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;
FVolumeSampleContext 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;
}
// Build shadow ray
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
}
RWStructuredBuffer<FVoxelDataPacked> RWVoxelOutputBuffer;
StructuredBuffer<FRayMarchingTile> RayMarchingTilesBuffer;
Buffer<uint> NumRayMarchingTilesBuffer;
float2 GetDispatchSize()
{
return View.ViewSizeAndInvSize.xy / DownsampleFactor;
}
[numthreads(THREADGROUP_SIZE_1D, 1, 1)]
void RenderSingleScatteringWithPreshadingCS(
uint GroupId : SV_GroupID,
uint GroupThreadId : SV_GroupThreadID
)
{
// Convert 1D index to pixel coordinate
FRayMarchingTile RayMarchingTile = RayMarchingTilesBuffer[GroupId];
// TODO: Morton..
uint2 TileCoord = uint2(GroupThreadId % THREADGROUP_SIZE_2D, GroupThreadId / THREADGROUP_SIZE_2D);
//float2 PixelCoord = View.ViewRectMin.xy + RayMarchingTile.PixelOffset + TileCoord;
float2 PixelCoord = RayMarchingTile.PixelOffset + TileCoord;
float2 ViewPixelCoord = PixelCoord * DownsampleFactor;
float3 Radiance = 0.0;
float4 LightingTexture = RWLightingTexture[PixelCoord];
float3 Transmittance = LightingTexture.aaa;
float PrevOpacity = Luminance(1.0 - Transmittance);
// Create screen ray
if (any(PixelCoord.xy >= GetDispatchSize()))
{
return;
}
PixelCoord += View.ViewRectMin.xy;
// 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), MaxTraceDistance);
DeviceZ = ConvertToDeviceZ(SceneDepth);
float Jitter = bJitter ? InterleavedGradientNoise(ViewPixelCoord, View.StateFrameIndexMod8) : 0.0;
// Intersect ray with bounding volume
// TODO: LWC integration..
float3 WorldRayOrigin = DFHackToFloat(DFFastSubtract(GetTranslatedWorldCameraPosFromView(ViewPixelCoord + View.TemporalAAJitter.xy), PrimaryView.PreViewTranslation));
float3 WorldRayEnd = DFHackToFloat(SvPositionToWorld(float4(ViewPixelCoord + View.TemporalAAJitter.xy, DeviceZ, 1)));
float3 WorldRayDirection = WorldRayEnd - WorldRayOrigin;
float WorldRayLength = length(WorldRayDirection);
WorldRayDirection /= WorldRayLength;
float3 LocalRayOrigin = mul(float4(WorldRayOrigin, 1.0), GetWorldToLocal()).xyz;
float3 LocalRayEnd = mul(float4(WorldRayEnd, 1.0), GetWorldToLocal()).xyz;
float3 LocalRayDirection = LocalRayEnd - LocalRayOrigin;
float LocalRayLength = length(LocalRayDirection);
LocalRayDirection /= LocalRayLength;
float3 LocalBoundsMin = GetLocalBoundsOrigin() - GetLocalBoundsExtent();
float3 LocalBoundsMax = GetLocalBoundsOrigin() + GetLocalBoundsExtent();
// Test bounding volume
float2 HitT = IntersectAABB(LocalRayOrigin, LocalRayDirection, 0.0, LocalRayLength, LocalBoundsMin, LocalBoundsMax);
float HitSpan = HitT.y - HitT.x;
if (HitSpan <= 0.0)
{
return;
}
float WorldHitTMin = WorldRayLength;
#if DIM_SPARSE_VOXEL_TRACING
float RayTMax = HitT.y;
#if DIM_VOXEL_CULLING
HitT = TraceVoxels(LocalRayOrigin, LocalRayDirection, 0.0, RayTMax,
LocalBoundsMin, LocalBoundsMax,
RayMarchingTile.Voxels[0], RayMarchingTile.Voxels[1]).HitT;
#else
HitT = TraceVoxels(LocalRayOrigin, LocalRayDirection, 0.0, RayTMax, LocalBoundsMin, LocalBoundsMax).HitT;
#endif // DIM_VOXEL_CULLING
HitSpan = HitT.y - HitT.x;
while (HitSpan > 0.0)
#endif // DIM_SPARSE_VOXEL_TRACING
{
FRayMarchingContext RayMarchingContext = CreateRayMarchingContext(
// Local-space
LocalRayOrigin,
LocalRayDirection,
HitT.x,
HitT.y,
// World-space
WorldRayOrigin,
WorldRayDirection,
// Ray-step attributes
Jitter,
CalcStepSize(LocalRayDirection),
MaxStepCount,
bApplyEmissionAndTransmittance,
bApplyDirectLighting,
DIM_APPLY_SHADOW_TRANSMITTANCE
);
const FDeferredLightData LightData = InitDeferredLightFromUniforms(LightType, VolumetricScatteringIntensity);
uint StepCount = CalcStepCount(RayMarchingContext);
float ScatterHitT = HitT.y;
RayMarchSingleScattering(
RayMarchingContext,
LightData,
LightType,
StepCount,
ScatterHitT,
Radiance,
Transmittance
);
WorldHitTMin = min(WorldHitTMin, ScatterHitT * RayMarchingContext.LocalToWorldScale);
#if DIM_SPARSE_VOXEL_TRACING
#if DIM_VOXEL_CULLING
HitT = TraceVoxels(LocalRayOrigin, LocalRayDirection, HitT.y + 0.01, RayTMax,
LocalBoundsMin, LocalBoundsMax,
RayMarchingTile.Voxels[0], RayMarchingTile.Voxels[1]).HitT;
#else
HitT = TraceVoxels(LocalRayOrigin, LocalRayDirection, HitT.y + 0.0001, RayTMax, LocalBoundsMin, LocalBoundsMax).HitT;
#endif // DIM_VOXEL_CULLING
HitSpan = HitT.y - HitT.x;
#endif // DIM_SPARSE_VOXEL_TRACING
}
// Radiance
LightingTexture.rgb += Radiance * View.PreExposure;
if (bApplyEmissionAndTransmittance)
{
float Opacity = Luminance(1.0 - Transmittance);
LightingTexture.a = Luminance(Transmittance);
if (ShouldWriteVelocity())
{
bool bWriteVelocity =
(WorldHitTMin < WorldRayLength) &&
(PrevOpacity < 0.5) &&
(Opacity > 0.5);
if (bWriteVelocity)
{
float3 Velocity = Calculate3DVelocityAtWorldPos(WorldRayOrigin, WorldRayDirection, WorldHitTMin);
RWVelocityTexture[PixelCoord] = EncodeVelocityToTexture(Velocity);
}
}
#if DIM_DEBUG
//if (!all(DispatchThreadId))
if (GroupId == 0 && GroupThreadId == 0)
{
RWVoxelOutputBuffer[0].MipLevel = GetNumVoxels();
for (uint VoxelIndex = 0; VoxelIndex < GetNumVoxels(); ++VoxelIndex)
{
RWVoxelOutputBuffer[VoxelIndex + 1] = GetVoxelDataPacked(VoxelIndex);
}
}
#endif
}
RWLightingTexture[PixelCoord] = LightingTexture;
}