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

1075 lines
39 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#include "../Common.ush"
#include "../DeferredShadingCommon.ush"
#include "LumenCardCommon.ush"
#include "LumenTracingCommon.ush"
#include "LumenSoftwareRayTracing.ush"
#define RADIANCE_CACHE_DEPTH_TEST_SPHERE_PARALLAX 1
#include "LumenRadianceCacheCommon.ush"
#include "LumenScreenProbeCommon.ush"
#include "LumenScreenProbeTracingCommon.ush"
#include "LumenFloatQuantization.ush"
#include "LumenVisualizeTraces.ush"
#define IS_SSGI_SHADER 0
#include "../SHCommon.ush"
#include "LumenScreenTracing.ush"
#include "LumenHairTracing.ush"
#include "../SceneTextureParameters.ush"
#include "../HZB.ush"
#ifndef THREADGROUP_SIZE
#define THREADGROUP_SIZE 0
#endif
#define DEBUG_VISUALIZE_TRACE_TYPES 0
#ifdef ClearTracesCS
[numthreads(PROBE_THREADGROUP_SIZE_2D, PROBE_THREADGROUP_SIZE_2D, 1)]
void ClearTracesCS(
uint3 GroupId : SV_GroupID,
uint3 DispatchThreadId : SV_DispatchThreadID,
uint3 GroupThreadId : SV_GroupThreadID)
{
RWTraceRadiance[DispatchThreadId.xy] = 0.0f;
RWTraceHit[DispatchThreadId.xy] = EncodeProbeRayDistance(0.0f, false, false, /*bReachedRadianceCache*/ false);
}
#endif
float MaxTraceDistance;
float MaxHierarchicalScreenTraceIterations;
float RelativeDepthThickness;
float HistoryDepthTestRelativeThickness;
float NumThicknessStepsToDetermineCertainty;
uint MinimumTracingThreadOccupancy;
uint SkipFoliageHits;
uint SkipHairHits;
uint bSupportsHairScreenTraces;
float BiasForTracesFromHairPixels;
#ifdef ScreenProbeTraceScreenTexturesCS
[numthreads(PROBE_THREADGROUP_SIZE_2D, PROBE_THREADGROUP_SIZE_2D, 1)]
void ScreenProbeTraceScreenTexturesCS(
uint3 GroupId : SV_GroupID,
uint3 DispatchThreadId : SV_DispatchThreadID,
uint3 GroupThreadId : SV_GroupThreadID)
{
uint ProbeTracingResolution = ScreenProbeTracingOctahedronResolution;
uint2 ScreenProbeAtlasCoord = DispatchThreadId.xy / ProbeTracingResolution;
uint2 TraceTexelCoord = DispatchThreadId.xy - ScreenProbeAtlasCoord * ProbeTracingResolution;
uint ScreenProbeIndex = ScreenProbeAtlasCoord.y * ScreenProbeAtlasViewSize.x + ScreenProbeAtlasCoord.x;
uint2 ScreenProbeScreenPosition = GetScreenProbeScreenPosition(ScreenProbeIndex);
uint2 ScreenTileCoord = GetScreenTileCoord(ScreenProbeScreenPosition);
if (ScreenProbeIndex < GetNumScreenProbes()
&& ScreenProbeAtlasCoord.x < ScreenProbeAtlasViewSize.x
&& all(TraceTexelCoord < ProbeTracingResolution))
{
float2 ScreenUV = GetScreenUVFromScreenProbePosition(ScreenProbeScreenPosition);
float SceneDepth = GetScreenProbeDepth(ScreenProbeAtlasCoord);
uint2 TraceBufferCoord = GetTraceBufferCoord(ScreenProbeAtlasCoord, TraceTexelCoord);
if (ShouldTraceScreenProbeTexel(TraceBufferCoord, SceneDepth))
{
float3 TranslatedWorldPosition = GetTranslatedWorldPositionFromScreenUV(ScreenUV, SceneDepth);
float3 RayWorldDirection = 0;
float TraceDistance = MaxTraceDistance;
float ConeHalfAngle = 0;
GetScreenProbeTexelRay(
TraceBufferCoord,
TraceTexelCoord,
ScreenTileCoord,
TranslatedWorldPosition,
RayWorldDirection,
TraceDistance,
ConeHalfAngle);
bool bBackfaceRay;
float3 ScreenProbeNormal = GetScreenProbeNormalForBiasing(ScreenProbeAtlasCoord, RayWorldDirection, bBackfaceRay);
// Skip screen traces for material not supporting screen traced or for backfacing rays for two-sided foliage, as they immediately self-intersect and cause a feedback loop
if ((bBackfaceRay && GetScreenProbeIsTwoSidedFoliage(ScreenProbeAtlasCoord))
|| (GetScreenProbeIsHair(ScreenProbeAtlasCoord) && !bSupportsHairScreenTraces))
{
return;
}
float NormalBias = 0;
// Bias along the normal to avoid self-intersecting camera facing scene depth texels
// HZB traversal uses point filtering, so scene depth texels stick out from the plane they are representing
{
float2 CornerScreenUV = ScreenUV + .5f * View.BufferSizeAndInvSize.zw;
float2 CornerScreenPosition = (CornerScreenUV - View.ScreenPositionScaleBias.wz) / View.ScreenPositionScaleBias.xy;
float3 CornerTranslatedWorldPosition = mul(float4(GetScreenPositionForProjectionType(CornerScreenPosition, SceneDepth), SceneDepth, 1), View.ScreenToTranslatedWorld).xyz;
// Project texel corner onto normal
NormalBias = abs(dot(CornerTranslatedWorldPosition - TranslatedWorldPosition, ScreenProbeNormal)) * 2;
}
float3 TranslatedRayOrigin = TranslatedWorldPosition + NormalBias * ScreenProbeNormal;
if (GetScreenProbeIsHair(ScreenProbeAtlasCoord))
{
TranslatedRayOrigin += BiasForTracesFromHairPixels * RayWorldDirection;
}
float4 RayStartClip = mul(float4(TranslatedRayOrigin, 1.0f), View.TranslatedWorldToClip);
// Skip screen traces if the normal bias pushed our starting point off-screen, as TraceScreen() doesn't handle this
if (any(RayStartClip.xy < -RayStartClip.w) || any(RayStartClip.xy > RayStartClip.w))
{
return;
}
//@todo - this should be applied with the hit UV, not the origin, but still works for self-shadowing
float DepthThresholdScale = GetScreenTraceDepthThresholdScale(ScreenUV);
{
bool bValidRadianceCacheCoverage = false;
#if RADIANCE_CACHE
{
FRadianceCacheCoverage Coverage = GetRadianceCacheCoverage(TranslatedWorldPosition - DFHackToFloat(PrimaryView.PreViewTranslation), RayWorldDirection, 0);
if (Coverage.bValid)
{
TraceDistance = min(TraceDistance, Coverage.MinTraceDistanceBeforeInterpolation);
bValidRadianceCacheCoverage = true;
}
}
#endif
#if HIERARCHICAL_SCREEN_TRACING
bool bHit;
bool bUncertain;
float3 HitUVz;
float3 LastVisibleHitUVz;
float Unused;
TraceScreen(
TranslatedRayOrigin,
RayWorldDirection,
TraceDistance,
HZBUvFactorAndInvFactor,
MaxHierarchicalScreenTraceIterations,
RelativeDepthThickness * DepthThresholdScale,
NumThicknessStepsToDetermineCertainty,
MinimumTracingThreadOccupancy,
bHit,
bUncertain,
HitUVz,
LastVisibleHitUVz,
Unused);
#if USE_HAIRSTRANDS_SCREEN
if (!bHit)
{
bool Hair_bHit;
bool Hair_bUncertain;
float3 Hair_HitUVz;
float3 Hair_LastVisibleHitUVz;
TraceHairScreen(
TranslatedWorldPosition,
RayWorldDirection,
TraceDistance,
HZBUvFactorAndInvFactor,
MaxHierarchicalScreenTraceIterations,
RelativeDepthThickness * DepthThresholdScale,
NumThicknessStepsToDetermineCertainty,
Hair_bHit,
Hair_bUncertain,
Hair_HitUVz,
Hair_LastVisibleHitUVz,
Unused);
if (Hair_bHit && !Hair_bUncertain)
{
bHit = Hair_bHit;
HitUVz = Hair_HitUVz;
LastVisibleHitUVz = Hair_LastVisibleHitUVz;
bUncertain = Hair_bUncertain;
}
}
#endif
float Level = 1;
bool bWriteDepthOnMiss = true;
#else
uint NumSteps = 16;
float StartMipLevel = 1.0f;
float MaxScreenTraceFraction = .2f;
// Can only get decent quality out of fixed step count screen traces by limiting the trace distance
float MaxWorldTraceDistance = MaxScreenTraceFraction * 2.0 * GetScreenRayLengthMultiplierForProjectionType(SceneDepth).x;
TraceDistance = min(TraceDistance, MaxWorldTraceDistance);
uint2 NoiseCoord = ScreenProbeAtlasCoord * ProbeTracingResolution + TraceTexelCoord;
float StepOffset = InterleavedGradientNoise(NoiseCoord + 0.5f, 0);
float RayRoughness = .2f;
StepOffset = StepOffset - .9f;
FSSRTCastingSettings CastSettings = CreateDefaultCastSettings();
CastSettings.bStopWhenUncertain = true;
bool bHit = false;
float Level;
float3 HitUVz;
bool bRayWasClipped;
FSSRTRay Ray = InitScreenSpaceRayFromWorldSpace(
TranslatedWorldPosition, RayWorldDirection,
/* WorldTMax = */ TraceDistance,
/* SceneDepth = */ SceneDepth,
/* SlopeCompareToleranceScale */ 2.0f * DepthThresholdScale,
/* bExtendRayToScreenBorder = */ false,
/* out */ bRayWasClipped);
bool bUncertain;
float3 DebugOutput;
CastScreenSpaceRay(
FurthestHZBTexture, FurthestHZBTextureSampler,
StartMipLevel,
CastSettings,
Ray, RayRoughness, NumSteps, StepOffset,
HZBUvFactorAndInvFactor, false,
/* out */ DebugOutput,
/* out */ HitUVz,
/* out */ Level,
/* out */ bHit,
/* out */ bUncertain);
#if USE_HAIRSTRANDS_SCREEN
if (!bHit)
{
float3 Hair_DebugOutput;
float3 Hair_HitUVz;
float Hair_Level;
bool Hair_bHit = false;
bool Hair_bUncertain = bUncertain;
CastScreenSpaceRay(
HairStrands.HairOnlyDepthFurthestHZBTexture, FurthestHZBTextureSampler,
StartMipLevel,
CastSettings,
Ray, RayRoughness, NumSteps, StepOffset,
HZBUvFactorAndInvFactor, false,
/* out */ Hair_DebugOutput,
/* out */ Hair_HitUVz,
/* out */ Hair_Level,
/* out */ Hair_bHit,
/* out */ Hair_bUncertain);
if (Hair_bHit && !Hair_bUncertain)
{
DebugOutput = Hair_DebugOutput;
HitUVz = Hair_HitUVz;
Level = Hair_Level;
bHit = Hair_bHit;
bUncertain = Hair_bUncertain;
}
}
#endif
// CastScreenSpaceRay skips Mesh SDF tracing in a lot of places where it shouldn't, in particular missing thin occluders due to low NumSteps.
bool bWriteDepthOnMiss = !bUncertain;
#endif
bHit = bHit && !bUncertain;
bool bFastMoving = false;
// Make the ray slightly longer as distance reported from screen space traces isn't very reliable.
// This way we can skip tracing if we reached trace max distance. If not, then the next pass will correct for it using RT pullback bias.
float HitDistanceOffset = !bHit && !bUncertain ? 2.0f : 0.0f;
float HitDistance = min(sqrt(ComputeRayHitSqrDistance(TranslatedWorldPosition, HitUVz)) + HitDistanceOffset, MaxTraceDistance);
if (bHit)
{
float2 HitScreenUV = HitUVz.xy;
float2 HitScreenPosition = (HitUVz.xy - View.ScreenPositionScaleBias.wz) / View.ScreenPositionScaleBias.xy;
float HitDeviceZ = HitUVz.z;
float3 HitHistoryScreenPosition = GetHistoryScreenPosition(HitScreenPosition, HitScreenUV, HitDeviceZ);
float Vignette = min(ComputeHitVignetteFromScreenPos(HitScreenPosition), ComputeHitVignetteFromScreenPos(HitHistoryScreenPosition.xy));
float Noise = InterleavedGradientNoise(TraceBufferCoord + 0.5f, ScreenProbeGatherStateFrameIndex % 8);
// Skip reporting a hit if near the edge of the screen
if (Vignette < Noise)
{
bHit = false;
}
if (bHit)
{
// Calculate the expected depth of the pixel last frame
float PrevDeviceZ = HitHistoryScreenPosition.z;
// Lookup the actual depth at the same screen position last frame
float2 HitHistoryScreenUVForDepth = HitHistoryScreenPosition.xy * PrevScreenPositionScaleBiasForDepth.xy + PrevScreenPositionScaleBiasForDepth.zw;
float HistoryDeviceZ = Texture2DSampleLevel(HistorySceneDepth, GlobalPointClampedSampler, HitHistoryScreenUVForDepth, 0).x;
bHit = abs(HistoryDeviceZ - PrevDeviceZ) < HistoryDepthTestRelativeThickness * lerp(.5f, 2.0f, Noise);
}
if (bHit && (SkipFoliageHits > 0 || SkipHairHits > 0))
{
FLumenMaterialData LumenMaterial = ReadMaterialDataFromSceneTextures(HitScreenUV * View.BufferSizeAndInvSize.xy, HitScreenUV);
const bool bSkipFoliage = LumenMaterial.bHasBackfaceDiffuse && SkipFoliageHits > 0;
const bool bSkipHair = IsHair(LumenMaterial) && SkipHairHits > 0;
if (bSkipFoliage || bSkipHair)
{
bHit = false;
}
}
if (bHit)
{
float2 HitHistoryScreenUV = HitHistoryScreenPosition.xy * PrevScreenPositionScaleBias.xy + PrevScreenPositionScaleBias.zw;
HitHistoryScreenUV = clamp(HitHistoryScreenUV, PrevSceneColorBilinearUVMin, PrevSceneColorBilinearUVMax);
float3 Lighting = SampleScreenColor(PrevSceneColorTexture, GlobalPointClampedSampler, HitHistoryScreenUV).xyz * PrevSceneColorPreExposureCorrection;
Lighting += GetSkylightLeaking(RayWorldDirection, HitDistance) * View.PreExposure;
#if DEBUG_VISUALIZE_TRACE_TYPES
RWTraceRadiance[TraceBufferCoord] = float3(.5f, 0, 0) * View.PreExposure;
#else
RWTraceRadiance[TraceBufferCoord] = QuantizeForFloatRenderTarget(Lighting, Noise);
#endif
{
float HitSceneDepth = ConvertFromDeviceZ(HitUVz.z);
float3 HitTranslatedWorldPosition = mul(float4(GetScreenPositionForProjectionType(HitScreenPosition, HitSceneDepth), HitSceneDepth, 1), View.ScreenToTranslatedWorld).xyz;
//Note: this will be affected by TAA jitter, should use GetHistoryScreenPositionIncludingTAAJitter instead but costs too much
float3 HitWorldVelocity = HitTranslatedWorldPosition - GetPrevTranslatedWorldPosition(HitHistoryScreenPosition);
bFastMoving = IsTraceMoving(TranslatedWorldPosition, SceneDepth, ScreenProbeAtlasCoord, HitTranslatedWorldPosition, HitWorldVelocity);
}
}
}
if (bHit || bWriteDepthOnMiss)
{
WriteTraceHit(TraceBufferCoord, HitDistance, bHit, bFastMoving, /*bReachedTraceDistance*/ bValidRadianceCacheCoverage && HitDistance >= TraceDistance);
}
}
}
}
}
#endif
#ifdef ScreenProbeCompactTracesCS
RWBuffer<uint> RWCompactedTraceTexelAllocator;
RWBuffer<uint> RWCompactedTraceTexelData;
uint CullByDistanceFromCamera;
float CompactionTracingEndDistanceFromCamera;
float CompactionMaxTraceDistance;
uint CompactForSkyApply;
groupshared uint SharedGlobalTraceTexelStartOffset;
#if WAVE_OPS
groupshared uint SharedGroupSum;
#else
//@todo - ordered compaction for non-wave ops path
groupshared uint SharedTraceTexelAllocator;
groupshared uint SharedTraceTexels[THREADGROUP_SIZE * THREADGROUP_SIZE];
#endif
#if COMPILER_SUPPORTS_WAVE_SIZE && WAVE_OPS
WAVESIZE(32)
#endif
[numthreads(THREADGROUP_SIZE, THREADGROUP_SIZE, 1)]
void ScreenProbeCompactTracesCS(
uint3 GroupId : SV_GroupID,
uint3 GroupThreadId : SV_GroupThreadID,
uint3 DispatchThreadId : SV_DispatchThreadID)
{
const uint LinearThreadIndex = GroupThreadId.y * THREADGROUP_SIZE + GroupThreadId.x;
if (LinearThreadIndex == 0)
#if WAVE_OPS
{
SharedGroupSum = 0;
}
#else
{
SharedTraceTexelAllocator = 0;
}
GroupMemoryBarrierWithGroupSync();
#endif
bool bTraceValid = false;
uint TraceTexelForThisThread = 0;
uint2 ScreenProbeAtlasCoord = DispatchThreadId.xy / ScreenProbeTracingOctahedronResolution;
uint2 TraceTexelCoord = DispatchThreadId.xy - ScreenProbeAtlasCoord * ScreenProbeTracingOctahedronResolution;
uint ScreenProbeIndex = ScreenProbeAtlasCoord.y * ScreenProbeAtlasViewSize.x + ScreenProbeAtlasCoord.x;
if (ScreenProbeIndex < GetNumScreenProbes()
&& ScreenProbeAtlasCoord.x < ScreenProbeAtlasViewSize.x
&& all(TraceTexelCoord < ScreenProbeTracingOctahedronResolution))
{
float SceneDepth = GetScreenProbeDepth(ScreenProbeAtlasCoord);
uint2 TraceBufferCoord = GetTraceBufferCoord(ScreenProbeAtlasCoord, TraceTexelCoord);
FProbeRayDistance ProbeRayDistance = ReadTraceHit(TraceBufferCoord);
bool bAcceptTrace = !ProbeRayDistance.bHit && (!ProbeRayDistance.bReachedRadianceCache || CompactForSkyApply);
if (ShouldTraceScreenProbeTexel(TraceBufferCoord, SceneDepth)
&& bAcceptTrace
&& (CullByDistanceFromCamera == 0 || SceneDepth < CompactionTracingEndDistanceFromCamera)
&& ProbeRayDistance.HitDistance < CompactionMaxTraceDistance)
{
#if WAVE_OPS
{
bTraceValid = true;
TraceTexelForThisThread = EncodeScreenProbeTraceTexel(ScreenProbeIndex, TraceTexelCoord);
}
#else
{
uint SharedTexelOffset;
InterlockedAdd(SharedTraceTexelAllocator, 1, SharedTexelOffset);
SharedTraceTexels[SharedTexelOffset] = EncodeScreenProbeTraceTexel(ScreenProbeIndex, TraceTexelCoord);
}
#endif
}
}
GroupMemoryBarrierWithGroupSync();
#if WAVE_OPS
{
const uint LastLaneIndex = WaveGetLaneCount() - 1;
const uint LaneIndex = WaveGetLaneIndex();
const uint OffsetInWave = WavePrefixCountBits(bTraceValid);
uint OffsetInGroup = 0;
if (LaneIndex == LastLaneIndex)
{
const uint ThisWaveSum = OffsetInWave + (bTraceValid ? 1 : 0);
InterlockedAdd(SharedGroupSum, ThisWaveSum, OffsetInGroup);
}
OffsetInGroup = WaveReadLaneAt(OffsetInGroup, LastLaneIndex) + OffsetInWave;
GroupMemoryBarrierWithGroupSync();
// Allocate this group's compacted traces from the global allocator
if (LinearThreadIndex == 0)
{
InterlockedAdd(RWCompactedTraceTexelAllocator[0], SharedGroupSum, SharedGlobalTraceTexelStartOffset);
}
GroupMemoryBarrierWithGroupSync();
if (bTraceValid)
{
RWCompactedTraceTexelData[SharedGlobalTraceTexelStartOffset + OffsetInGroup] = TraceTexelForThisThread;
}
}
#else
{
if (LinearThreadIndex == 0)
{
InterlockedAdd(RWCompactedTraceTexelAllocator[0], SharedTraceTexelAllocator, SharedGlobalTraceTexelStartOffset);
}
GroupMemoryBarrierWithGroupSync();
if (LinearThreadIndex < SharedTraceTexelAllocator)
{
RWCompactedTraceTexelData[SharedGlobalTraceTexelStartOffset + LinearThreadIndex] = SharedTraceTexels[LinearThreadIndex];
}
}
#endif
}
#endif
#ifdef SetupCompactedTracesIndirectArgsCS
RWBuffer<uint> RWScreenProbeCompactTracingIndirectArgs;
[numthreads(1, 1, 1)]
void SetupCompactedTracesIndirectArgsCS(
uint3 GroupId : SV_GroupID,
uint3 DispatchThreadId : SV_DispatchThreadID,
uint3 GroupThreadId : SV_GroupThreadID)
{
// THREADGROUP_SIZE_64
WriteDispatchIndirectArgs(RWScreenProbeCompactTracingIndirectArgs, 0, DivideAndRoundUp64(CompactedTraceTexelAllocator[0]), 1, 1);
// THREADGROUP_SIZE_32
WriteDispatchIndirectArgs(RWScreenProbeCompactTracingIndirectArgs, 1, DivideAndRoundUp32(CompactedTraceTexelAllocator[0]), 1, 1);
// LightSample THREADGROUP_SIZE_64
WriteDispatchIndirectArgs(RWScreenProbeCompactTracingIndirectArgs, 2, DivideAndRoundUp64(CompactedTraceTexelAllocator[1]), 1, 1);
// LightSample THREADGROUP_SIZE_32
WriteDispatchIndirectArgs(RWScreenProbeCompactTracingIndirectArgs, 3, DivideAndRoundUp32(CompactedTraceTexelAllocator[1]), 1, 1);
}
#endif
uint CardGridPixelSizeShift;
float3 CardGridZParams;
uint3 CullGridSize;
uint ComputeCardGridCellIndex(uint2 PixelPos, float SceneDepth)
{
uint ZSlice = (uint)(max(0, log2(SceneDepth * CardGridZParams.x + CardGridZParams.y) * CardGridZParams.z));
ZSlice = min(ZSlice, (uint)(CullGridSize.z - 1));
uint3 GridCoordinate = uint3(PixelPos >> CardGridPixelSizeShift, ZSlice);
uint GridIndex = (GridCoordinate.z * CullGridSize.y + GridCoordinate.y) * CullGridSize.x + GridCoordinate.x;
return GridIndex;
}
float StepFactor;
float MinSampleRadius;
float MinTraceDistance;
float SurfaceBias;
float CardInterpolateInfluenceRadius;
float CardTraceEndDistanceFromCamera;
float MaxMeshSDFTraceDistance;
void TraceMeshSDFs(
uint2 ScreenProbeAtlasCoord,
uint2 TraceTexelCoord,
uint ScreenProbeIndex)
{
uint2 ScreenProbeScreenPosition = GetScreenProbeScreenPosition(ScreenProbeIndex);
uint2 ScreenTileCoord = GetScreenTileCoord(ScreenProbeScreenPosition);
uint ProbeTracingResolution = ScreenProbeTracingOctahedronResolution;
{
float2 ScreenUV = GetScreenUVFromScreenProbePosition(ScreenProbeScreenPosition);
float SceneDepth = GetScreenProbeDepth(ScreenProbeAtlasCoord);
uint2 TraceBufferCoord = GetTraceBufferCoord(ScreenProbeAtlasCoord, TraceTexelCoord);
float3 TranslatedWorldPosition = GetTranslatedWorldPositionFromScreenUV(ScreenUV, SceneDepth);
float3 WorldPosition = TranslatedWorldPosition - DFHackToFloat(PrimaryView.PreViewTranslation);
float TraceHitDistance = DecodeProbeRayDistance(RWTraceHit[TraceBufferCoord]).HitDistance;
bool bHit = false;
bool bMoving = false;
{
float3 RayWorldDirection = 0;
float TraceDistance = MaxTraceDistance;
float ConeHalfAngle = 0;
GetScreenProbeTexelRay(
TraceBufferCoord,
TraceTexelCoord,
ScreenTileCoord,
TranslatedWorldPosition,
RayWorldDirection,
TraceDistance,
ConeHalfAngle);
float3 TranslatedSamplePosition = TranslatedWorldPosition + SurfaceBias * RayWorldDirection;
TranslatedSamplePosition += SurfaceBias * GetScreenProbeNormalForBiasing(ScreenProbeAtlasCoord, RayWorldDirection);
float3 SamplePosition = TranslatedSamplePosition - DFHackToFloat(PrimaryView.PreViewTranslation);
FConeTraceInput TraceInput;
TraceInput.Setup(SamplePosition, TranslatedSamplePosition, RayWorldDirection, ConeHalfAngle, MinSampleRadius, max(MinTraceDistance, TraceHitDistance - SurfaceBias * 2), MaxTraceDistance, StepFactor);
TraceInput.VoxelTraceStartDistance = min(MaxMeshSDFTraceDistance, TraceDistance);
TraceInput.bDitheredTransparency = true;
TraceInput.DitherScreenCoord = ScreenTileCoord * ProbeTracingResolution + TraceTexelCoord;
uint CardGridCellIndex = ComputeCardGridCellIndex(ScreenProbeScreenPosition - PrimaryView.ViewRectMinAndSize.xy, SceneDepth);
TraceInput.NumMeshSDFs = NumGridCulledMeshSDFObjects[CardGridCellIndex];
TraceInput.MeshSDFStartOffset = GridCulledMeshSDFObjectStartOffsetArray[CardGridCellIndex];
TraceInput.CardInterpolateInfluenceRadius = CardInterpolateInfluenceRadius;
TraceInput.bCalculateHitVelocity = true;
FConeTraceResult TraceResult;
ConeTraceLumenSceneCards(TraceInput, TraceResult);
TraceInput.NumHeightfields = NumGridCulledHeightfieldObjects[CardGridCellIndex];
TraceInput.HeightfieldStartOffset = GridCulledHeightfieldObjectStartOffsetArray[CardGridCellIndex];
ConeTraceLumenSceneHeightfields(TraceInput, TraceResult);
// Trace against hair voxel structure, if enabled and not already done by other tracing method
#if USE_HAIRSTRANDS_VOXEL
{
float HairTraceDistance = min(TraceInput.MaxTraceDistance, TraceResult.OpaqueHitDistance);
bool bHairHit;
float HairTransparency;
float HairHitT;
TraceHairVoxels(
ScreenProbeScreenPosition,
SceneDepth,
// Use (Translated)WorldPosition instead of SamplePosition, as the bias is too strong otherwise. This is not an issue as
// the voxel structure does not cause any self shadowing issue
TranslatedWorldPosition,
RayWorldDirection,
HairTraceDistance,
false,
bHairHit,
HairTransparency,
HairHitT);
if (bHairHit && HairHitT < HairTraceDistance)
{
TraceResult.Lighting *= HairTransparency;
TraceResult.Transparency *= HairTransparency;
TraceResult.OpaqueHitDistance = min(HairHitT, TraceResult.OpaqueHitDistance);
}
}
#endif
float3 Lighting = TraceResult.Lighting;
float Transparency = TraceResult.Transparency;
float OpaqueHitDistance = TraceResult.OpaqueHitDistance;
{
float3 HitWorldPosition = SamplePosition + RayWorldDirection * OpaqueHitDistance;
bMoving = IsTraceMoving(WorldPosition, SceneDepth, ScreenProbeAtlasCoord, HitWorldPosition, TraceResult.WorldVelocity);
}
float DistanceFromViewpoint = GetDistanceToCameraFromViewVector(DFHackToFloat(PrimaryView.WorldCameraOrigin) - WorldPosition);
float DistanceFade = saturate(6 * DistanceFromViewpoint / CardTraceEndDistanceFromCamera - 5);
float CoverageForFog = saturate(1.0 - Transparency); // LUMEN_TODO single evaluation for now even if we can hit translucent hair above.
Transparency = lerp(Transparency, 1, DistanceFade);
if (Transparency < InterleavedGradientNoise(TraceBufferCoord + 0.5f, 0))
{
bHit = true;
}
if (bHit)
{
Lighting *= 1 - DistanceFade;
Lighting += GetSkylightLeaking(RayWorldDirection, OpaqueHitDistance);
Lighting *= View.PreExposure;
if (SampleHeightFog > 0)
{
float3 OriginToCollider = RayWorldDirection * OpaqueHitDistance;
Lighting.rgb = GetFogOnLuminance(Lighting.rgb, CoverageForFog, SamplePosition, RayWorldDirection, OpaqueHitDistance);
}
#if DEBUG_VISUALIZE_TRACE_TYPES
RWTraceRadiance[TraceBufferCoord] = float3(0, .5f, 0) * View.PreExposure;
#else
RWTraceRadiance[TraceBufferCoord] = Lighting;
#endif
}
TraceHitDistance = OpaqueHitDistance + length(WorldPosition - SamplePosition);
}
//@todo - set bMoving based on hit object velocity
WriteTraceHit(TraceBufferCoord, min(TraceHitDistance, MaxTraceDistance), bHit, bMoving, /*bReachedRadianceCache*/ false);
}
}
#if THREADGROUP_SIZE_32
#define PROBE_TRACE_THREADGROUP_SIZE_1D 32
#else
#define PROBE_TRACE_THREADGROUP_SIZE_1D 64
#endif
#ifdef ScreenProbeTraceMeshSDFsCS
[numthreads(PROBE_TRACE_THREADGROUP_SIZE_1D, 1, 1)]
void ScreenProbeTraceMeshSDFsCS(
uint3 GroupId : SV_GroupID,
uint3 DispatchThreadId : SV_DispatchThreadID,
uint3 GroupThreadId : SV_GroupThreadID)
{
if (DispatchThreadId.x < CompactedTraceTexelAllocator[0])
{
uint ScreenProbeIndex;
uint2 TraceTexelCoord;
ReadTraceTexel(DispatchThreadId.x, ScreenProbeIndex, TraceTexelCoord);
uint2 ScreenProbeAtlasCoord = uint2(ScreenProbeIndex % ScreenProbeAtlasViewSize.x, ScreenProbeIndex / ScreenProbeAtlasViewSize.x);
TraceMeshSDFs(ScreenProbeAtlasCoord, TraceTexelCoord, ScreenProbeIndex);
}
}
#endif
#ifdef ScreenProbeTraceVoxelsCS
void TraceVoxels(
uint2 ScreenProbeAtlasCoord,
uint2 TraceTexelCoord,
uint ScreenProbeIndex)
{
uint2 ScreenProbeScreenPosition = GetScreenProbeScreenPosition(ScreenProbeIndex);
uint2 ScreenTileCoord = GetScreenTileCoord(ScreenProbeScreenPosition);
uint ProbeTracingResolution = ScreenProbeTracingOctahedronResolution;
uint2 TraceBufferCoord = GetTraceBufferCoord(ScreenProbeAtlasCoord, TraceTexelCoord);
float TraceHitDistance = DecodeProbeRayDistance(RWTraceHit[TraceBufferCoord]).HitDistance;
{
float2 ScreenUV = GetScreenUVFromScreenProbePosition(ScreenProbeScreenPosition);
float SceneDepth = GetScreenProbeDepth(ScreenProbeAtlasCoord);
bool bHit = false;
{
float3 TranslatedWorldPosition = GetTranslatedWorldPositionFromScreenUV(ScreenUV, SceneDepth);
float3 WorldPosition = TranslatedWorldPosition - DFHackToFloat(PrimaryView.PreViewTranslation);
float3 RayWorldDirection = 0;
float TraceDistance = MaxTraceDistance;
float ConeHalfAngle = 0;
GetScreenProbeTexelRay(
TraceBufferCoord,
TraceTexelCoord,
ScreenTileCoord,
TranslatedWorldPosition,
RayWorldDirection,
TraceDistance,
ConeHalfAngle);
float3 TranslatedSamplePosition = TranslatedWorldPosition + SurfaceBias * RayWorldDirection;
TranslatedSamplePosition += SurfaceBias * GetScreenProbeNormalForBiasing(ScreenProbeAtlasCoord, RayWorldDirection);
float3 SamplePosition = TranslatedSamplePosition - DFHackToFloat(PrimaryView.PreViewTranslation);
FRadianceCacheCoverage Coverage;
Coverage.bValid = false;
#if RADIANCE_CACHE
Coverage = GetRadianceCacheCoverage(WorldPosition, RayWorldDirection, 0);
if (Coverage.bValid)
{
TraceDistance = min(TraceDistance, Coverage.MinTraceDistanceBeforeInterpolation);
}
#endif
FConeTraceInput TraceInput;
TraceInput.Setup(SamplePosition, TranslatedSamplePosition, RayWorldDirection, ConeHalfAngle, MinSampleRadius, MinTraceDistance, TraceDistance, StepFactor);
TraceInput.VoxelTraceStartDistance = max(MinTraceDistance, TraceHitDistance - SurfaceBias * 2);
TraceInput.bDitheredTransparency = true;
TraceInput.DitherScreenCoord = ScreenTileCoord * ProbeTracingResolution + TraceTexelCoord;
FConeTraceResult TraceResult = (FConeTraceResult)0;
TraceResult.Lighting = 0;
TraceResult.Transparency = 1;
TraceResult.OpaqueHitDistance = TraceInput.MaxTraceDistance;
#if TRACE_VOXELS
ConeTraceLumenSceneVoxels(TraceInput, TraceResult);
#endif
// Trace against hair voxel structure, if enabled and not already done by other tracing method
#if USE_HAIRSTRANDS_VOXEL
{
float HairTraceDistance = min(TraceDistance, TraceResult.OpaqueHitDistance);
bool bHairHit;
float HairTransparency;
float HairHitT;
TraceHairVoxels(
ScreenProbeScreenPosition,
SceneDepth,
// Use (Translated)WorldPosition instead of SamplePosition, as the bias is too strong otherwise. This is not an issue as
// the voxel structure does not cause any self shadowing issue
TranslatedWorldPosition,
RayWorldDirection,
HairTraceDistance,
false,
bHairHit,
HairTransparency,
HairHitT);
if (bHairHit && HairHitT < HairTraceDistance)
{
TraceResult.Lighting *= HairTransparency;
TraceResult.Transparency *= HairTransparency;
TraceResult.OpaqueHitDistance = min(HairHitT, TraceResult.OpaqueHitDistance);
}
}
#endif
float TraceHitDistanceForSkyLeaking = TraceHitDistance;
if (TraceResult.Transparency <= .5f)
{
// Self intersection from grazing angle traces causes noise that can't be removed by the spatial filter
#define USE_VOXEL_TRACE_HIT_DISTANCE 0
#if USE_VOXEL_TRACE_HIT_DISTANCE
TraceHitDistance = TraceResult.OpaqueHitDistance;
#else
TraceHitDistance = TraceDistance;
#endif
TraceHitDistanceForSkyLeaking = TraceResult.OpaqueHitDistance;
bHit = true;
}
float CoverageForFog = bHit ? 1.0f - TraceResult.Transparency : 0.0f;
#if RADIANCE_CACHE
if (Coverage.bValid)
{
if (TraceResult.Transparency > .5f)
{
// We don't store depth of Radiance Cache hits
TraceHitDistance = MaxTraceDistance;
TraceHitDistanceForSkyLeaking = MaxTraceDistance;
}
#if SCREEN_PROBE_EXTRA_AO
// AO may need hit distances further than what screen probes trace
SampleRadianceCacheAndApply(Coverage, WorldPosition, RayWorldDirection, ConeHalfAngle, /*RandomScalarForStochasticIterpolation*/ 0.5f, TraceResult.Lighting, TraceResult.Transparency, TraceHitDistance);
#else
SampleRadianceCacheAndApply(Coverage, WorldPosition, RayWorldDirection, ConeHalfAngle, /*RandomScalarForStochasticIterpolation*/ 0.5f, TraceResult.Lighting, TraceResult.Transparency);
#endif
}
else
#endif
{
if (TraceResult.Transparency > .5f)
{
TraceHitDistance = MaxTraceDistance;
TraceHitDistanceForSkyLeaking = MaxTraceDistance;
}
ApplySkylightToTraceResult(RayWorldDirection, TraceResult);
if (TraceHitDistance >= GetProbeMaxHitDistance())
{
TraceHitDistance = MaxTraceDistance;
}
}
TraceResult.Lighting += GetSkylightLeaking(RayWorldDirection, TraceHitDistanceForSkyLeaking);
TraceResult.Lighting *= View.PreExposure;
if (SampleHeightFog > 0)
{
float3 OriginToCollider = RayWorldDirection * TraceHitDistance;
TraceResult.Lighting.rgb = GetFogOnLuminance(TraceResult.Lighting.rgb, CoverageForFog, TranslatedWorldPosition, RayWorldDirection, TraceHitDistance);
}
#if DEBUG_VISUALIZE_TRACE_TYPES
RWTraceRadiance[TraceBufferCoord] = float3(0, 0, .5f) * View.PreExposure;
#else
RWTraceRadiance[TraceBufferCoord] = TraceResult.Lighting;
#endif
}
WriteTraceHit(TraceBufferCoord, TraceHitDistance, bHit, false, /*bReachedRadianceCache*/ false);
}
}
[numthreads(PROBE_TRACE_THREADGROUP_SIZE_1D, 1, 1)]
void ScreenProbeTraceVoxelsCS(
uint3 GroupId : SV_GroupID,
uint3 DispatchThreadId : SV_DispatchThreadID,
uint3 GroupThreadId : SV_GroupThreadID)
{
if (DispatchThreadId.x < CompactedTraceTexelAllocator[0])
{
uint ScreenProbeIndex;
uint2 TraceTexelCoord;
ReadTraceTexel(DispatchThreadId.x, ScreenProbeIndex, TraceTexelCoord);
uint2 ScreenProbeAtlasCoord = uint2(ScreenProbeIndex % ScreenProbeAtlasViewSize.x, ScreenProbeIndex / ScreenProbeAtlasViewSize.x);
TraceVoxels(ScreenProbeAtlasCoord, TraceTexelCoord, ScreenProbeIndex);
}
}
#endif
#ifdef ScreenProbeSetupVisualizeTraces
Texture2D TraceRadiance;
[numthreads(THREADGROUP_SIZE, THREADGROUP_SIZE, 1)]
void ScreenProbeSetupVisualizeTraces(
uint3 GroupId : SV_GroupID,
uint3 DispatchThreadId : SV_DispatchThreadID,
uint3 GroupThreadId : SV_GroupThreadID)
{
uint2 QueryProbeScreenPosition = View.CursorPosition.x >= 0 ? View.CursorPosition * View.ViewResolutionFraction : ScreenProbeViewSize / 2 * ScreenProbeDownsampleFactor;
uint2 ScreenTileCoord = clamp(GetScreenTileCoord(QueryProbeScreenPosition), (uint2)0, ScreenProbeViewSize - 1);
uint2 ScreenProbeAtlasCoord = ScreenTileCoord;
uint2 UniformScreenProbeScreenPosition = GetUniformScreenProbeScreenPosition(ScreenTileCoord);
uint2 ScreenProbeScreenPosition = UniformScreenProbeScreenPosition;
{
float MinDistance = length(QueryProbeScreenPosition - UniformScreenProbeScreenPosition);
uint NumAdaptiveProbes = ScreenTileAdaptiveProbeHeader[ScreenTileCoord];
for (uint AdaptiveProbeListIndex = 0; AdaptiveProbeListIndex < NumAdaptiveProbes; AdaptiveProbeListIndex++)
{
uint2 AdaptiveProbeCoord = GetAdaptiveProbeCoord(ScreenTileCoord, AdaptiveProbeListIndex);
uint AdaptiveProbeIndex = ScreenTileAdaptiveProbeIndices[AdaptiveProbeCoord];
uint ScreenProbeIndex = AdaptiveProbeIndex + NumUniformScreenProbes;
uint2 AdaptiveProbeScreenPosition = GetScreenProbeScreenPosition(ScreenProbeIndex);
float AdaptiveProbeDistance = length(QueryProbeScreenPosition - AdaptiveProbeScreenPosition);
if (AdaptiveProbeDistance < MinDistance)
{
MinDistance = AdaptiveProbeDistance;
ScreenProbeAtlasCoord = uint2(ScreenProbeIndex % ScreenProbeAtlasViewSize.x, ScreenProbeIndex / ScreenProbeAtlasViewSize.x);
ScreenProbeScreenPosition = AdaptiveProbeScreenPosition;
}
}
}
float2 ProbeScreenUV = GetScreenUVFromScreenProbePosition(ScreenProbeScreenPosition);
float ProbeSceneDepth = GetScreenProbeDepth(ScreenProbeAtlasCoord);
float3 ProbeWorldPosition = 0;
float3 ProbeTranslatedWorldPosition = 0;
if (ProbeSceneDepth > 0.0f)
{
ProbeWorldPosition = GetWorldPositionFromScreenUV(ProbeScreenUV, ProbeSceneDepth);
ProbeTranslatedWorldPosition = GetTranslatedWorldPositionFromScreenUV(ProbeScreenUV, ProbeSceneDepth);
}
float3 PreViewTranslation = DFDemote(MakeDFVector3(View.PreViewTranslationHigh, View.PreViewTranslationLow));
{
float2 CursorSceneBufferUV = SvPositionToBufferUV(float4(floor((View.CursorPosition) * View.ViewResolutionFraction) + .5f, 0, 0)).xy;
float2 CursorScreenPosition = (CursorSceneBufferUV - View.ScreenPositionScaleBias.wz) / View.ScreenPositionScaleBias.xy;
float CursorSceneDepth = ConvertFromDeviceZ(Texture2DSampleLevel(SceneTexturesStruct.SceneDepthTexture, SceneTexturesStruct_SceneDepthTextureSampler, CursorSceneBufferUV, 0).r);
float3 CursorTranslatedWorldPosition = mul(float4(GetScreenPositionForProjectionType(CursorScreenPosition, CursorSceneDepth), CursorSceneDepth, 1), View.ScreenToTranslatedWorld).xyz;
float3 CursorWorldPosition = CursorTranslatedWorldPosition - PreViewTranslation;
}
uint2 TraceTexelCoord = DispatchThreadId.xy;
#define VISUALIZE_SCENE_DEPTH_TEXEL 0
#if VISUALIZE_SCENE_DEPTH_TEXEL
float3 Corner00WorldPosition;
{
float2 CornerSceneBufferUV = ProbeScreenUV - .5f * View.BufferSizeAndInvSize.zw;
float2 CornerScreenPosition = (CornerSceneBufferUV - View.ScreenPositionScaleBias.wz) / View.ScreenPositionScaleBias.xy;
Corner00WorldPosition = mul(float4(GetScreenPositionForProjectionType(CornerScreenPosition, ProbeSceneDepth), ProbeSceneDepth, 1), View.ScreenToTranslatedWorld).xyz - PreViewTranslation;
}
float3 Corner01WorldPosition;
{
float2 CornerSceneBufferUV = ProbeScreenUV + float2(-.5f, .5f) * View.BufferSizeAndInvSize.zw;
float2 CornerScreenPosition = (CornerSceneBufferUV - View.ScreenPositionScaleBias.wz) / View.ScreenPositionScaleBias.xy;
Corner01WorldPosition = mul(float4(GetScreenPositionForProjectionType(CornerScreenPosition, ProbeSceneDepth), ProbeSceneDepth, 1), View.ScreenToTranslatedWorld).xyz - PreViewTranslation;
}
float3 Corner10WorldPosition;
{
float2 CornerSceneBufferUV = ProbeScreenUV + float2(.5f, -.5f) * View.BufferSizeAndInvSize.zw;
float2 CornerScreenPosition = (CornerSceneBufferUV - View.ScreenPositionScaleBias.wz) / View.ScreenPositionScaleBias.xy;
Corner10WorldPosition = mul(float4(GetScreenPositionForProjectionType(CornerScreenPosition, ProbeSceneDepth), ProbeSceneDepth, 1), View.ScreenToTranslatedWorld).xyz - PreViewTranslation;
}
float3 Corner11WorldPosition;
{
float2 CornerSceneBufferUV = ProbeScreenUV + float2(.5f, .5f) * View.BufferSizeAndInvSize.zw;
float2 CornerScreenPosition = (CornerSceneBufferUV - View.ScreenPositionScaleBias.wz) / View.ScreenPositionScaleBias.xy;
Corner11WorldPosition = mul(float4(GetScreenPositionForProjectionType(CornerScreenPosition, ProbeSceneDepth), ProbeSceneDepth, 1), View.ScreenToTranslatedWorld).xyz - PreViewTranslation;
}
if (DispatchThreadId.x == 0 && DispatchThreadId.y == 0)
{
WriteTraceForVisualization(0, Corner00WorldPosition, (Corner10WorldPosition - Corner00WorldPosition), 1, float3(1, 0, 0));
WriteTraceForVisualization(1, Corner10WorldPosition, (Corner11WorldPosition - Corner10WorldPosition), 1, float3(1, 0, 0));
WriteTraceForVisualization(2, Corner11WorldPosition, (Corner01WorldPosition - Corner11WorldPosition), 1, float3(1, 0, 0));
WriteTraceForVisualization(3, Corner01WorldPosition, (Corner00WorldPosition - Corner01WorldPosition), 1, float3(1, 0, 0));
}
float2 CornerScreenUV = ProbeScreenUV + .5f * View.BufferSizeAndInvSize.zw;
float2 CornerScreenPosition = (CornerScreenUV - View.ScreenPositionScaleBias.wz) / View.ScreenPositionScaleBias.xy;
float3 CornerTranslatedWorldPosition = mul(float4(GetScreenPositionForProjectionType(CornerScreenPosition, ProbeSceneDepth), ProbeSceneDepth, 1), View.ScreenToTranslatedWorld).xyz;
float3 ScreenProbeNormal = GetScreenProbeNormalForBiasing(ScreenProbeAtlasCoord, CornerTranslatedWorldPosition - ProbeTranslatedWorldPosition);
float NormalBias = abs(dot(CornerTranslatedWorldPosition - ProbeTranslatedWorldPosition, ScreenProbeNormal));
if (DispatchThreadId.x == 0 && DispatchThreadId.y == 0)
{
// Diagonal that will be projected to measure texel size
WriteTraceForVisualization(4, ProbeWorldPosition, (CornerTranslatedWorldPosition - ProbeTranslatedWorldPosition), 0, float3(1, 0, 1));
// Probe normal from GBuffer
WriteTraceForVisualization(5, ProbeWorldPosition, 1 * ScreenProbeNormal, 0, float3(0, 0, 1));
// Calculated bias along probe normal needed to avoid screen trace self-intersection
WriteTraceForVisualization(6, ProbeWorldPosition, NormalBias * ScreenProbeNormal, 1, float3(1, 1, 0));
}
uint TraceIndex = TraceTexelCoord.y * ScreenProbeTracingOctahedronResolution + TraceTexelCoord.x + 7;
if (TraceIndex < ScreenProbeTracingOctahedronResolution * ScreenProbeTracingOctahedronResolution)
{
WriteTraceForVisualization(TraceIndex, 0, 0, 0, float3(1, 0, 0));
}
#else
if (all(TraceTexelCoord < ScreenProbeTracingOctahedronResolution))
{
uint2 TraceBufferCoord = GetTraceBufferCoord(ScreenProbeAtlasCoord, TraceTexelCoord);
FProbeRayDistance ProbeRayDistance = DecodeProbeRayDistance(TraceHit[TraceBufferCoord].x);
bool bHit = ProbeRayDistance.bHit;
float TraceHitDistance = ProbeRayDistance.HitDistance;
float3 RayWorldDirection = 0;
float RefinementRay = 0;
if (ProbeSceneDepth > 0.0f)
{
float2 ProbeUV;
float ConeHalfAngle;
GetProbeTracingUV(TraceBufferCoord, TraceTexelCoord, GetProbeTexelCenter(ScreenTileCoord), 1, ProbeUV, ConeHalfAngle);
RayWorldDirection = EquiAreaSphericalMapping(ProbeUV);
float BaseAngle = acosFast(1.0f - 1.0f / (float)(ScreenProbeTracingOctahedronResolution * ScreenProbeTracingOctahedronResolution));
RefinementRay = 1 - ConeHalfAngle / BaseAngle;
}
uint TraceIndex = TraceTexelCoord.y * ScreenProbeTracingOctahedronResolution + TraceTexelCoord.x;
float3 Radiance = TraceRadiance[TraceBufferCoord].xyz;
// Visualize incorrect self-intersections
if (bHit && TraceHitDistance < 1)
{
TraceHitDistance = 5;
Radiance = float3(1, 0, 0);
}
bool bVisualizeRefinementRays = false;
WriteTraceForVisualization(TraceIndex, ProbeWorldPosition, RayWorldDirection, TraceHitDistance, select(bVisualizeRefinementRays, RefinementRay.xxx, Radiance));
}
#endif
}
#endif