// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= LumenTranslucencyVolumeHardwareRayTracing.usf =============================================================================*/ // TODO: Remove hair dependency #include "../HairStrands/HairStrandsVoxelPageCommonStruct.ush" #include "/Engine/Shared/RayTracingTypes.h" #include "../Common.ush" #include "../SHCommon.ush" #include "../MonteCarlo.ush" #include "../OctahedralCommon.ush" #include "../SceneTextureParameters.ush" #include "LumenCardCommon.ush" #include "LumenTracingCommon.ush" #include "LumenRadianceCacheCommon.ush" #include "LumenTranslucencyVolumeLightingShared.ush" #if LUMEN_HARDWARE_RAYTRACING #include "LumenHardwareRayTracingCommon.ush" #ifndef DIM_LIGHTING_MODE #define DIM_LIGHTING_MODE LIGHTING_FROM_SURFACE_CACHE #endif RaytracingAccelerationStructure TLAS; RaytracingAccelerationStructure FarFieldTLAS; #if LUMEN_HARDWARE_INLINE_RAYTRACING StructuredBuffer HitGroupData; StructuredBuffer RayTracingSceneMetadata; RWStructuredBuffer RWInstanceHitCountBuffer; #endif // LUMEN_HARDWARE_INLINE_RAYTRACING RWTexture3D RWVolumeTraceRadiance; RWTexture3D RWVolumeTraceHitDistance; uint MaxTraversalIterations; uint MeshSectionVisibilityTest; // Applying an offset from the depth buffer helps with reducing self intersection with global distance field tracing. void ApplyDepthconstraintsToOffset(float3 GridCoordinate, inout float3 LocalFrameJitterOffset) { if (GridCenterOffsetFromDepthBuffer >= 0.0) { float4 GridSampleClip = mul(float4(ComputeCellTranslatedWorldPosition(GridCoordinate, LocalFrameJitterOffset), 1), View.TranslatedWorldToClip); float3 GridSampleScreen = GridSampleClip.xyz / GridSampleClip.w; float3 GridSampleUVz = float3(GridSampleScreen.xy * View.ScreenPositionScaleBias.xy + View.ScreenPositionScaleBias.wz, GridSampleScreen.z); float GridSampleSceneDepth = ConvertFromDeviceZ(SceneTexturesStruct.SceneDepthTexture.SampleLevel(SceneTexturesStruct_SceneDepthTextureSampler, GridSampleUVz.xy, 0).r); float DepthGridCoordinateWithZOffset = ComputeZSliceFromDepth(GridSampleSceneDepth) - GridCenterOffsetFromDepthBuffer; float3 GridCoordinateOffset = float3(GridCoordinate) + LocalFrameJitterOffset; const float DepthToSampleGridOffset = DepthGridCoordinateWithZOffset - GridCoordinateOffset.z; // <0 if samples is in front of the depth buffer if (DepthToSampleGridOffset < 0.0 // Move the sample forward only if the depth buffer grid coordinate is closer, && (-DepthToSampleGridOffset < GridCenterOffsetThresholdToAcceptDepthBufferOffset) // But only if it is less than N slice away (to not bring all the sample behind forward) ) { // If the grid sample is further than the grid z for the depth buffer, adjust the offset LocalFrameJitterOffset.z += DepthToSampleGridOffset; } } } LUMEN_HARDWARE_RAY_TRACING_ENTRY(LumenTranslucencyVolumeHardwareRayTracing) { uint OctahedralAtlasSizeX = TranslucencyGIGridSize.x * TranslucencyVolumeTracingOctahedronResolution; // DispatchThreadId to match compute version uint3 DispatchThreadId = uint3(DispatchThreadIndex.x % OctahedralAtlasSizeX, DispatchThreadIndex.y, DispatchThreadIndex.x / OctahedralAtlasSizeX); #define DEINTERLEAVED_VOLUME_TRACING 0 #if DEINTERLEAVED_VOLUME_TRACING uint3 GridCoordinate = uint3(DispatchThreadId.xy % TranslucencyGIGridSize.xy, DispatchThreadId.z); uint2 TraceTexelCoord = DispatchThreadId.xy / TranslucencyGIGridSize.xy; #else uint3 GridCoordinate = uint3(DispatchThreadId.xy / TranslucencyVolumeTracingOctahedronResolution, DispatchThreadId.z); uint2 TraceTexelCoord = DispatchThreadId.xy - GridCoordinate.xy * TranslucencyVolumeTracingOctahedronResolution; #endif if (all(GridCoordinate < TranslucencyGIGridSize) && all(TraceTexelCoord < TranslucencyVolumeTracingOctahedronResolution)) { FRayTracedLightingResult TraceLightingResult; TraceLightingResult.Radiance = 0; TraceLightingResult.TraceHitDistance = MaxTraceDistance; if (IsFroxelVisible(GridCoordinate)) { float3 LocalFrameJitterOffset = FrameJitterOffset.xyz; ApplyDepthconstraintsToOffset(GridCoordinate, LocalFrameJitterOffset); float3 WorldPosition = ComputeCellWorldPosition(GridCoordinate, LocalFrameJitterOffset); float2 ProbeUV; float ConeHalfAngle; GetProbeTracingUV(TraceTexelCoord, GetProbeTexelCenter(GridCoordinate.xy), ProbeUV, ConeHalfAngle); float3 WorldConeDirection = EquiAreaSphericalMapping(ProbeUV); float TraceDistance = MaxTraceDistance; FRadianceCacheCoverage Coverage; Coverage.bValid = false; #if PROBE_SOURCE_MODE == PROBE_SOURCE_MODE_RADIANCE_CACHE Coverage = GetRadianceCacheCoverage(WorldPosition, WorldConeDirection, .5f); if (Coverage.bValid) { TraceDistance = min(TraceDistance, Coverage.MinTraceDistanceBeforeInterpolation); } #endif FRayDesc Ray; Ray.Origin = WorldPosition + DFHackToFloat(PrimaryView.PreViewTranslation); // LUMEN_LWC_TODO Ray.Direction = WorldConeDirection; Ray.TMin = 0; Ray.TMax = TraceDistance; FRayCone RayCone = (FRayCone)0; RayCone = PropagateRayCone(RayCone, ConeHalfAngle, 0.0); const uint LinearCoord = GridCoordinate.y * TranslucencyGIGridSize.x + GridCoordinate.x; FRayTracedLightingContext Context = CreateRayTracedLightingContext( RayCone, GridCoordinate.xy, LinearCoord, /*CullingMode*/ RAY_FLAG_NONE, // Disable culling in order to minimize leaking when rays start inside walls MaxTraversalIterations, MeshSectionVisibilityTest != 0); #if DIM_LIGHTING_MODE == LIGHTING_FROM_SURFACE_CACHE { #if LUMEN_HARDWARE_INLINE_RAYTRACING Context.HitGroupData = HitGroupData; Context.RayTracingSceneMetadata = RayTracingSceneMetadata; Context.RWInstanceHitCountBuffer = RWInstanceHitCountBuffer; #endif TraceLightingResult = TraceSurfaceCacheRay(TLAS, Ray, Context); } #else { TraceLightingResult = TraceAndCalculateRayTracedLighting(TLAS, FarFieldTLAS, Ray, Context, DIM_LIGHTING_MODE, false); } #endif if (!TraceLightingResult.bIsHit) { float Transparency = 1.0f; #if PROBE_SOURCE_MODE == PROBE_SOURCE_MODE_RADIANCE_CACHE if (Coverage.bValid) { SampleRadianceCacheAndApply(Coverage, WorldPosition, WorldConeDirection, ConeHalfAngle, /*RandomScalarForStochasticIterpolation*/ 0.5f, TraceLightingResult.Radiance, Transparency); } else #endif { FConeTraceResult TraceResult; TraceResult.Lighting = TraceLightingResult.Radiance; TraceResult.Transparency = Transparency; ApplySkylightToTraceResult(WorldConeDirection, TraceResult); TraceLightingResult.Radiance = TraceResult.Lighting; } } TraceLightingResult.Radiance *= View.PreExposure; float MaxLighting = max3(TraceLightingResult.Radiance.x, TraceLightingResult.Radiance.y, TraceLightingResult.Radiance.z); if (MaxLighting > MaxRayIntensity) { TraceLightingResult.Radiance *= MaxRayIntensity / MaxLighting; } } uint3 WriteCoord = uint3(GridCoordinate.xy * TranslucencyVolumeTracingOctahedronResolution + TraceTexelCoord, GridCoordinate.z); RWVolumeTraceRadiance[WriteCoord] = TraceLightingResult.Radiance; RWVolumeTraceHitDistance[WriteCoord] = min(TraceLightingResult.TraceHitDistance, MaxHalfFloat); } } #endif // LUMEN_HARDWARE_RAYTRACING