// 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 RWCompactedTraceTexelAllocator; RWBuffer 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 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