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

768 lines
28 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#include "HeterogeneousVolumesVoxelGridTypes.ush"
#include "../Common.ush"
#include "../DeferredShadingCommon.ush"
#include "../ComputeShaderUtils.ush"
#include "HeterogeneousVolumesTracingUtils.ush"
#include "HeterogeneousVolumesVoxelGridTraversal.ush"
#include "HeterogeneousVolumesVoxelGridRenderingUtils.ush"
#include "../PathTracing/Utilities/PathTracingRandomSequence.ush"
#define DEBUG_MODE_DDA_MARCHING 0
#define DEBUG_MODE_SAMPLE_BASED_MARCHING 1
#define DEBUG_MODE_SAMPLE_BASED_MAJORANT 2
#define DEBUG_MODE_SAMPLE_BASED_MAX_MAJORANT 3
#define DEBUG_MODE_SAMPLE_BASED_MAJORANT_DDA 4
#define DEBUG_MODE_TRACKING_BASED_DELTA_TRACKING 5
#define DEBUG_MODE_TRACKING_BASED_RATIO_TRACKING 6
#define DEBUG_MODE_TRACKING_BASED_SPECTRAL_RATIO_TRACKING 7
#define DEBUG_MODE_TRACKING_BASED_MAJORANT_DDA 8
#define DEBUG_MODE_SAMPLING_BASED \
(DEBUG_MODE == DEBUG_MODE_SAMPLE_BASED_MARCHING) || \
(DEBUG_MODE == DEBUG_MODE_SAMPLE_BASED_MAJORANT) || \
(DEBUG_MODE == DEBUG_MODE_SAMPLE_BASED_MAX_MAJORANT) || \
(DEBUG_MODE == DEBUG_MODE_SAMPLE_BASED_MAJORANT_DDA)
#define DEBUG_MODE_TRACKING_BASED \
(DEBUG_MODE == DEBUG_MODE_TRACKING_BASED_DELTA_TRACKING) || \
(DEBUG_MODE == DEBUG_MODE_TRACKING_BASED_RATIO_TRACKING) || \
(DEBUG_MODE == DEBUG_MODE_TRACKING_BASED_SPECTRAL_RATIO_TRACKING) || \
(DEBUG_MODE == DEBUG_MODE_TRACKING_BASED_MAJORANT_DDA)
#define COLLISION_MODE_RATIO_TRACKING 0
#define COLLISION_MODE_MAX_COMPONENT 1
#define COLLISION_MODE_AVG_COMPONENT 2
struct FRenderDebugData
{
float3 Estimate;
float3 RayOrigin;
float3 RayDirection;
float TMin;
float TMax;
float Pdf;
float Distance;
float4x4 ScreenToTranslatedWorld;
float4x4 ClipToTranslatedWorld;
};
// Ray data
int bJitter;
float MaxTraceDistance;
float StepSize;
int MaxStepCount;
// Dispatch data
int3 GroupCount;
int DebugMode;
// Output
RWTexture2D<float4> RWLightingTexture;
RWStructuredBuffer<FRenderDebugData> RWDebugBuffer;
struct FTrackingResult
{
float3 Radiance;
float3 Throughput;
float Pdf;
float Distance;
bool bIsScattering;
};
FTrackingResult CreateTrackingResult(float Distance)
{
FTrackingResult Result = (FTrackingResult)0;
Result.Radiance = 0;
Result.Throughput = 1;
Result.Pdf = 0;
Result.Distance = Distance;
Result.bIsScattering = false;
return Result;
}
// Concatenates the result of rhs onto lhs
FTrackingResult ConcatenateTrackingResult(FTrackingResult lhs, FTrackingResult rhs)
{
FTrackingResult Result;
//Result.Radiance = lhs.Radiance + rhs.Radiance * lhs.Throughput;
Result.Radiance = rhs.Radiance * lhs.Throughput;
Result.Throughput = lhs.Throughput * rhs.Throughput;
Result.Pdf = lhs.Pdf * rhs.Pdf;
Result.Distance = lhs.Distance + rhs.Distance;
Result.bIsScattering = rhs.bIsScattering;
return Result;
}
FTrackingResult AddTrackingResult(FTrackingResult lhs, FTrackingResult rhs)
{
FTrackingResult Sum;
Sum.Radiance = lhs.Radiance + rhs.Radiance;
Sum.Throughput = lhs.Throughput + rhs.Throughput;
Sum.Pdf = lhs.Pdf + rhs.Pdf;
Sum.Distance = lhs.Distance + rhs.Distance;
return Sum;
}
FTrackingResult DivideTrackingResult(FTrackingResult Value, int SPP)
{
FTrackingResult Quotient;
Quotient.Radiance = Value.Radiance / SPP;
Quotient.Throughput = Value.Throughput / SPP;
Quotient.Pdf = Value.Pdf / SPP;
Quotient.Distance = Value.Distance / SPP;
return Quotient;
}
float3 GetTranslatedWorldPos(float3 WorldPos)
{
float3 TranslatedWorldPos = DFFastToTranslatedWorld(WorldPos, PrimaryView.PreViewTranslation);
return TranslatedWorldPos;
}
FMajorantData EvalMajorant(float3 TranslatedWorldPos)
{
FMajorantData MajorantData = CreateMajorantData();
float3 TranslatedWorldBoundsMin = GetTranslatedWorldPos(OrthoGridUniformBuffer.TopLevelGridWorldBoundsMin);
float3 TranslatedWorldBoundsMax = GetTranslatedWorldPos(OrthoGridUniformBuffer.TopLevelGridWorldBoundsMax);
float3 TranslatedWorldBoundsExtent = TranslatedWorldBoundsMax - TranslatedWorldBoundsMin;
float3 GridUV = (TranslatedWorldPos - TranslatedWorldBoundsMin) / TranslatedWorldBoundsExtent;
if (all(GridUV >= 0.0) && all(GridUV <= 1.0))
{
float3 TopLevelVoxelPos = GridUV * OrthoGridUniformBuffer.TopLevelGridResolution;
uint LinearTopLevelVoxelPos = GetLinearIndex(TopLevelVoxelPos, OrthoGridUniformBuffer.TopLevelGridResolution);
UpdateMajorantData(MajorantData, GetMajorantData(OrthoGridUniformBuffer.MajorantGridBuffer[LinearTopLevelVoxelPos]));
}
return MajorantData;
}
float CalcStepSizeLocal(float3 WorldDirection)
{
float3 TranslatedWorldBoundsMin = GetTranslatedWorldPos(OrthoGridUniformBuffer.TopLevelGridWorldBoundsMin);
float3 TranslatedWorldBoundsMax = GetTranslatedWorldPos(OrthoGridUniformBuffer.TopLevelGridWorldBoundsMax);
float3 TranslatedWorldBoundsExtent = TranslatedWorldBoundsMax - TranslatedWorldBoundsMin;
float3 BottomLevelVoxelResolution = OrthoGridUniformBuffer.TopLevelGridResolution * 4;
// Conditionally apply indirection dimensions
BottomLevelVoxelResolution *= 4;
float3 VoxelSize = TranslatedWorldBoundsExtent / BottomLevelVoxelResolution;
float3 VoxelHitT = VoxelSize / abs(WorldDirection);
float StepSize = min(VoxelHitT.x, min(VoxelHitT.y, VoxelHitT.z));
return StepSize;
}
FTrackingResult SampleBasedMarch(float3 WorldRayOrigin, float3 WorldRayDirection, float TMin, float TMax, float WorldStepSize)
{
FTrackingResult Result = CreateTrackingResult(TMin);
float HitT = TMin;
const float Epsilon = 1.0e-4;
while (HitT < TMax && any(Result.Throughput > Epsilon))
{
float3 WorldPosition = WorldRayOrigin + WorldRayDirection * HitT;
int LinearVoxelPos = -1;
float3 Extinction = EvalExtinctionOutOfFrustum(WorldPosition, LinearVoxelPos);
if (any(Extinction > 0))
{
Result.Radiance += GetEmission(OrthoGridUniformBuffer.EmissionGridBuffer[LinearVoxelPos]) * Result.Throughput;
}
Result.Throughput *= exp(-Extinction * WorldStepSize);
HitT += WorldStepSize;
}
Result.Distance = HitT;
return Result;
}
FTrackingResult SampleBasedMajorantMarch(float3 WorldRayOrigin, float3 WorldRayDirection, float TMin, float TMax, float WorldStepSize)
{
FTrackingResult Result = (FTrackingResult)0;
Result.Throughput = 1.0;
float HitT = TMin;
const float Epsilon = 1.0e-3;
while (HitT < TMax && any(Result.Throughput > Epsilon))
{
float3 WorldPosition = WorldRayOrigin + WorldRayDirection * HitT;
FMajorantData MajorantData = EvalMajorant(WorldPosition);
float3 Extinction = MajorantData.Majorant;
Result.Throughput *= exp(-Extinction * WorldStepSize);
HitT += WorldStepSize;
}
Result.Distance = HitT;
Result.Pdf = Result.Throughput.x; // TODO: handle RGB?
return Result;
}
FTrackingResult SampleBasedMaxMajorantMarch(float3 WorldRayOrigin, float3 WorldRayDirection, float TMin, float TMax, float WorldStepSize)
{
FTrackingResult Result = (FTrackingResult)0;
Result.Throughput = 1.0;
float HitT = TMin;
const float Epsilon = 1.0e-3;
while (HitT < TMax && any(Result.Throughput > Epsilon))
{
float3 WorldPosition = WorldRayOrigin + WorldRayDirection * HitT;
FMajorantData MajorantData = GetMajorantData(OrthoGridUniformBuffer.MajorantGridBuffer[0]);
float3 Extinction = MajorantData.Majorant;
Result.Throughput *= exp(-Extinction * WorldStepSize);
HitT += WorldStepSize;
}
Result.Distance = HitT;
Result.Pdf = Result.Throughput.x; // TODO: handle RGB?
return Result;
}
float MaxComponent(float3 Value)
{
return max(Value.x, max(Value.y, Value.z));
}
FTrackingResult DeltaTracking(float3 WorldRayOrigin, float3 WorldRayDirection, float TMin, float TMax, float SigmaBar, inout RandomSequence RandSequence)
{
FTrackingResult Result = CreateTrackingResult(TMin);
float HitT = 0;
while (Result.Distance < TMax)
{
// Pick a distance, based on SigmaBar
float RandValue = RandomSequence_GenerateSample1D(RandSequence);
HitT = -log(RandValue) / SigmaBar;
Result.Distance += HitT;
// Evaluate SigmaT at that location
if (Result.Distance < TMax)
{
float3 WorldPosition = WorldRayOrigin + WorldRayDirection * Result.Distance;
int LinearVoxelPos = -1;
float3 Extinction = EvalExtinctionOutOfFrustum(WorldPosition, LinearVoxelPos);
float SigmaT = Luminance(Extinction);
//float AcceptanceProbability = MaxComponent(SigmaT) / SigmaBar;
float AcceptanceProbability = min(SigmaT / SigmaBar, 1);
if (RandomSequence_GenerateSample1D(RandSequence) < AcceptanceProbability)
{
Result.Throughput = 0;
Result.Radiance = GetEmission(OrthoGridUniformBuffer.EmissionGridBuffer[LinearVoxelPos]) / Luminance(SigmaT);
break;
}
}
}
Result.Distance = min(Result.Distance, TMax);
Result.Pdf = SigmaBar * exp(-SigmaBar * (Result.Distance - TMin));
return Result;
}
FTrackingResult SpectralWeightedDeltaTracking(float3 WorldRayOrigin, float3 WorldRayDirection, float TMin, float TMax, float SigmaBar, int CollisionMode, inout RandomSequence RandSequence)
{
FTrackingResult Result = CreateTrackingResult(TMin);
float HitT = 0;
const float Epsilon = 1.0e-4;
while (Result.Distance < TMax && all(Result.Throughput > Epsilon))
{
// Distance-sampling, based on SigmaBar
float RandValue = RandomSequence_GenerateSample1D(RandSequence);
HitT = -log(RandValue) / SigmaBar;
Result.Distance += HitT;
if (Result.Distance < TMax)
{
float3 WorldPosition = WorldRayOrigin + WorldRayDirection * Result.Distance;
int LinearVoxelPos = -1;
float3 Extinction = EvalExtinctionOutOfFrustum(WorldPosition, LinearVoxelPos);
// Pick a volume coefficient, based on proportional weight
float3 Albedo = GetAlbedo(OrthoGridUniformBuffer.ScatteringGridBuffer[LinearVoxelPos]) / Extinction;
float3 SpectralAbsorptionCollisionWeight = Extinction * (1 - Albedo) / SigmaBar;
float3 SpectralScatteringCollisionWeight = Extinction * Albedo / SigmaBar;
float3 SpectralNullCollisionWeight = 1.0 - Extinction / SigmaBar;
// Set collision probabilities
float AbsorptionProbability = 0;
float ScatteringProbability = 0;
float NullCollisionProbability = 0;
if (CollisionMode == COLLISION_MODE_MAX_COMPONENT)
{
AbsorptionProbability = MaxComponent(SpectralAbsorptionCollisionWeight);
ScatteringProbability = MaxComponent(SpectralScatteringCollisionWeight);
NullCollisionProbability = MaxComponent(SpectralNullCollisionWeight);
}
else if (CollisionMode == COLLISION_MODE_AVG_COMPONENT)
{
AbsorptionProbability = dot(SpectralAbsorptionCollisionWeight, Result.Throughput);
ScatteringProbability = dot(SpectralScatteringCollisionWeight, Result.Throughput);
NullCollisionProbability = dot(SpectralNullCollisionWeight, Result.Throughput);
}
else if (CollisionMode == COLLISION_MODE_RATIO_TRACKING)
{
AbsorptionProbability = 0;
ScatteringProbability = 0;
NullCollisionProbability = 1;
}
// Collision-sampling
float3 CollisionCdf = float3(AbsorptionProbability,
AbsorptionProbability + ScatteringProbability,
AbsorptionProbability + ScatteringProbability + NullCollisionProbability
);
float CollisionSample = RandomSequence_GenerateSample1D(RandSequence) * CollisionCdf.z;
// Normalize probabilities
AbsorptionProbability *= rcp(CollisionCdf.z);
ScatteringProbability *= rcp(CollisionCdf.z);
NullCollisionProbability *= rcp(CollisionCdf.z);
// Emission
if (CollisionSample < CollisionCdf.x)
{
Result.Throughput *= SpectralAbsorptionCollisionWeight / AbsorptionProbability;
Result.Radiance = GetEmission(OrthoGridUniformBuffer.EmissionGridBuffer[LinearVoxelPos]) * Result.Throughput;
Result.Throughput = 0;
break;
}
// Scattering
else if (CollisionSample < CollisionCdf.y)
{
// Compute in-scattering
Result.Throughput *= SpectralScatteringCollisionWeight / ScatteringProbability;
Result.bIsScattering = true;
break;
}
// Transmittance
else
{
Result.Throughput *= SpectralNullCollisionWeight / NullCollisionProbability;
}
}
}
Result.Distance = min(Result.Distance, TMax);
Result.Pdf = SigmaBar * exp(-SigmaBar * (Result.Distance - TMin));
return Result;
}
FTrackingResult RatioTracking(float3 WorldRayOrigin, float3 WorldRayDirection, float TMin, float TMax, float SigmaBar, inout RandomSequence RandSequence)
{
FTrackingResult Result = CreateTrackingResult(TMin);
const float Epsilon = 1.0e-5;
float HitT = 0;
while (Result.Distance < TMax && all(Result.Throughput > Epsilon))
{
// Pick a distance, based on SigmaBar
float RandValue = RandomSequence_GenerateSample1D(RandSequence);
HitT = -log(RandValue) / SigmaBar;
Result.Distance += HitT;
// Evaluate SigmaT at that location
if (Result.Distance < TMax)
{
float3 WorldPosition = WorldRayOrigin + WorldRayDirection * Result.Distance;
int LinearVoxelPos = -1;
float3 Extinction = EvalExtinctionOutOfFrustum(WorldPosition, LinearVoxelPos);
float SigmaT = Luminance(Extinction);
float AcceptanceProbability = min(SigmaT / SigmaBar, 1);
if (RandomSequence_GenerateSample1D(RandSequence) < AcceptanceProbability)
{
//Result.Emission = GetEmission(OrthoGridUniformBuffer.EmissionGridBuffer[LinearVoxelPos]) / Luminance(SigmaT);
}
Result.Throughput *= (1 - AcceptanceProbability);
}
}
Result.Distance = min(Result.Distance, TMax);
Result.Pdf = SigmaBar * exp(-SigmaBar * (Result.Distance - TMin));
return Result;
}
FTrackingResult SpectralRatioTracking(float3 WorldRayOrigin, float3 WorldRayDirection, float TMin, float TMax, float SigmaBar, inout RandomSequence RandSequence)
{
FTrackingResult Result = CreateTrackingResult(TMin);
const float Epsilon = 1.0e-5;
float HitT = 0;
while (Result.Distance < TMax && all(Result.Throughput > Epsilon))
{
// Pick a distance, based on SigmaBar
float RandValue = RandomSequence_GenerateSample1D(RandSequence);
HitT = -log(RandValue) / SigmaBar;
Result.Distance += HitT;
// Evaluate SigmaT at that location
if (Result.Distance < TMax)
{
float3 WorldPosition = WorldRayOrigin + WorldRayDirection * Result.Distance;
int LinearVoxelPos = -1;
float3 Extinction = EvalExtinctionOutOfFrustum(WorldPosition, LinearVoxelPos);
// Pick a component, based on proportional weight
float3 SpectralCdf = float3(Extinction.x, Extinction.x + Extinction.y, Extinction.x + Extinction.y + Extinction.z);
float RandSpectra = RandomSequence_GenerateSample1D(RandSequence) * SpectralCdf.z;
float SigmaT = (RandSpectra < SpectralCdf.x) ? Extinction.x : ((RandSpectra < SpectralCdf.y) ? Extinction.y : Extinction.z);
if (SigmaT > 0)
{
float3 SpectralWeight = Extinction / SigmaT;
float AcceptanceProbability = min(SigmaT / SigmaBar, 1);
float3 SpectralAcceptanceProbability = min(SpectralWeight * SigmaT / SigmaBar, 1);
if (RandomSequence_GenerateSample1D(RandSequence) < AcceptanceProbability)
{
//Result.Emission = GetEmission(OrthoGridUniformBuffer.EmissionGridBuffer[LinearVoxelPos]) / Luminance(SigmaT);
}
Result.Throughput *= (1 - SpectralAcceptanceProbability);
}
}
}
Result.Distance = min(Result.Distance, TMax);
Result.Pdf = SigmaBar * exp(-SigmaBar * (Result.Distance - TMin));
return Result;
}
FTrackingResult DDABasedMajorantMarch(float3 WorldRayOrigin, float3 Direction, float TMin, float TMax, int DistanceSamplingMethod, int CollisionSamplingMethod, inout RandomSequence RandSequence)
{
FTrackingResult TrackingResult = CreateTrackingResult(TMin);
float3 WorldRayBegin = WorldRayOrigin + Direction * TMin;
float3 WorldRayEnd = WorldRayOrigin + Direction * TMax;
float WorldRayTMax = length(WorldRayEnd - WorldRayBegin);
// Transform to voxel-space
float3 WorldBoundsMin = OrthoGridUniformBuffer.TopLevelGridWorldBoundsMin;
float3 WorldBoundsMax = OrthoGridUniformBuffer.TopLevelGridWorldBoundsMax;
float3 TopLevelGridWorldBoundsExtent = WorldBoundsMax - WorldBoundsMin;
float3 VoxelRayBegin = (WorldRayBegin - WorldBoundsMin) / TopLevelGridWorldBoundsExtent * OrthoGridUniformBuffer.TopLevelGridResolution;
float3 VoxelRayEnd = (WorldRayEnd - WorldBoundsMin) / TopLevelGridWorldBoundsExtent * OrthoGridUniformBuffer.TopLevelGridResolution;
float3 VoxelRayDirection = VoxelRayEnd - VoxelRayBegin;
float VoxelRayTMin = 0.0;
float VoxelRayTMax = length(VoxelRayDirection);
VoxelRayDirection /= VoxelRayTMax;
float VoxelToWorldScale = WorldRayTMax / VoxelRayTMax;
// March majorant grid via DDA
float3 VoxelRayDirectionInv = 1.0 / VoxelRayDirection;
float VoxelRayMarchT = VoxelRayTMin;
float3 VoxelRayMarchPos = VoxelRayBegin;
float3 VoxelBoundsPos;
VoxelBoundsPos.x = sign(VoxelRayDirection.x) > 0 ? floor(VoxelRayMarchPos.x) + 1 : ceil(VoxelRayMarchPos.x) - 1;
VoxelBoundsPos.y = sign(VoxelRayDirection.y) > 0 ? floor(VoxelRayMarchPos.y) + 1 : ceil(VoxelRayMarchPos.y) - 1;
VoxelBoundsPos.z = sign(VoxelRayDirection.z) > 0 ? floor(VoxelRayMarchPos.z) + 1 : ceil(VoxelRayMarchPos.z) - 1;
float3 VoxelBoundsHitT = (VoxelBoundsPos - VoxelRayMarchPos) * VoxelRayDirectionInv;
// Remove floating-point rounding error
float Epsilon = 1.0e-4;
if (VoxelBoundsHitT.x <= Epsilon) VoxelBoundsHitT.x += abs(VoxelRayDirectionInv.x);
if (VoxelBoundsHitT.y <= Epsilon) VoxelBoundsHitT.y += abs(VoxelRayDirectionInv.y);
if (VoxelBoundsHitT.z <= Epsilon) VoxelBoundsHitT.z += abs(VoxelRayDirectionInv.z);
while ((VoxelRayMarchT < VoxelRayTMax) && any(TrackingResult.Throughput > Epsilon) && !TrackingResult.bIsScattering)
{
float VoxelRayMarchDeltaT = 0.0;
if (VoxelBoundsHitT.x < VoxelBoundsHitT.y)
{
if (VoxelBoundsHitT.x < VoxelBoundsHitT.z)
{
VoxelRayMarchDeltaT = VoxelBoundsHitT.x - VoxelRayMarchT;
VoxelBoundsHitT.x += abs(VoxelRayDirectionInv.x);
}
else
{
VoxelRayMarchDeltaT = VoxelBoundsHitT.z - VoxelRayMarchT;
VoxelBoundsHitT.z += abs(VoxelRayDirectionInv.z);
}
}
else
{
if (VoxelBoundsHitT.y < VoxelBoundsHitT.z)
{
VoxelRayMarchDeltaT = VoxelBoundsHitT.y - VoxelRayMarchT;
VoxelBoundsHitT.y += abs(VoxelRayDirectionInv.y);
}
else
{
VoxelRayMarchDeltaT = VoxelBoundsHitT.z - VoxelRayMarchT;
VoxelBoundsHitT.z += abs(VoxelRayDirectionInv.z);
}
}
// Clip voxel delta-t by overall voxel ray-length.
if (VoxelRayMarchT + VoxelRayMarchDeltaT > VoxelRayTMax)
{
VoxelRayMarchDeltaT = VoxelRayTMax - VoxelRayMarchT;
}
// Sample at the midpoint of the voxel ray
float3 VoxelEndPos = VoxelRayMarchPos + VoxelRayDirection * VoxelRayMarchDeltaT;
float3 VoxelSamplePos = (VoxelRayMarchPos + VoxelEndPos) * 0.5;
uint VoxelSampleLinearPos = GetLinearIndex(VoxelSamplePos, OrthoGridUniformBuffer.TopLevelGridResolution);
FMajorantData MajorantData = GetMajorantData(OrthoGridUniformBuffer.MajorantGridBuffer[VoxelSampleLinearPos]);
float3 TranslatedWorldPos = WorldRayBegin + Direction * VoxelRayMarchT * VoxelToWorldScale;
if (DistanceSamplingMethod == DEBUG_MODE_SAMPLE_BASED_MARCHING)
{
float StepSize = CalcStepSizeLocal(Direction);
FTrackingResult Sample = SampleBasedMarch(TranslatedWorldPos, Direction, 0.0, VoxelRayMarchDeltaT * VoxelToWorldScale, StepSize);
TrackingResult = ConcatenateTrackingResult(TrackingResult, Sample);
}
else if (DistanceSamplingMethod == DEBUG_MODE_TRACKING_BASED_SPECTRAL_RATIO_TRACKING)
{
FTrackingResult Sample = SpectralRatioTracking(TranslatedWorldPos, Direction, 0.0, VoxelRayMarchDeltaT * VoxelToWorldScale, MajorantData.Majorant, RandSequence);
TrackingResult = ConcatenateTrackingResult(TrackingResult, Sample);
}
else if (DistanceSamplingMethod == DEBUG_MODE_TRACKING_BASED_DELTA_TRACKING)
{
//FTrackingResult Sample = SpectralWeightedDeltaTracking(TranslatedWorldPos, Direction, 0.0, VoxelRayMarchDeltaT * VoxelToWorldScale, MajorantData.Majorant, CollisionSamplingMethod, RandSequence);
FTrackingResult Sample = DeltaTracking(TranslatedWorldPos, Direction, 0.0, VoxelRayMarchDeltaT * VoxelToWorldScale, MajorantData.Majorant, RandSequence);
TrackingResult = ConcatenateTrackingResult(TrackingResult, Sample);
}
else
{
FTrackingResult Sample = CreateTrackingResult(0);
Sample.Distance += VoxelRayMarchDeltaT * VoxelToWorldScale;
Sample.Throughput *= exp(-MajorantData.Majorant * Sample.Distance);
TrackingResult = ConcatenateTrackingResult(TrackingResult, Sample);
}
VoxelRayMarchT += VoxelRayMarchDeltaT;
VoxelRayMarchPos = VoxelEndPos;
}
return TrackingResult;
}
[numthreads(THREADGROUP_SIZE_2D, THREADGROUP_SIZE_2D, 1)]
void RenderTransmittanceWithVoxelGridCS(
uint2 GroupThreadId : SV_GroupThreadID,
uint2 DispatchThreadId : SV_DispatchThreadID
)
{
float3 Radiance = 0.0;
// Create screen ray
if (any(DispatchThreadId.xy >= View.ViewSizeAndInvSize.xy))
{
return;
}
uint LinearIndex = DispatchThreadId.y * GroupCount.x * THREADGROUP_SIZE_2D + DispatchThreadId.x;
uint2 PixelCoord = DispatchThreadId.xy + View.ViewRectMin.xy;
// Extract depth
float DeviceZ = SceneDepthTexture.Load(int3(PixelCoord, 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);
float3 RayOrigin = GetTranslatedWorldCameraPosFromView(PixelCoord + 0.5);
float3 RayEnd = SvPositionToTranslatedWorld(float4(PixelCoord + 0.5, DeviceZ, 1));
float3 RayDirection = RayEnd - RayOrigin;
float RayTMin = 0.0;
float RayTMax = length(RayDirection);
RayDirection /= RayTMax;
RandomSequence RandSequence;
RandomSequence_Initialize(RandSequence, LinearIndex, View.StateFrameIndex);
float Jitter = RandomSequence_GenerateSample1D(RandSequence);
float3 Emission = 0;
float3 Transmittance = 1;
float3 BoundsMin = GetTranslatedWorldPos(OrthoGridUniformBuffer.TopLevelGridWorldBoundsMin);
float3 BoundsMax = GetTranslatedWorldPos(OrthoGridUniformBuffer.TopLevelGridWorldBoundsMax);
float2 ClipHitT = IntersectAABB(RayOrigin, RayDirection, RayTMin, RayTMax, BoundsMin, BoundsMax);
#if DEBUG_MODE_SAMPLING_BASED
if (ClipHitT.x < ClipHitT.y)
{
float StepSize = CalcStepSizeLocal(RayDirection);
Jitter *= StepSize;
#if DEBUG_MODE == DEBUG_MODE_SAMPLE_BASED_MARCHING
FTrackingResult Result = SampleBasedMarch(RayOrigin, RayDirection, ClipHitT.x + Jitter, ClipHitT.y, StepSize);
#elif DEBUG_MODE == DEBUG_MODE_SAMPLE_BASED_MAJORANT
FTrackingResult Result = SampleBasedMajorantMarch(RayOrigin, RayDirection, ClipHitT.x + Jitter, ClipHitT.y, StepSize);
#elif DEBUG_MODE == DEBUG_MODE_SAMPLE_BASED_MAX_MAJORANT
FTrackingResult Result = SampleBasedMaxMajorantMarch(RayOrigin, RayDirection, ClipHitT.x + Jitter, ClipHitT.y, StepSize);
#else // DEBUG_MODE == DEBUG_MODE_SAMPLE_BASED_MAJORANT_DDA
int DistanceSamplingMethod = DEBUG_MODE_SAMPLE_BASED_MARCHING;
int CollisionSamplingMethod = 0;
FTrackingResult Result = DDABasedMajorantMarch(RayOrigin, RayDirection, ClipHitT.x, ClipHitT.y, DistanceSamplingMethod, CollisionSamplingMethod, RandSequence);
#endif
Transmittance = Result.Throughput;
}
//Radiance = Result.Emission;
Radiance = 1.0 - Transmittance;
#elif DEBUG_MODE_TRACKING_BASED
if (ClipHitT.x < ClipHitT.y)
{
FTrackingResult Result = (FTrackingResult) 0;
FMajorantData MajorantData = GetMajorantData(OrthoGridUniformBuffer.MajorantGridBuffer[0]);
float SigmaBar = MajorantData.Majorant;
int SPPCount = 1;
for (int Index = 0; Index < SPPCount; ++Index)
{
RandomSequence_Initialize(RandSequence, LinearIndex, View.StateFrameIndex * SPPCount + Index);
#if DEBUG_MODE == DEBUG_MODE_TRACKING_BASED_DELTA_TRACKING
FTrackingResult Sample = SpectralWeightedDeltaTracking(RayOrigin, RayDirection, ClipHitT.x, ClipHitT.y, SigmaBar, COLLISION_MODE_AVG_COMPONENT, RandSequence);
#elif DEBUG_MODE == DEBUG_MODE_TRACKING_BASED_RATIO_TRACKING
FTrackingResult Sample = RatioTracking(RayOrigin, RayDirection, ClipHitT.x, ClipHitT.y, SigmaBar, RandSequence);
#elif DEBUG_MODE == DEBUG_MODE_TRACKING_BASED_SPECTRAL_RATIO_TRACKING
FTrackingResult Sample = SpectralRatioTracking(RayOrigin, RayDirection, ClipHitT.x, ClipHitT.y, SigmaBar, RandSequence);
#else // DEBUG_MODE == DEBUG_MODE_TRACKING_BASED_MAJORANT_DDA
//int DistanceSamplingMethod = DEBUG_MODE_TRACKING_BASED_SPECTRAL_RATIO_TRACKING;
int DistanceSamplingMethod = DEBUG_MODE_TRACKING_BASED_DELTA_TRACKING;
int CollisionSamplingMethod = COLLISION_MODE_AVG_COMPONENT;
FTrackingResult Sample = DDABasedMajorantMarch(RayOrigin, RayDirection, ClipHitT.x, ClipHitT.y, DistanceSamplingMethod, CollisionSamplingMethod, RandSequence);
#endif
#if 0
// Faux direct-lighting ray
if (Sample.bIsScattering)
{
float3 ShadowRayOrigin = RayOrigin + RayDirection * Sample.Distance;
float3 LightDirection = normalize(float3(0, 0, 1));
float2 ClipShadowHitT = IntersectAABB(ShadowRayOrigin, LightDirection, 0, 10000, BoundsMin, BoundsMax);
if (ClipShadowHitT.y > ClipShadowHitT.x)
{
float3 Le = 10000;
float Phase = 1.0 / (4 * PI);
FTrackingResult Lighting = DDABasedMajorantMarch(ShadowRayOrigin, LightDirection, ClipShadowHitT.x, ClipShadowHitT.y, DEBUG_MODE_TRACKING_BASED_DELTA_TRACKING, COLLISION_MODE_RATIO_TRACKING, RandSequence);
Sample.Radiance = Le * Phase * Lighting.Throughput * Sample.Throughput;// / (Sample.Pdf * Lighting.Pdf);
}
Sample.Throughput = 0;
}
#endif
Result = AddTrackingResult(Result, Sample);
}
Result = DivideTrackingResult(Result, SPPCount);
float3 Le = 1;
Radiance = (Result.Radiance + (1.0 - Result.Throughput) * Le);
Transmittance = Result.Throughput;
}
#elif DEBUG_MODE == DEBUG_MODE_DDA_MARCHING
float FrustumRayTMin = RayTMin;
float FrustumRayTMax = RayTMax;
if (FrustumGridUniformBuffer.bUseFrustumGrid)
{
float ViewNearZ = min(ConvertFromDeviceZ(DeviceZ), FrustumGridUniformBuffer.NearPlaneDepth);
float DeviceNearZ = ConvertToDeviceZ(ViewNearZ);
float ViewFarZ = min(ConvertFromDeviceZ(DeviceZ), FrustumGridUniformBuffer.FarPlaneDepth);
float DeviceFarZ = ConvertToDeviceZ(ViewFarZ);
// TODO: Shared code is imprecise, in comparison with commented out frustum-trimmed ray. Investigate...
#if 0
float3 RayBegin = SvPositionToTranslatedWorld(float4(PixelCoord + 0.5, DeviceNearZ, 1));
float3 RayEnd = SvPositionToTranslatedWorld(float4(PixelCoord + 0.5, DeviceFarZ, 1));
float3 RayDirection = RayEnd - RayBegin;
FrustumRayTMin = 0.0;
FrustumRayTMax = length(RayDirection);
RayDirection /= FrustumRayTMax;
Transmittance = TraceFrustumVoxelGrid(RayBegin, RayDirection, FrustumRayTMin, FrustumRayTMax);
float RayTOffset = length(RayBegin - RayOrigin);
FrustumRayTMin += RayTOffset;
FrustumRayTMax += RayTOffset;
#else
float3 WorldRayOrigin = DFHackToFloat(SvPositionToWorld(float4(PixelCoord + 0.5, DeviceNearZ, 1)));
float3 WorldRayEnd = DFHackToFloat(SvPositionToWorld(float4(PixelCoord + 0.5, DeviceFarZ, 1)));
float3 WorldRayDirection = WorldRayEnd - WorldRayOrigin;
float WorldRayLength = length(WorldRayDirection);
WorldRayDirection /= WorldRayLength;
// Convert to view-space
float3 ViewRayOrigin = mul(float4(WorldRayOrigin, 1), FrustumGridUniformBuffer.WorldToView).xyz;
float3 ViewRayEnd = mul(float4(WorldRayEnd, 1), FrustumGridUniformBuffer.WorldToView).xyz;
float3 ViewRayDirection = ViewRayEnd - ViewRayOrigin;
float ViewRayLength = length(ViewRayDirection);
ViewRayDirection /= ViewRayLength;
// Convert to voxel-space
int3 VoxelDimensions = FrustumGridUniformBuffer.VoxelDimensions;
float NearPlaneDepth = FrustumGridUniformBuffer.NearPlaneDepth;
float FarPlaneDepth = FrustumGridUniformBuffer.FarPlaneDepth;
float TanHalfFOV = FrustumGridUniformBuffer.TanHalfFOV;
float3 VoxelRayOrigin = ViewToVoxel(ViewRayOrigin, VoxelDimensions, NearPlaneDepth, FarPlaneDepth, TanHalfFOV);
float3 VoxelRayEnd = ViewToVoxel(ViewRayOrigin + ViewRayDirection * ViewRayLength, VoxelDimensions, NearPlaneDepth, FarPlaneDepth, TanHalfFOV);
float3 VoxelRayDirection = VoxelRayEnd - VoxelRayOrigin;
float VoxelRayLength = length(VoxelRayDirection);
VoxelRayDirection /= VoxelRayLength;
float VoxelRayTMin = 0.0;
float VoxelRayTMax = VoxelRayLength;
// Traversal
{
MarchTopLevelFrustumVoxelGrid(
VoxelRayOrigin,
VoxelRayDirection,
VoxelRayTMin,
VoxelRayTMax,
WorldRayLength,
FrustumGridUniformBuffer.TopLevelFroxelGridResolution,
FrustumGridUniformBuffer.TopLevelFroxelGridBuffer,
FrustumGridUniformBuffer.ExtinctionFroxelGridBuffer,
Transmittance
);
}
float3 RayBegin = SvPositionToTranslatedWorld(float4(PixelCoord + 0.5, DeviceNearZ, 1));
float RayTOffset = length(RayBegin - RayOrigin);
FrustumRayTMin += RayTOffset;
FrustumRayTMax += RayTOffset;
#endif
}
if (OrthoGridUniformBuffer.bUseOrthoGrid)
{
if (FrustumGridUniformBuffer.bUseFrustumGrid && (FrustumRayTMin < FrustumRayTMax))
{
Transmittance *= TraceOrthoVoxelGrid(RayOrigin, RayDirection, RayTMin, FrustumRayTMin);
Transmittance *= TraceOrthoVoxelGrid(RayOrigin, RayDirection, FrustumRayTMax, RayTMax);
}
else
{
Transmittance = TraceOrthoVoxelGrid(RayOrigin, RayDirection, RayTMin, RayTMax);
}
}
#endif // DEBUG_MODE_DDA_MAJORANT
// Output..
RWLightingTexture[PixelCoord] += float4(Radiance, Luminance(Transmittance));
}