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

1246 lines
43 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
// Change this to force recompilation of all Lumen HWRT shaders
#pragma message("UESHADERMETADATA_VERSION A027A214-A1AF-323E-24C1-AFAF33FAB21C")
#define SUPPORT_CONTACT_SHADOWS 0
#define USE_HAIR_LIGHTING 0
#ifndef FAR_FIELD_OCCLUSION_ONLY
#define FAR_FIELD_OCCLUSION_ONLY 0
#endif
#if NANITE_RAY_TRACING
#include "../Nanite/NaniteRayTrace.ush"
#endif
// Do not override the SSS material, so that hit lighting surface get proper diffuse color
#define SUBSTRATE_SSS_MATERIAL_OVERRIDE 0
// Additional rewiring to make DeferredShadingCommon happy
#define PreIntegratedGF ReflectionStruct.PreIntegratedGF
#define PreIntegratedGFSampler GlobalBilinearClampedSampler
#include "../Substrate/SubstrateEvaluation.ush"
#include "../SceneData.ush"
#include "../RayTracing/RayTracingCommon.ush"
#include "../RayTracing/RayTracingDeferredMaterials.ush"
#if !COMPUTESHADER
#include "../RayTracing/RayTracingDeferredShadingCommon.ush"
#include "../RayTracing/RayTracingLightingCommon.ush"
#endif
#include "../RayTracing/RayTracingReflectionEnvironment.ush"
#include "LumenHardwareRayTracingPayloadCommon.ush"
#if !COMPUTESHADER
#include "LumenHardwareRayTracingPlatformCommon.ush"
#endif
#if COMPUTESHADER
#include "../RayTracing/TraceRayInline.ush"
#endif
#include "LumenReflectionsCombine.ush"
#include "LumenTracingCommon.ush"
#include "SurfaceCache/LumenSurfaceCacheSampling.ush"
float3 CalcPrevTranslatedWorldPositionFromGPUSceneInstanceIndex(float3 TranslatedWorldPosition, uint GPUSceneInstanceIndex)
{
FInstanceSceneData InstanceSceneData = GetInstanceSceneData(GPUSceneInstanceIndex);
float4x4 TranslatedWorldToLocal = DFFastToTranslatedWorld(InstanceSceneData.WorldToLocal, PrimaryView.PreViewTranslation);
float4x4 PrevLocalToTranslatedWorld = DFFastToTranslatedWorld(InstanceSceneData.PrevLocalToWorld, PrimaryView.PreViewTranslation);
float3 LocalPosition = mul(float4(TranslatedWorldPosition, 1.0), TranslatedWorldToLocal).xyz;
float3 PrevTranslatedWorldPosition = mul(float4(LocalPosition, 1.0), PrevLocalToTranslatedWorld).xyz;
return PrevTranslatedWorldPosition;
}
#ifndef ENABLE_NEAR_FIELD_TRACING
#define ENABLE_NEAR_FIELD_TRACING 1
#endif // ENABLE_NEAR_FIELD_TRACING
#ifndef LUMEN_HARDWARE_INLINE_RAYTRACING
#define LUMEN_HARDWARE_INLINE_RAYTRACING 0
#endif
#ifndef RECURSIVE_REFRACTION_TRACES
#define RECURSIVE_REFRACTION_TRACES 0
#endif
// Whether to skip back-face and two-sided hits for a short distance
// Must match LumenHardwareRaytracing::EAvoidSelfIntersectionsMode
#define AVOID_SELF_INTERSECTIONS_MODE_DISABLED 0
#define AVOID_SELF_INTERSECTIONS_MODE_RETRACE 1
#define AVOID_SELF_INTERSECTIONS_MODE_AHS 2
#ifndef AVOID_SELF_INTERSECTIONS_MODE
#define AVOID_SELF_INTERSECTIONS_MODE AVOID_SELF_INTERSECTIONS_MODE_DISABLED
#endif
// Whether to retrace ray if surface cache sample returns a transparent surface
#ifndef SURFACE_CACHE_ALPHA_MASKING
#define SURFACE_CACHE_ALPHA_MASKING 0
#endif
#ifndef ENABLE_TRACING_FEEDBACK
#define ENABLE_TRACING_FEEDBACK 0
#endif
// Helper macro to set common entry point for passes that have both inline (CS) and non-inline (RGS) version.
// Given "name" it will create "nameCS" entry point for compute and "nameRGS" for raygen shader.
#if LUMEN_HARDWARE_INLINE_RAYTRACING
#define LUMEN_HARDWARE_RAY_TRACING_ENTRY(name)\
void name##_INTERNAL(uint3 DispatchThreadIndex, uint3 DispatchGroupId, uint DispatchGroupIndex);\
[numthreads(INLINE_RAY_TRACING_THREAD_GROUP_SIZE_X, INLINE_RAY_TRACING_THREAD_GROUP_SIZE_Y, 1)]\
void name##CS(uint3 DispatchThreadIndex : SV_DispatchThreadID, uint3 DispatchGroupId : SV_GroupID, uint DispatchGroupIndex : SV_GroupIndex) {\
name##_INTERNAL(DispatchThreadIndex, DispatchGroupId, DispatchGroupIndex);}\
void name##_INTERNAL(uint3 DispatchThreadIndex, uint3 DispatchGroupId, uint DispatchGroupIndex)
#else // LUMEN_HARDWARE_RAYTRACING
#define LUMEN_HARDWARE_RAY_TRACING_ENTRY(name)\
void name##_INTERNAL(uint3 DispatchThreadIndex, uint3 DispatchGroupId, uint DispatchGroupIndex);\
RAY_TRACING_ENTRY_RAYGEN(name##RGS){\
name##_INTERNAL(DispatchRaysIndex(), DispatchRaysIndex(), 0);}\
void name##_INTERNAL(uint3 DispatchThreadIndex, uint3 DispatchGroupId, uint DispatchGroupIndex)
#endif // LUMEN_HARDWARE_INLINE_RAYTRACING
/**
* Clip near field ray to ray tracing culling radius, and apply dithering to hide transition region between nead and far field.
*/
float ClipAndDitherNearFieldMaxTraceDistance(
float3 TranslatedWorldRayOrigin,
float3 RayDirection,
uint2 NoiseCoord,
float NearFieldSceneRadius,
float NearFieldMaxTraceDistance,
float NearFieldMaxTraceDistanceDitherScale)
{
float ClippedNearFieldMaxTraceDistance = 0.0f;
float2 RaySphereHit = RayIntersectSphere(TranslatedWorldRayOrigin, RayDirection, float4(PrimaryView.TranslatedWorldCameraOrigin, NearFieldSceneRadius));
if (RaySphereHit.x < 0.0f && RaySphereHit.y > 0.0f)
{
ClippedNearFieldMaxTraceDistance = min(RaySphereHit.y, NearFieldMaxTraceDistance) - InterleavedGradientNoise(NoiseCoord, View.StateFrameIndexMod8) * NearFieldMaxTraceDistanceDitherScale;
}
return ClippedNearFieldMaxTraceDistance;
}
struct FHitGroupRootConstants
{
uint UserData;
};
struct FRayTracedLightingContext
{
#if LUMEN_HARDWARE_INLINE_RAYTRACING
StructuredBuffer<FHitGroupRootConstants> HitGroupData;
StructuredBuffer<FRayTracingSceneMetadataRecord> RayTracingSceneMetadata;
RWStructuredBuffer<uint> RWInstanceHitCountBuffer;
#endif // LUMEN_HARDWARE_INLINE_RAYTRACING
FRayCone RayCone;
uint2 TraceCoord;
uint LinearCoord;
uint InstanceMask;
// Max ray tracing traversal iterations on supported platforms
uint MaxTraversalIterations;
float MinTraceDistanceToSampleSurfaceCache;
uint MaxReflectionBounces;
uint MaxRefractionBounces;
uint CullingMode;
bool bHiResSurface; // Whether to sample high res surface cache data or low res always resident pages
bool bAcceptFirstHitAndEndSearch;
bool bIsShadowRay;
bool bIsFarFieldRay;
bool bUseBookmark;
bool bForceOpaque;
bool bMeshSectionVisibilityTest;
bool bForceClosestHitShader;
// Hit-lighting
float HitLightingShadowMaxTraceDistance;
uint HitLightingShadowMode;
uint HitLightingShadowTranslucencyMode;
bool bHitLightingDirectLighting;
bool bHitLightingSkylight;
bool bUseReflectionCaptures;
uint LightingChannelMask;
};
FRayTracedLightingContext CreateRayTracedLightingContext(
in FRayCone RayCone,
in uint2 TraceCoord,
in uint LinearCoord,
in uint CullingMode,
uint MaxTraversalIterations,
bool bMeshSectionVisibilityTest)
{
FRayTracedLightingContext Context;
Context.RayCone = RayCone;
Context.TraceCoord = TraceCoord;
Context.LinearCoord = LinearCoord;
Context.InstanceMask = RAY_TRACING_MASK_OPAQUE;
Context.MaxTraversalIterations = MaxTraversalIterations;
Context.MinTraceDistanceToSampleSurfaceCache = 0.0f;
Context.MaxReflectionBounces = 1;
Context.MaxRefractionBounces = 0;
Context.CullingMode = CullingMode;
Context.bHiResSurface = false;
Context.bAcceptFirstHitAndEndSearch = false;
Context.bIsShadowRay = false;
Context.bIsFarFieldRay = false;
Context.bUseBookmark = false;
Context.bForceOpaque = false;
Context.bMeshSectionVisibilityTest = bMeshSectionVisibilityTest;
Context.bForceClosestHitShader = false;
Context.HitLightingShadowMode = 1;
Context.HitLightingShadowTranslucencyMode = 1;
Context.bHitLightingDirectLighting = true;
Context.bHitLightingSkylight = false;
Context.bUseReflectionCaptures = false;
Context.HitLightingShadowMaxTraceDistance = 0.0f;
Context.LightingChannelMask = 0xFF;
return Context;
}
struct FLumenMinimalRayResult
{
bool bHit;
bool bCompleted;
bool bTranslucent;
bool bTwoSided;
bool bAlphaMasked;
bool bFrontFace;
uint MaterialShaderIndex;
uint SceneInstanceIndex;
float HitT;
float3 HitNormal;
FLumenRayHitBookmark Bookmark;
};
FLumenMinimalRayResult InitLumenMinimalRayResult()
{
FLumenMinimalRayResult MinimalRayResult = (FLumenMinimalRayResult)0;
MinimalRayResult.bHit = false;
MinimalRayResult.bCompleted = true;
return MinimalRayResult;
}
struct FRayTracedLightingResult
{
bool bIsHit;
bool bIsCompleted; // Was ray tracing completed or stopped due to reaching MaxTraversalIterations
bool bIsFarField;
bool bHitTwoSided;
float TraceHitDistance;
float3 BackgroundVisibility;
bool bIsRadianceCompleted; // Is radiance computation completed or do we need a fallback shading pass
float3 Radiance;
float3 GeometryWorldNormal;
uint SceneInstanceIndex;
bool bValidSurfaceCache;
uint MaterialShaderIndex;
FLumenRayHitBookmark Bookmark;
};
FRayTracedLightingResult CreateRayTracedLightingResult(FRayDesc Ray)
{
FRayTracedLightingResult Result;
Result.bIsHit = false;
Result.bIsCompleted = true;
Result.bIsFarField = false;
Result.bHitTwoSided = false;
Result.TraceHitDistance = Ray.TMax;
Result.BackgroundVisibility = 1;
Result.bIsRadianceCompleted = true;
Result.Radiance = 0;
Result.GeometryWorldNormal = 0.0f;
Result.SceneInstanceIndex = LUMEN_INVALID_SCENE_INSTANCE_INDEX;
Result.bValidSurfaceCache = false;
Result.MaterialShaderIndex = RAY_TRACING_DEFERRED_MATERIAL_KEY_INVALID;
Result.Bookmark.PackedData[0] = 0xFFFFFFFF;
Result.Bookmark.PackedData[1] = 0xFFFFFFFF;
return Result;
}
FRayTracedLightingResult CreateRayTracedLightingResult(
FRayDesc Ray,
FRayTracedLightingContext Context,
FLumenMinimalRayResult MinimalRayResult,
FSurfaceCacheSample SurfaceCacheSample)
{
FRayTracedLightingResult Result = CreateRayTracedLightingResult(Ray);
Result.TraceHitDistance = Ray.TMax;
Result.bIsHit = MinimalRayResult.bHit;
Result.bIsCompleted = MinimalRayResult.bCompleted;
Result.bIsFarField = Context.bIsFarFieldRay;
Result.bHitTwoSided = MinimalRayResult.bTwoSided;
if (MinimalRayResult.bHit)
{
Result.TraceHitDistance = MinimalRayResult.HitT;
Result.MaterialShaderIndex = MinimalRayResult.MaterialShaderIndex;
Result.Bookmark = MinimalRayResult.Bookmark;
Result.bIsRadianceCompleted = SurfaceCacheSample.bValid;
Result.Radiance = SurfaceCacheSample.Radiance;
Result.GeometryWorldNormal = MinimalRayResult.HitNormal;
Result.SceneInstanceIndex = MinimalRayResult.SceneInstanceIndex;
}
// Force a black hit when reached max iterations without hitting anything
if (!Result.bIsHit && !Result.bIsCompleted)
{
Result.bIsHit = true;
Result.TraceHitDistance = Ray.TMax;
Result.bIsRadianceCompleted = true;
Result.Radiance = float3(0.0f, 0.0f, 0.0f);
}
return Result;
}
#if !COMPUTESHADER
bool TraceLumenHitLightingRay(
in RaytracingAccelerationStructure TLAS,
in FRayDesc Ray,
inout FRayTracedLightingContext Context,
inout FLumenRayHitBookmark Bookmark,
inout FPackedMaterialClosestHitPayload Payload)
{
uint RayFlags = Context.bForceOpaque ? RAY_FLAG_FORCE_OPAQUE : 0;
// "Avoid Self Intersections" will force ray to be two-sided in order to minimize leaking
#if AVOID_SELF_INTERSECTIONS_MODE == AVOID_SELF_INTERSECTIONS_MODE_DISABLED
{
RayFlags |= Context.CullingMode;
}
#endif
if (Context.bUseBookmark)
{
TraceLumenShadingRay(TLAS, RayFlags, Context.InstanceMask, RAY_TRACING_SHADER_SLOT_MATERIAL, RAY_TRACING_NUM_SHADER_SLOTS, 0, Ray.GetNativeDesc(), Bookmark, Payload);
}
else
{
TraceLumenShadingRay(TLAS, RayFlags, Context.InstanceMask, RAY_TRACING_SHADER_SLOT_MATERIAL, RAY_TRACING_NUM_SHADER_SLOTS, 0, Ray.GetNativeDesc(), Payload);
}
return Payload.IsHit();
}
#endif // !COMPUTESHADER
/**
* Lumen minimal (surface cache) ray any hit shader logic shared between inline and RayGen traces.
* See LumenHardwareRayTracingMaterials.usf.
*/
bool LumenMinimalRayAnyHitShader(uint UserData, float HitT, bool bHitBackFace, bool bShadowRay)
{
const bool bIsAlphaMasked = (UserData >> 28) & 0x1;
const bool bCastRayTracedShadows = (UserData >> 29) & 0x1;
const bool bIsTwoSided = (UserData >> 30) & 0x1;
const bool bIsTranslucent = (UserData >> 31) & 0x1;
if (!bCastRayTracedShadows && bShadowRay)
{
return false;
}
// Skip all translucent meshes
if (bIsTranslucent && LumenHardwareRayTracingUniformBuffer.SkipTranslucent > 0.0f)
{
return false;
}
#if AVOID_SELF_INTERSECTIONS_MODE == AVOID_SELF_INTERSECTIONS_MODE_AHS
{
if (bIsTwoSided)
{
// SkipBackFaceHitDistance doesn't work on two sided geometry, but we still need to avoid self-intersections with the Nanite fallback mesh
if (HitT < LumenHardwareRayTracingUniformBuffer.SkipTwoSidedHitDistance)
{
return false;
}
}
else if (bHitBackFace)
{
// Avoid self-shadowing by skipping back face hits for some distance
if (HitT < LumenHardwareRayTracingUniformBuffer.SkipBackFaceHitDistance)
{
return false;
}
}
}
#endif
return true;
}
#if COMPUTESHADER && LUMEN_HARDWARE_INLINE_RAYTRACING
struct FLumenTraceRayInlineCallback : FDefaultTraceRayInlineCallback
{
StructuredBuffer<FHitGroupRootConstants> HitGroupData;
bool bShadowRay;
uint LightingChannelMask;
bool OnAnyHit(float3 ObjectRayOrigin, float3 ObjectRayDirection, FTraceRayInlineResult Hit)
{
// Our SBTs always have 2 slots per material. Therefore InstanceContributionToHitGroupIndex has a RAY_TRACING_NUM_SHADER_SLOTS multiplier.
// But because we only encode 1 material into LumenHardwareRayTracingHitDataBuffer we want to get actual material index.
uint InstanceContributionIndex = Hit.InstanceContributionToHitGroupIndex / RAY_TRACING_NUM_SHADER_SLOTS;
uint HitGroupIndex = Hit.GeometryIndex + InstanceContributionIndex;
FHitGroupRootConstants HitData = HitGroupData[HitGroupIndex];
#if MEGA_LIGHTS_LIGHTING_CHANNELS
FInstanceSceneData InstanceData = GetInstanceSceneData(Hit.InstanceID);
uint PrimLightingChannelMask = GetPrimitive_LightingChannelMask(InstanceData.PrimitiveId);
if ((LightingChannelMask & PrimLightingChannelMask) == 0)
{
return false;
}
#endif // MEGA_LIGHTS_LIGHTING_CHANNELS
return LumenMinimalRayAnyHitShader(HitData.UserData, Hit.HitT, /*bHitBackFace*/ !Hit.bIsFrontFace, bShadowRay);
}
};
#endif
FLumenMinimalRayResult TraceLumenMinimalRay(
in RaytracingAccelerationStructure TLAS,
FRayDesc Ray,
inout FRayTracedLightingContext Context)
{
FLumenMinimalPayload Payload = (FLumenMinimalPayload)0;
FLumenMinimalRayResult MinimalRayResult = InitLumenMinimalRayResult();
Payload.SetIsShadowRay(Context.bIsShadowRay);
uint RayFlags = 0;
RayFlags |= Context.bAcceptFirstHitAndEndSearch ? RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH : 0;
RayFlags |= Context.bMeshSectionVisibilityTest ? 0 : RAY_FLAG_FORCE_OPAQUE;
// Alpha masking is done through a re-trace so need to sample surface cache if alpha masking is enabled
if (Context.bIsShadowRay && !Context.bForceClosestHitShader && !SURFACE_CACHE_ALPHA_MASKING)
{
RayFlags |= RAY_FLAG_SKIP_CLOSEST_HIT_SHADER;
}
// "Avoid Self Intersections" will force ray to be two-sided in order to minimize leaking
#if AVOID_SELF_INTERSECTIONS_MODE == AVOID_SELF_INTERSECTIONS_MODE_DISABLED
{
RayFlags |= Context.CullingMode;
}
#endif
#if AVOID_SELF_INTERSECTIONS_MODE == AVOID_SELF_INTERSECTIONS_MODE_AHS
{
// If needed force any-hit shader on all instances for handling "Avoid Self Intersections" in Lumen minimal AHS
const float AvoidSelfIntersectionsDistance = max(LumenHardwareRayTracingUniformBuffer.SkipTwoSidedHitDistance, LumenHardwareRayTracingUniformBuffer.SkipBackFaceHitDistance);
if (Ray.TMin < AvoidSelfIntersectionsDistance)
{
RayFlags &= ~RAY_FLAG_FORCE_OPAQUE;
RayFlags |= RAY_FLAG_FORCE_NON_OPAQUE;
}
}
#endif
#if MEGA_LIGHTS_LIGHTING_CHANNELS
const uint DefaultLightingChannelMask = 1;
bool bUsesLightingChannels = Context.LightingChannelMask != DefaultLightingChannelMask;
if(bUsesLightingChannels)
{
RayFlags &= ~RAY_FLAG_FORCE_OPAQUE;
RayFlags |= RAY_FLAG_FORCE_NON_OPAQUE;
}
#endif
#if LUMEN_HARDWARE_INLINE_RAYTRACING
{
FTraceRayInlineContext TraceRayInlineContext = CreateTraceRayInlineContext();
TraceRayInlineContext.MaxIterations = Context.MaxTraversalIterations;
FLumenTraceRayInlineCallback LumenCallback;
LumenCallback.HitGroupData = Context.HitGroupData;
LumenCallback.bShadowRay = Context.bIsShadowRay;
LumenCallback.LightingChannelMask = Context.LightingChannelMask;
FTraceRayInlineResult TraceResult = TraceRayInlineWithCallback(TLAS, RayFlags, Context.InstanceMask, Ray.GetNativeDesc(), TraceRayInlineContext, LumenCallback);
if (TraceResult.IsHit())
{
Payload.HitT = TraceResult.HitT;
// Our SBTs always have 2 slots per material. Therefore InstanceContributionToHitGroupIndex has a RAY_TRACING_NUM_SHADER_SLOTS multiplier.
// But because we only encode 1 material into LumenHardwareRayTracingHitDataBuffer we want to get actual material index.
uint InstanceContributionIndex = TraceResult.InstanceContributionToHitGroupIndex / RAY_TRACING_NUM_SHADER_SLOTS;
uint HitGroupIndex = TraceResult.GeometryIndex + InstanceContributionIndex;
FHitGroupRootConstants HitData = Context.HitGroupData[HitGroupIndex];
Payload.SetGPUSceneInstanceIndex(TraceResult.InstanceID);
uint MaterialShaderIndex = HitData.UserData & LUMEN_MATERIAL_SHADER_INDEX_MASK;
Payload.SetMaterialShaderIndex(MaterialShaderIndex);
uint bIsTranslucent = (HitData.UserData >> 31) & 0x1;
Payload.SetIsTranslucent(bIsTranslucent);
uint bIsTwoSided = (HitData.UserData >> 30) & 0x1;
Payload.SetIsTwoSided(bIsTwoSided);
uint bIsAlphaMasked = (HitData.UserData >> 28) & 0x1;
Payload.SetIsAlphaMasked(bIsAlphaMasked);
uint bIsDynamicGeometry = (HitData.UserData >> 27) & 0x1;
Payload.SetIsDynamicGeometry(bIsDynamicGeometry);
Payload.SetFrontFace(TraceResult.bIsFrontFace);
float3 WorldNormal;
{
#if PLATFORM_SUPPORTS_INLINE_RAY_TRACING_TRIANGLE_NORMALS
WorldNormal = TraceResult.WorldGeometryNormal;
#elif PLATFORM_SUPPORTS_INLINE_RAY_TRACING_TRIANGLE_ATTRIBUTES
# if NANITE_RAY_TRACING
FInstanceSceneData InstanceData = GetInstanceSceneData(TraceResult.InstanceID);
FPrimitiveSceneData PrimitiveData = GetPrimitiveData(InstanceData.PrimitiveId);
if (PrimitiveData.NaniteRayTracingDataOffset != uint(-1))
{
const uint HitGroupFirstPrimitive = GetInlineRayTracingHitGroupFirstPrimitive(
Context.RayTracingSceneMetadata,
TraceResult.InstanceContributionToHitGroupIndex,
RAY_TRACING_NUM_SHADER_SLOTS,
TraceResult.GeometryIndex
);
float3 LocalEdges[2];
GetNaniteTriangleEdges(PrimitiveData.NaniteRayTracingDataOffset + HitGroupFirstPrimitive + TraceResult.PrimitiveIndex, LocalEdges[0], LocalEdges[1]);
float3 LocalNormal = cross(LocalEdges[1], LocalEdges[0]);
WorldNormal = normalize(mul(LocalNormal, (float3x3)TraceResult.WorldToObject3x4));
}
else
# endif // NANITE_RAY_TRACING
{
FTriangleBaseAttributes Attributes = LoadInlineRayTracingTriangleAttributes(
Context.RayTracingSceneMetadata,
TraceResult.InstanceContributionToHitGroupIndex,
RAY_TRACING_NUM_SHADER_SLOTS,
TraceResult.GeometryIndex,
TraceResult.PrimitiveIndex
);
float3 LocalNormal = cross(Attributes.LocalPositions[2] - Attributes.LocalPositions[0], Attributes.LocalPositions[1] - Attributes.LocalPositions[0]);
WorldNormal = normalize(mul(LocalNormal, (float3x3)TraceResult.WorldToObject3x4));
}
#else // !PLATFORM_SUPPORTS_INLINE_RAY_TRACING_TRIANGLE_ATTRIBUTES
WorldNormal = 0.0f;
#endif // !PLATFORM_SUPPORTS_INLINE_RAY_TRACING_TRIANGLE_ATTRIBUTES
}
Payload.SetWorldNormal(WorldNormal);
#if ENABLE_TRACING_FEEDBACK
if (Payload.IsDynamicGeometry())
{
Context.RWInstanceHitCountBuffer[TraceResult.InstanceIndex] = 1;
}
#endif
MinimalRayResult.Bookmark.PackedData = TraceResult.Bookmark;
}
else
{
Payload.SetMiss();
}
MinimalRayResult.bCompleted = TraceResult.bIsCompleted;
}
#else
{
// AVOID_SELF_INTERSECTIONS_MODE_AHS permutation is stored as two HitGroups
const uint ShaderSlotIndex = AVOID_SELF_INTERSECTIONS_MODE == AVOID_SELF_INTERSECTIONS_MODE_AHS ? RAY_TRACING_SHADER_SLOT_SHADOW : RAY_TRACING_SHADER_SLOT_MATERIAL;
TraceLumenRay(TLAS, RayFlags, Context.InstanceMask, ShaderSlotIndex, RAY_TRACING_NUM_SHADER_SLOTS, 0, Ray.GetNativeDesc(), Payload, MinimalRayResult.Bookmark);
}
#endif
MinimalRayResult.bHit = Payload.IsHit();
if (MinimalRayResult.bHit)
{
MinimalRayResult.bTranslucent = Payload.IsTranslucent();
MinimalRayResult.bTwoSided = Payload.IsTwoSided();
MinimalRayResult.bFrontFace = Payload.IsFrontFace();
MinimalRayResult.bAlphaMasked = Payload.IsAlphaMasked();
MinimalRayResult.HitNormal = Payload.IsFrontFace() ? Payload.GetWorldNormal() : -Payload.GetWorldNormal();
MinimalRayResult.HitT = Payload.HitT;
MinimalRayResult.SceneInstanceIndex = Payload.GetGPUSceneInstanceIndex();
MinimalRayResult.MaterialShaderIndex = Payload.GetMaterialShaderIndex();
}
return MinimalRayResult;
}
float SurfaceCacheSamplingDepthBias;
FSurfaceCacheSample CalculateSurfaceCacheLighting(
FRayDesc Ray,
FRayTracedLightingContext Context,
float3 RayHitTranslatedWorldPosition,
float3 RayHitWorldNormal,
float HitDistance,
uint SceneInstanceIndex
)
{
float InterpolateRadius = tan(Context.RayCone.SpreadAngle) * HitDistance;
float SurfaceCacheDepthBias = Context.bIsFarFieldRay ? 1000.0f : SurfaceCacheSamplingDepthBias;
const uint MeshCardsIndex = GetMeshCardsIndexFromSceneInstanceIndex(SceneInstanceIndex);
FSurfaceCacheSample SurfaceCacheSample = EvaluateRayHitFromSurfaceCache(
Context.TraceCoord,
MeshCardsIndex,
RayHitTranslatedWorldPosition - DFHackToFloat(PrimaryView.PreViewTranslation), // LUMEN_LWC_TODO
RayHitWorldNormal,
InterpolateRadius,
SurfaceCacheDepthBias,
Context.bHiResSurface
);
// Surface cache has limited resolution. Make sure we don't self-intersect and cause leaking or GI feedback loop
if (HitDistance < Context.MinTraceDistanceToSampleSurfaceCache)
{
SurfaceCacheSample.Radiance = float3(0, 0, 0);
SurfaceCacheSample.DirectLighting = float3(0, 0, 0);
SurfaceCacheSample.IndirectLighting = float3(0, 0, 0);
}
return SurfaceCacheSample;
}
FSurfaceCacheSample SampleLumenMinimalRayHit(
FRayDesc Ray,
FRayTracedLightingContext Context,
FLumenMinimalRayResult MinimalRayResult)
{
float3 RayHitWorldNormal = MinimalRayResult.HitNormal;
float3 RayHitTranslatedWorldPosition = Ray.Origin + Ray.Direction * MinimalRayResult.HitT;
FSurfaceCacheSample SurfaceCacheSample = CalculateSurfaceCacheLighting(
Ray,
Context,
RayHitTranslatedWorldPosition,
RayHitWorldNormal,
MinimalRayResult.HitT,
MinimalRayResult.SceneInstanceIndex);
// Skip backface hits if we forced two-sided to minimize leaking
if (!(MinimalRayResult.bFrontFace || MinimalRayResult.bTwoSided))
{
if (SurfaceCacheSample.bHeightfield)
{
// Reuse alpha from the other side of the heightfield, so that Landscape opacity works correctly when viewed from the other side
SurfaceCacheSample.Radiance = 0.0f;
}
else
{
SurfaceCacheSample.Radiance = 0.0f;
SurfaceCacheSample.Opacity = 1.0f;
SurfaceCacheSample.bValid = false;
}
}
return SurfaceCacheSample;
}
#if !COMPUTESHADER
bool TraceDeferredMaterialRay(
in RaytracingAccelerationStructure TLAS,
in FRayDesc Ray,
inout FRayTracedLightingContext Context,
inout FDeferredMaterialPayload DeferredMaterialPayload
)
{
DeferredMaterialPayload = (FDeferredMaterialPayload)0;
DeferredMaterialPayload.SortKey = RAY_TRACING_DEFERRED_MATERIAL_KEY_RAY_MISS;
DeferredMaterialPayload.PixelCoordinates = (Context.TraceCoord.y << 16) | Context.TraceCoord.x;
uint RayFlags = RAY_FLAG_FORCE_OPAQUE;
RayFlags |= Context.CullingMode;
FRayIntersectionBookmark Bookmark = (FRayIntersectionBookmark)0;
TraceDeferredMaterialGatherRay(TLAS, RayFlags, Context.InstanceMask, Ray.GetNativeDesc(), Bookmark, DeferredMaterialPayload);
return DeferredMaterialPayload.IsHit();
}
float3 CalculateDirectLighting(
in RaytracingAccelerationStructure TLAS,
in RaytracingAccelerationStructure FarFieldTLAS,
in FRayDesc Ray,
in FRayTracedLightingContext Context,
inout FPackedMaterialClosestHitPayload Payload,
RandomSequence RandSequence,
float3 RayHitTranslatedWorldPosition,
float3 RayHitWorldNormal)
{
Payload.SetIndirectLightingRay();
float3 DirectLighting = 0.0f;
AccumulateResults(
Payload,
RayHitTranslatedWorldPosition,
Ray.Direction,
TLAS,
FarFieldTLAS,
RandSequence,
Context.TraceCoord,
/*MaxNormalBias*/ 0.05f,
Context.HitLightingShadowMaxTraceDistance,
Context.HitLightingShadowMode,
Context.HitLightingShadowTranslucencyMode,
Context.bHitLightingDirectLighting,
/*bShouldDoEmissiveAndIndirectLighting*/ false,
/*bTopLayerRayTraceSkyLightContribution*/ false,
/*bDecoupleSampleGeneration*/ false,
Context.RayCone,
DirectLighting);
return DirectLighting;
}
struct FLumenHitLightingMaterial
{
float3 TopLayerSpecularColor;
float TopLayerRoughness;
// Return approximate diffuse color as if the entire surface was diffuse in order to conserve energy in secondary bounces
// Keep in sync with LumenCardPixelShader.usf
// #lumen_todo: refactor and merge with LumenCardPixelShader.usf to ensure that their logic is the same
float3 ApproxFullyRoughDiffuseColor;
float3 DiffuseColor;
float3 ApproxFullyRoughSpecularColor;
float3 RefractedThroughputPreCoverage;
float Ior;
};
FLumenHitLightingMaterial GetLumenHitLightingMaterial(FRayTracedLightingContext Context, FPackedMaterialClosestHitPayload PackedPayload, FRayDesc Ray)
{
FLumenHitLightingMaterial LumenMaterial;
LumenMaterial.TopLayerSpecularColor = 0.0f;
LumenMaterial.TopLayerRoughness = 1.0f;
LumenMaterial.ApproxFullyRoughDiffuseColor = 0.0f;
LumenMaterial.DiffuseColor = 0.0f;
LumenMaterial.ApproxFullyRoughSpecularColor = 0.0f;
LumenMaterial.RefractedThroughputPreCoverage = 1.f;
float3 DiffuseAndSubsurfaceColor = 0.0f;
float3 SpecularColor = 0.0f;
#if SUBTRATE_GBUFFER_FORMAT==1
{
#if SUBSTRATE_MATERIALCONTAINER_IS_VIEWRESOURCE == 0
const float3 V = -Ray.Direction;
const FSubstrateIntegrationSettings Settings = InitSubstrateIntegrationSettings(false /*bForceFullyRough*/, true /*bRoughDiffuseEnabled*/, 0 /*PeelLayersAboveDepth*/, false/*bRoughnessTracking*/);
FSubstrateAddressing SubstrateAddressing = GetSubstratePixelDataByteOffset(0, 0, 0);
FSubstratePixelHeader SubstratePixelHeader = UnpackSubstrateHeaderIn(PackedPayload.SubstrateData, SubstrateAddressing, PackedPayload.SubstrateData);
// For raytracing , we force Substrate material to be simplified into a single closure. Remove the loop in order to help compiler
if (SubstratePixelHeader.ClosureCount > 0)
{
FSubstrateBSDF BSDF = UnpackSubstrateBSDF(PackedPayload.SubstrateData, SubstrateAddressing, SubstratePixelHeader);
if (SubstrateIsBSDFVisible(BSDF))
{
// Create the BSDF context
FSubstrateBSDFContext SubstrateBSDFContext = SubstrateCreateBSDFContext(SubstratePixelHeader, BSDF, SubstrateAddressing, V);
SubstrateBSDFContext.PixelCoord = Context.TraceCoord;
const float3 BSDFThroughput = LuminanceWeight(SubstrateBSDFContext, BSDF);
// Evaluate environment lighting
FSubstrateEnvLightResult SubstrateEnvLight = SubstrateEvaluateForEnvLight(SubstrateBSDFContext, true /*bEnableSpecular*/, Settings);
DiffuseAndSubsurfaceColor += BSDFThroughput * (SubstrateEnvLight.DiffuseWeight + SubstrateEnvLight.DiffuseBackFaceWeight);
SpecularColor += BSDFThroughput * SubstrateEnvLight.SpecularWeight;
}
}
// SUBSTRATE_TODO This does not work when enabling translucent tracing in reflection. We would need to evaluate the material, not read form the opaque TopLayerData buffer.
FSubstrateTopLayerData SubstrateTopLayerData = SubstrateUnpackTopLayerData(PackedPayload.SubstrateData.PackedTopLayerData);
LumenMaterial.TopLayerRoughness = SubstrateTopLayerData.Roughness;
LumenMaterial.TopLayerSpecularColor = SpecularColor;
LumenMaterial.RefractedThroughputPreCoverage = PackedPayload.GetTransmittancePreCoverage();
#endif
}
#else
{
DiffuseAndSubsurfaceColor = PackedPayload.GetDiffuseColor();
SpecularColor = PackedPayload.GetSpecularColor();
LumenMaterial.TopLayerRoughness = PackedPayload.GetRoughness();
// TopLayerSpecularColor is SpecularWeight when Substrate is enabled and SpecularWeight seems equivalent to EnvBRDF(SpecularColor, Roughness, NoV)
float NoV = saturate(dot(-Ray.Direction, PackedPayload.GetWorldNormal()));
LumenMaterial.TopLayerSpecularColor = EnvBRDF(SpecularColor, LumenMaterial.TopLayerRoughness, NoV);
const uint ShadingModelID = PackedPayload.GetShadingModelID();
if (ShadingModelID == SHADINGMODELID_TWOSIDED_FOLIAGE || ShadingModelID == SHADINGMODELID_SUBSURFACE)
{
// Add subsurface following EncodeSubsurfaceColor/ExtractSubsurfaceColor
DiffuseAndSubsurfaceColor += Pow2(PackedPayload.GetCustomData().xyz);
}
}
#endif // SUBTRATE_GBUFFER_FORMAT==1
LumenMaterial.DiffuseColor = ApplyDiffuseColorBoost(DiffuseAndSubsurfaceColor, DiffuseColorBoost);
#if SUBTRATE_GBUFFER_FORMAT==1
LumenMaterial.ApproxFullyRoughSpecularColor = SpecularColor; // Match behavior in LumenCardPixelShader.usf
LumenMaterial.Ior = PackedPayload.GetRayCone().Width;
if (LumenMaterial.Ior < 0.0f)
{
LumenMaterial.Ior = DielectricF0RGBToIor(SpecularColor);
}
#else
EnvBRDFApproxFullyRough(LumenMaterial.ApproxFullyRoughSpecularColor, SpecularColor);
LumenMaterial.Ior = PackedPayload.GetIor();
#endif
LumenMaterial.ApproxFullyRoughDiffuseColor = LumenMaterial.DiffuseColor + LumenMaterial.ApproxFullyRoughSpecularColor;
return LumenMaterial;
}
float3 CalculateLightingAtHit(
RaytracingAccelerationStructure TLAS,
RaytracingAccelerationStructure FarFieldTLAS,
FRayDesc Ray,
FRayTracedLightingContext Context,
RandomSequence RandSequence,
FLumenHitLightingMaterial LumenMaterial,
float NextReflectionRayAlpha,
inout FPackedMaterialClosestHitPayload Payload)
{
float3 Radiance = 0;
float3 RayHitTranslatedWorldPosition = Ray.Origin + Ray.Direction * Payload.HitT;
if (Context.bHitLightingDirectLighting)
{
Radiance += CalculateDirectLighting(TLAS, FarFieldTLAS, Ray, Context, Payload, RandSequence, RayHitTranslatedWorldPosition, Payload.GetWorldNormal());
}
if (Context.bHitLightingSkylight)
{
// Add diffuse part
Radiance += LumenMaterial.DiffuseColor * Payload.GetIndirectIrradiance();
// Add specular part
if (Context.bUseReflectionCaptures)
{
if (1.0f - NextReflectionRayAlpha > 0.0f)
{
const bool bSkyLightAffectReflection = ShouldSkyLightAffectReflection();
float3 R = reflect(Ray.Direction, Payload.GetWorldNormal());
const float NoV = saturate(dot(-Ray.Direction, Payload.GetWorldNormal()));
const float RoughnessSq = LumenMaterial.TopLayerRoughness * LumenMaterial.TopLayerRoughness;
const float SpecularOcclusion = GetSpecularOcclusion(NoV, RoughnessSq, 1.0);
Radiance += LumenMaterial.TopLayerSpecularColor * SpecularOcclusion * (1.0f - NextReflectionRayAlpha) *
CompositeReflectionCapturesAndSkylightTWS(
1.0, // CompositeAlpha
RayHitTranslatedWorldPosition,
R,
LumenMaterial.TopLayerRoughness,
0.0, // IndirectIrradiance,
1.0, // IndirectSpecularOcclusion
0.0, // ExtraIndirectSpecular
ForwardLightStruct.NumReflectionCaptures,
0, // ReflectionCapturesStartIndex
0,
bSkyLightAffectReflection);
}
}
else
{
Radiance += LumenMaterial.ApproxFullyRoughSpecularColor * (1.0f - NextReflectionRayAlpha) * Payload.GetIndirectIrradiance();
}
}
else
{
float3 RayHitGeometryWorldNormal = Payload.GetGeometryNormal();
// Reverse surface cache lookup normal to match non hit lighting path (usually surface cache is only valid on front faces)
if (Payload.IsTwoSided() && !Payload.IsFrontFace())
{
RayHitGeometryWorldNormal = -RayHitGeometryWorldNormal;
}
FSurfaceCacheSample SurfaceCacheSample = CalculateSurfaceCacheLighting(
Ray,
Context,
RayHitTranslatedWorldPosition,
RayHitGeometryWorldNormal,
Payload.HitT,
Payload.GetSceneInstanceIndex());
if (!Context.bHitLightingDirectLighting && !Context.bHitLightingSkylight)
{
Radiance += Diffuse_Lambert(LumenMaterial.ApproxFullyRoughDiffuseColor) * (SurfaceCacheSample.DirectLighting + SurfaceCacheSample.IndirectLighting);
}
else
{
// Add diffuse part
Radiance += Diffuse_Lambert(LumenMaterial.DiffuseColor) * SurfaceCacheSample.IndirectLighting;
// Add rough specular part
Radiance += Diffuse_Lambert(LumenMaterial.ApproxFullyRoughSpecularColor) * (1.0f - NextReflectionRayAlpha) * SurfaceCacheSample.IndirectLighting;
}
}
return Radiance;
}
/**
* Traces a ray and calculates lighting at a hit point using hit lighting.
*/
FRayTracedLightingResult TraceAndCalculateRayTracedLighting(
in RaytracingAccelerationStructure TLAS,
in RaytracingAccelerationStructure FarFieldTLAS,
in FRayDesc Ray,
in FRayTracedLightingContext Context,
inout FLumenRayHitBookmark Bookmark
)
{
RandomSequence RandSequence;
RandomSequence_Initialize(RandSequence, Context.LinearCoord, View.StateFrameIndex);
FRayTracedLightingResult Result = CreateRayTracedLightingResult(Ray);
Result.bIsHit = false;
Result.TraceHitDistance = Ray.TMax;
#if !RECURSIVE_REFLECTION_TRACES
{
Context.MaxReflectionBounces = 1;
}
#endif
float3 RefractionPathThroughput = 1.0f;
uint RefractionIndex = 0;
#if RECURSIVE_REFRACTION_TRACES
// Refraction are traced as follow due to the lack of a proper path stack:
// 1- A surface is hit, we process all reflection events (and never refract during those events)
// 2- Once all reflections have been integrated, we refract once account for coverage and BSDF for the path throughput.
// 3- We stop once we have used all our refraction event or the path throughput is below a threshold.
const float RefractionPassThroughputThreshold = 0.01f;
const float3 RefractionDir = Ray.Direction; // The refraction direction is set once for all at the beginning. No change of direction as of today.
float3 RefractionPos = 0; // Translated world post
bool bStopRefraction = false;
for (RefractionIndex = 0; (any(RefractionPathThroughput >= RefractionPassThroughputThreshold) && RefractionIndex < Context.MaxRefractionBounces); ++RefractionIndex)
#endif
{
float ReflectionPathRoughness = 0.0f;
float3 ReflectionPathThroughput = 1.0f;
float3 NextRefractionPathVertexThroughput = 1.0f;
for (uint ReflectionBounceIndex = 0; ReflectionBounceIndex < Context.MaxReflectionBounces; ++ReflectionBounceIndex)
{
float NextReflectionRayAlpha = 0.0f;
FPackedMaterialClosestHitPayload Payload = (FPackedMaterialClosestHitPayload)0;
Payload.SetLumenPayload();
Payload.SetIndirectLightingRay();
Payload.SetRandom(0.5f); // Is an actual random number needed?
#if RECURSIVE_REFRACTION_TRACES
Context.InstanceMask |= RAY_TRACING_MASK_TRANSLUCENT;
#else
Payload.SetIgnoreTranslucentMaterials();
#endif
if (Context.bHitLightingSkylight)
{
Payload.SetEnableSkyLightContribution();
}
if (Context.bIsFarFieldRay)
{
TraceLumenHitLightingRay(FarFieldTLAS, Ray, Context, Bookmark, Payload);
}
else
{
TraceLumenHitLightingRay(TLAS, Ray, Context, Bookmark, Payload);
}
#if AVOID_SELF_INTERSECTIONS_MODE == AVOID_SELF_INTERSECTIONS_MODE_RETRACE
if (ReflectionBounceIndex == 0
&& RefractionIndex == 0
&& !Context.bIsFarFieldRay
&& Payload.IsHit())
{
float SkipDistance = -1;
if (Payload.IsTwoSided() && Payload.HitT < LumenHardwareRayTracingUniformBuffer.SkipTwoSidedHitDistance)
{
SkipDistance = Payload.HitT + 0.01f;
}
else if (!Payload.IsTwoSided() && !Payload.IsFrontFace() && Payload.HitT < LumenHardwareRayTracingUniformBuffer.SkipBackFaceHitDistance)
{
SkipDistance = LumenHardwareRayTracingUniformBuffer.SkipBackFaceHitDistance;
}
if (SkipDistance > 0.0f)
{
float PrevTMin = Ray.TMin;
Ray.TMin = SkipDistance;
TraceLumenHitLightingRay(TLAS, Ray, Context, Bookmark, Payload);
Ray.TMin = PrevTMin;
}
}
#endif
if (Payload.IsHit() && !(Payload.IsFrontFace() || Payload.IsTwoSided()))
{
// Skip backface hits and terminate this path if we forced two-sided to minimize leaking
Result.bIsHit = true;
Result.TraceHitDistance = Payload.HitT;
Result.GeometryWorldNormal = Payload.GetGeometryNormal();
}
else if (Payload.IsHit())
{
float3 RayHitTranslatedWorldPosition = Ray.Origin + Ray.Direction * Payload.HitT;
const float SurfaceCoverage = Payload.GetBlendingMode() == RAY_TRACING_BLEND_MODE_OPAQUE ? 1.f : Payload.GetOpacity();
// Apply emissive material
float3 Radiance = Payload.GetRadiance();
FLumenHitLightingMaterial LumenMaterial = GetLumenHitLightingMaterial(Context, Payload, Ray);
// Accumulate roughness in order to terminate recursive rays earlier on rough surfaces
ReflectionPathRoughness = 1.0f - (1.0f - ReflectionPathRoughness) * (1.0f - LumenMaterial.TopLayerRoughness);
#if RECURSIVE_REFLECTION_TRACES
// Blend between rough reflections approximation and a single reflection ray
if ((Context.bHitLightingDirectLighting || Context.bHitLightingSkylight) && ReflectionBounceIndex + 1 < Context.MaxReflectionBounces)
{
NextReflectionRayAlpha = LumenCombineReflectionsAlpha(ReflectionPathRoughness, /*bHasBackfaceDiffuse*/ false);
}
#endif
Radiance += CalculateLightingAtHit(TLAS, FarFieldTLAS, Ray, Context, RandSequence, LumenMaterial, NextReflectionRayAlpha, Payload);
Result.Radiance += SurfaceCoverage * RefractionPathThroughput * ReflectionPathThroughput * Radiance;
// Capture hit properties at first hit
if (ReflectionBounceIndex == 0)
{
Result.bIsHit = true;
Result.TraceHitDistance = Payload.HitT;
Result.GeometryWorldNormal = Payload.GetGeometryNormal();
#if RECURSIVE_REFRACTION_TRACES
// Prepare next refraction event
RefractionPos = RayHitTranslatedWorldPosition + RefractionDir * 0.05f;
if (Payload.IsFrontFace() || Payload.IsTwoSided())
{
// Update throughput only if hitting a front face or if the material is two sided.
// According to opacity
// Coverage needs to be factored into the refracted througput with Substrate.
NextRefractionPathVertexThroughput = 1.0f - SurfaceCoverage;
#if SUBTRATE_GBUFFER_FORMAT==1
NextRefractionPathVertexThroughput += LumenMaterial.RefractedThroughputPreCoverage * SurfaceCoverage;
#endif
}
#endif // RECURSIVE_REFRACTION_TRACES
}
if (NextReflectionRayAlpha > 0.0f)
{
// Fresnel
ReflectionPathThroughput *= SurfaceCoverage * LumenMaterial.TopLayerSpecularColor;
float3 ReflectedRayOrigin = Ray.Origin + Ray.Direction * Payload.HitT + 0.05f * Payload.GetGeometryNormal();
float GGXSamplingBias = 0.1f;
float2 E = RandomSequence_GenerateSample2D(RandSequence);
E.y *= 1.0f - GGXSamplingBias;
float3x3 TangentBasis = GetTangentBasis(Payload.GetWorldNormal());
float3 TangentV = mul(TangentBasis, -Ray.Direction);
float4 GGXSample = ImportanceSampleVisibleGGX(E, Pow2(LumenMaterial.TopLayerRoughness), TangentV);
float3 WorldH = mul(GGXSample.xyz, TangentBasis);
float3 ReflectedRayDirection = reflect(Ray.Direction, WorldH);
// Setup next ray
Ray.Origin = ReflectedRayOrigin;
Ray.Direction = ReflectedRayDirection;
Ray.TMin = 0.0f;
Ray.TMax = Ray.TMax;
}
else
{
break;
}
}
else
{
if (
ReflectionBounceIndex > 0
#if RECURSIVE_REFRACTION_TRACES
|| RefractionIndex > 0
#endif
)
{
Result.Radiance += RefractionPathThroughput * ReflectionPathThroughput * EvaluateSkyRadiance(Ray.Direction);
}
#if RECURSIVE_REFRACTION_TRACES
bStopRefraction = ReflectionBounceIndex==0 && RefractionIndex > 0; // Stop if this was a refraction event that caused hitting the sky
#endif
break;
}
} // Reflection loop
#if RECURSIVE_REFRACTION_TRACES
if (bStopRefraction)
{
break;
}
RefractionPathThroughput *= NextRefractionPathVertexThroughput;
Ray.Origin = RefractionPos;
Ray.Direction = RefractionDir;
Ray.TMin = 0.0f;
Ray.TMax = Ray.TMax;
#endif
} // Refraction loop
return Result;
}
FRayTracedLightingResult TraceAndCalculateRayTracedLighting(
in RaytracingAccelerationStructure TLAS,
in RaytracingAccelerationStructure FarFieldTLAS,
in FRayDesc Ray,
inout FRayTracedLightingContext Context)
{
Context.bUseBookmark = false;
FLumenRayHitBookmark Bookmark;
return TraceAndCalculateRayTracedLighting(TLAS, FarFieldTLAS, Ray, Context, Bookmark);
}
#endif // !COMPUTESHADER
/**
* Trace ray without using any material shaders
* Optionally handle alpha masking through surface cache
*/
FRayTracedLightingResult TraceSurfaceCacheRay(
in RaytracingAccelerationStructure TLAS,
FRayDesc Ray,
FRayTracedLightingContext Context)
{
FLumenMinimalRayResult MinimalRayResult = TraceLumenMinimalRay(TLAS, Ray, Context);
#if AVOID_SELF_INTERSECTIONS_MODE == AVOID_SELF_INTERSECTIONS_MODE_RETRACE
if (MinimalRayResult.bHit)
{
float SkipDistance = -1;
if (MinimalRayResult.bTwoSided)
{
// SkipBackFaceHitDistance doesn't work on two sided geometry, but we still need to avoid self-intersections with the Nanite fallback mesh
if (MinimalRayResult.HitT < LumenHardwareRayTracingUniformBuffer.SkipTwoSidedHitDistance)
{
SkipDistance = MinimalRayResult.HitT + 0.01f;
}
}
else if (!MinimalRayResult.bFrontFace)
{
// Avoid self-shadowing by skipping back face hits for some distance
if (MinimalRayResult.HitT < LumenHardwareRayTracingUniformBuffer.SkipBackFaceHitDistance)
{
SkipDistance = LumenHardwareRayTracingUniformBuffer.SkipBackFaceHitDistance;
}
}
if (SkipDistance > 0.0f)
{
float PrevTMin = Ray.TMin;
Ray.TMin = SkipDistance;
MinimalRayResult = TraceLumenMinimalRay(TLAS, Ray, Context);
Ray.TMin = PrevTMin;
}
}
#endif
FSurfaceCacheSample SurfaceCacheSample = InitSurfaceCacheSample();
if (MinimalRayResult.bHit && MinimalRayResult.bCompleted && !(Context.bIsShadowRay && !SURFACE_CACHE_ALPHA_MASKING))
{
SurfaceCacheSample = SampleLumenMinimalRayHit(Ray, Context, MinimalRayResult);
}
#if !RECURSIVE_REFRACTION_TRACES && SURFACE_CACHE_ALPHA_MASKING
// If we trace for translucent, then we want to consider all the surfaces and not do any Opacity based masking.
// If we trace only for opaque, we do a single re-trace when hit masked part of surface cache
if (MinimalRayResult.bHit && MinimalRayResult.bCompleted && SurfaceCacheSample.bValid && SurfaceCacheSample.Opacity < 0.5f)
{
Ray.TMin = MinimalRayResult.HitT + 0.01f;
MinimalRayResult = TraceLumenMinimalRay(TLAS, Ray, Context);
SurfaceCacheSample = InitSurfaceCacheSample();
if (MinimalRayResult.bHit && MinimalRayResult.bCompleted)
{
SurfaceCacheSample = SampleLumenMinimalRayHit(Ray, Context, MinimalRayResult);
}
}
#endif
return CreateRayTracedLightingResult(Ray, Context, MinimalRayResult, SurfaceCacheSample);
}
/**
* Trace far field ray without using any material shaders
*/
FRayTracedLightingResult TraceSurfaceCacheFarFieldRay(
in RaytracingAccelerationStructure TLAS,
FRayDesc Ray,
FRayTracedLightingContext Context)
{
Context.bIsFarFieldRay = true;
#if FAR_FIELD_OCCLUSION_ONLY
{
Context.bAcceptFirstHitAndEndSearch = true;
}
#endif
FLumenMinimalRayResult MinimalRayResult = TraceLumenMinimalRay(TLAS, Ray, Context);
FSurfaceCacheSample SurfaceCacheSample = InitSurfaceCacheSample();
if (!FAR_FIELD_OCCLUSION_ONLY && MinimalRayResult.bHit)
{
SurfaceCacheSample = SampleLumenMinimalRayHit(Ray, Context, MinimalRayResult);
}
return CreateRayTracedLightingResult(Ray, Context, MinimalRayResult, SurfaceCacheSample);
}