// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "RayTracingCommon.ush" #include "../DeferredLightingCommon.ush" #include "../PathTracing/Utilities/PathTracingRandomSequence.ush" #include "../LightShaderParameters.ush" #include "RayTracingDirectionalLight.ush" #include "RayTracingRectLight.ush" #include "RayTracingSphereLight.ush" #include "RayTracingCapsuleLight.ush" #include "RayTracingSkyLightEvaluation.ush" #include "RayTracingLightCullingCommon.ush" #include "/Engine/Shared/LightDefinitions.h" #include "/Engine/Shared/RayTracingTypes.h" #ifndef ENABLE_FAR_FIELD_TRACING #define ENABLE_FAR_FIELD_TRACING 0 #endif // ENABLE_FAR_FIELD_TRACING FDeferredLightData GetRayTracingDeferredLightData( int LightIndex, bool bIndirectLighting, inout uint OutLightType, inout uint OutLightMissShaderIndex) { FDeferredLightData LightData = (FDeferredLightData)0; FRTLightingData RayTracingLightData = RaytracingLightGridData.SceneLights[LightIndex]; const uint LightType = RayTracingLightData.Type; LightData.TranslatedWorldPosition = RayTracingLightData.TranslatedLightPosition; LightData.InvRadius = RayTracingLightData.InvRadius; LightData.Color = RayTracingLightData.LightColor * (bIndirectLighting ? RayTracingLightData.IndirectLightScale : 1.0f); LightData.FalloffExponent = RayTracingLightData.FalloffExponent; LightData.Direction = RayTracingLightData.Direction; LightData.Tangent = RayTracingLightData.Tangent; LightData.SpotAngles = RayTracingLightData.SpotAngles; LightData.SourceRadius = RayTracingLightData.SourceRadius; LightData.SourceLength = RayTracingLightData.SourceLength; LightData.SoftSourceRadius = RayTracingLightData.SoftSourceRadius; LightData.DiffuseScale = UnpackFloat2FromUInt(RayTracingLightData.DiffuseSpecularScale).x; LightData.SpecularScale = UnpackFloat2FromUInt(RayTracingLightData.DiffuseSpecularScale).y; LightData.RectLightData.BarnCosAngle = RayTracingLightData.RectLightBarnCosAngle; LightData.RectLightData.BarnLength = RayTracingLightData.RectLightBarnLength; LightData.DistanceFadeMAD = UnpackFloat2FromUInt(RayTracingLightData.DistanceFadeMAD); LightData.ShadowMapChannelMask = float4(0, 0, 0, 0); LightData.ShadowedBits = 0; // Not lit dynamic shadows LightData.ContactShadowLength = 0.0; LightData.ContactShadowLengthInWS = false; LightData.ContactShadowCastingIntensity = 1.0f; LightData.ContactShadowNonCastingIntensity = 0.0f; LightData.bRadialLight = (LightType != LIGHT_TYPE_DIRECTIONAL); LightData.bSpotLight = (LightType == LIGHT_TYPE_SPOT); LightData.bRectLight = (LightType == LIGHT_TYPE_RECT); if (LightType == LIGHT_TYPE_DIRECTIONAL) { LightData.bInverseSquared = false; } else { LightData.bInverseSquared = LightData.FalloffExponent == 0; } LightData.RectLightData.AtlasData.AtlasUVOffset = RayTracingLightData.RectLightAtlasUVOffset; LightData.RectLightData.AtlasData.AtlasUVScale = RayTracingLightData.RectLightAtlasUVScale; LightData.RectLightData.AtlasData.AtlasMaxLevel = RayTracingLightData.RectLightAtlasMaxLevel; LightData.IESAtlasIndex = RayTracingLightData.IESAtlasIndex; OutLightType = LightType; OutLightMissShaderIndex = RayTracingLightData.LightMissShaderIndex; return LightData; } FDeferredLightData GetRayTracingDeferredLightData( int LightIndex, bool bIndirectLighting, inout uint OutLightType) { uint LightMissShaderIndex = 0; return GetRayTracingDeferredLightData(LightIndex, bIndirectLighting, OutLightType, LightMissShaderIndex); } float3 GenerateReflectedRayDirection( float3 IncidentDirection, float3 WorldNormal, float Roughness, float2 RandSample ) { float3 RayDirection; if (Roughness < 0.001) //ReflectionSmoothClamp) { RayDirection = reflect(IncidentDirection, WorldNormal); } else { float3 N = WorldNormal; float3 V = -IncidentDirection; float2 E = RandSample; float3x3 TangentBasis = GetTangentBasis(N); float3 TangentV = mul(TangentBasis, V); float NoV = saturate(dot(V, WorldNormal)); float4 Sample = ImportanceSampleVisibleGGX(E, Pow2(Roughness), TangentV); float3 H = mul(Sample.xyz, TangentBasis); float3 L = 2 * dot(V, H) * H - V; RayDirection = L; } return RayDirection; } void TraceShadowRayMissShaderLighting( in FRayDesc Ray, in uint RayFlags, in uint InstanceInclusionMask, in RaytracingAccelerationStructure TLAS, in uint MissShaderIndex, inout FPackedMaterialClosestHitPayload PackedPayload) { TraceRay ( TLAS, RayFlags, InstanceInclusionMask, RAY_TRACING_SHADER_SLOT_SHADOW, RAY_TRACING_NUM_SHADER_SLOTS, MissShaderIndex, Ray.GetNativeDesc(), PackedPayload ); } // Returns xyz: Direction, w: RayLength float4 SampleAreaLightDirection( in FDeferredLightData LightData, in float3 TranslatedWorldPosition, in float3 WorldNormal, in uint LightType, inout RandomSequence RandSequence ) { float3 ShadowRayDirection = 0.0; float3 RayOrigin = float3(0,0,0); float RayTMin = 0; float RayTMax = 0; float RayPdf = 0; float2 RandSample = RandomSequence_GenerateSample2D(RandSequence); FLightShaderParameters LightParameters; LightParameters.TranslatedWorldPosition = LightData.TranslatedWorldPosition; LightParameters.SpotAngles = LightData.SpotAngles; LightParameters.SourceRadius = LightData.SourceRadius; LightParameters.SourceLength = LightData.SourceLength; LightParameters.Tangent = LightData.Tangent; LightParameters.Direction = LightData.Direction; if (LightType == LIGHT_TYPE_DIRECTIONAL) { float ShadowSourceAngleFactor = LightData.RectLightData.BarnCosAngle; LightParameters.SourceRadius *= ShadowSourceAngleFactor; GenerateDirectionalLightOcclusionRay( LightParameters, TranslatedWorldPosition, WorldNormal, RandSample, /* out */ RayOrigin, /* out */ ShadowRayDirection, /* out */ RayTMin, /* out */ RayTMax); } else if (LightType == LIGHT_TYPE_POINT || LightType == LIGHT_TYPE_SPOT) { // NOTE: Spot cone is being checked before calling this function, so we know we are inside the cone if (LightParameters.SourceLength > 0.0) { GenerateCapsuleLightOcclusionRayWithSolidAngleSampling( LightParameters, TranslatedWorldPosition, WorldNormal, RandSample, /* out */ RayOrigin, /* out */ ShadowRayDirection, /* out */ RayTMin, /* out */ RayTMax, /* out */ RayPdf); } else { GenerateSphereLightOcclusionRayWithSolidAngleSampling( LightParameters, TranslatedWorldPosition, WorldNormal, RandSample, /* out */ RayOrigin, /* out */ ShadowRayDirection, /* out */ RayTMin, /* out */ RayTMax, /* out */ RayPdf); } } else if (LightType == LIGHT_TYPE_RECT) { // fill in extra parameters used only for rect lights LightParameters.RectLightBarnLength = LightData.RectLightData.BarnLength; LightParameters.RectLightBarnCosAngle = LightData.RectLightData.BarnCosAngle; GenerateRectLightOcclusionRay( LightParameters, TranslatedWorldPosition, WorldNormal, RandSample, /* out */ RayOrigin, /* out */ ShadowRayDirection, /* out */ RayTMin, /* out */ RayTMax, /* out */ RayPdf); } return float4(ShadowRayDirection, RayTMax); } float3 ComputeIndirectLighting( in float3 TranslatedWorldPosition, in float3 ViewDirection, in RaytracingAccelerationStructure TLAS, in uint2 PixelCoord, in FPackedMaterialClosestHitPayload Payload, in bool bRayTraceSkyLightContribution, in bool bDecoupleSampleGeneration) #if SUBTRATE_GBUFFER_FORMAT==1 { // SUBSTRATE_TODO return 0; } #else { float3 IndirectLighting = float3(0.0f, 0.0f, 0.0f); // Payload indirect irradiance contribution float3 DiffuseColor = Payload.GetDiffuseColor(); if (Payload.GetShadingModelID() == SHADINGMODELID_CLOTH) { float4 CustomData = Payload.GetCustomData(); DiffuseColor += CustomData.rgb * CustomData.a; } IndirectLighting += DiffuseColor * Payload.GetIndirectIrradiance(); // Ray traced sky light contribution if (bRayTraceSkyLightContribution) { FGBufferData GBufferData = GetGBufferDataFromPayload(Payload); // Evaluate the Sky Light at the surface point const bool bGBufferSampleOrigin = false; const float DeviceZ = 0.0f; // No camera related depth needed since sample is not from g-buffer float3 ExitantRadiance; float3 DiffuseExitantRadiance; float AmbientOcclusion; float HitDistance; SkyLightEvaluate( PixelCoord, SkyLight.SamplesPerPixel, TranslatedWorldPosition, GBufferData.WorldNormal, ViewDirection, GBufferData, TLAS, bGBufferSampleOrigin, DeviceZ, bDecoupleSampleGeneration, ExitantRadiance, DiffuseExitantRadiance, AmbientOcclusion, HitDistance); // Add the diffuse exitant radiance to the contribution IndirectLighting += DiffuseExitantRadiance; } return IndirectLighting; } #endif bool HasBackfaceDiffuse(FPackedMaterialClosestHitPayload Payload) { #if SUBTRATE_GBUFFER_FORMAT==1 { #if SUBSTRATE_MATERIALCONTAINER_IS_VIEWRESOURCE == 0 FSubstrateAddressing SubstrateAddressing = GetSubstratePixelDataByteOffset(0,0,0); FSubstratePixelHeader SubstratePixelHeader = UnpackSubstrateHeaderIn(Payload.SubstrateData, SubstrateAddressing, Payload.SubstrateData); const uint BSDFType = SubstratePixelHeader.SubstrateGetBSDFType(); return BSDFType == SUBSTRATE_BSDF_TYPE_SLAB && SubstratePixelHeader.HasSubsurface(); #else return false; #endif } #else { const uint ShadingModelID = Payload.GetShadingModelID(); return ShadingModelID == SHADINGMODELID_TWOSIDED_FOLIAGE || ShadingModelID == SHADINGMODELID_SUBSURFACE; } #endif } float3 ComputeDirectLighting( in float3 TranslatedWorldPosition, in float3 ViewDirection, in FRayCone RayCone, in RaytracingAccelerationStructure TLAS, in RaytracingAccelerationStructure FarFieldTLAS, inout FPackedMaterialClosestHitPayload Payload, inout RandomSequence RandSequence, in uint ShadowsType, in uint ShadowsTranslucencyType, float ShadowMaxNormalBias, float MaxTraceDistance) { float3 DirectLighting = (float3)0; // Repurpose some fields in the material payload to pass parameters into lighting miss shader. // Backup the values that will be aliased with other entries float3 OldRadiance = Payload.GetRadiance(); #if SUBTRATE_GBUFFER_FORMAT==1 uint OldTransmittancePreCoverage = Payload.TransmittancePreCoverage; uint OldSceneInstanceIndex = Payload.SceneInstanceIndex; uint OldFlags = Payload.Flags; #else uint2 OldIndirectIrradiance = uint2(Payload.PackedIndirectIrradiance[0], Payload.PackedIndirectIrradiance[1]); uint OldFlags = Payload.FlagsAndMipBias; #endif uint OldRayCone = Payload.PackedRayCone; Payload.SetRadiance(float3(0, 0, 0)); Payload.SetRayDirection(ViewDirection); FCulledLightList CullData = FCulledLightList::Create(TranslatedWorldPosition); const uint LightCount = CullData.GetNumLights(); for (uint Index = 0; WaveActiveAnyTrue(Index < LightCount); Index++) { uint Lit = 0; uint LightIndex = 0; LightIndex = CullData.GetLightIndex(Index, Lit); uint LightType = 0; uint LightMissShaderIndex = 0; FDeferredLightData LightData = GetRayTracingDeferredLightData(LightIndex, Payload.IsIndirectLightingRay(), LightType, LightMissShaderIndex); float3 ShadowRayDirection; // ToLight should not be normalized because its length is used to compute the shadow ray TMax float3 ToLight = LightData.TranslatedWorldPosition - TranslatedWorldPosition; float LightMask = 1.0; if (LightType == LIGHT_TYPE_DIRECTIONAL) { ShadowRayDirection = LightData.Direction; ToLight = LightData.Direction * MaxTraceDistance; } else { LightMask = GetLocalLightAttenuation(TranslatedWorldPosition, LightData, ToLight, ShadowRayDirection); // Skip the light sample that does not contribute anything due to attenuation. if (LightMask <= 0.0) { Lit = 0; } } const bool bHasBackfaceDiffuse = HasBackfaceDiffuse(Payload); const bool bBackFace = dot(Payload.GetWorldNormal(), normalize(ToLight)) <= 0; // We can skip the light sample pointing backwards if we don't need to compute backface diffuse (e.g. SHADINGMODELID_TWOSIDED_FOLIAGE) if (bBackFace && !bHasBackfaceDiffuse) { Lit = 0; } if (WaveActiveAllTrue(Lit == 0)) { continue; } float ShadowRayLength = 0.0; if (ShadowsType == RAY_TRACING_SHADOWS_TYPE_SOFT) { // TODO: Make a permutation for this case? It adds pressure on the live state when not used float4 Result = SampleAreaLightDirection(LightData, TranslatedWorldPosition, Payload.GetWorldNormal(), LightType, RandSequence); ShadowRayDirection = Result.xyz; ShadowRayLength = Result.w; } else { // hard shadow or no shadow -- just trace toward light center ShadowRayLength = length(ToLight); } if (ShadowRayLength == 0) { Lit = 0; } const bool ApplyShadow = (Lit && ShadowsType != RAY_TRACING_SHADOWS_TYPE_OFF); FRayDesc ShadowRay; ShadowRay.Origin = TranslatedWorldPosition; ShadowRay.Direction = ShadowRayDirection; ShadowRay.TMin = 1e-4f; ShadowRay.TMax = (ApplyShadow) ? ShadowRayLength : ShadowRay.TMin; // Always bias towards the light ApplyPositionBias(ShadowRay, bBackFace ? -Payload.GetWorldNormal() : Payload.GetWorldNormal(), ShadowMaxNormalBias); uint RayFlags = RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH // Terminate immediatly after detecting a hit | (ShadowsTranslucencyType == RAY_TRACING_SHADOWS_TRANSLUCENCY_TYPE_OPAQUE ? RAY_FLAG_FORCE_OPAQUE : 0) // Optionally skip Any Hit Shader | RAY_FLAG_SKIP_CLOSEST_HIT_SHADER; // Skip Closest Hit shader, only run Any Hit shader const uint TranslucencyMask = (ShadowsTranslucencyType == RAY_TRACING_SHADOWS_TRANSLUCENCY_TYPE_FRACTIONAL_VISIBILITY ? RAY_TRACING_MASK_TRANSLUCENT_SHADOW : 0); const uint InstanceInclusionMask = (ApplyShadow) ? (RAY_TRACING_MASK_SHADOW | TranslucencyMask) : 0; Payload.SetShadowRay(); Payload.SetMinimalPayloadMode(); Payload.SetShadowVisibility(1.0); Payload.HitT = 0.0; #if !ENABLE_TWO_SIDED_GEOMETRY RayFlags |= RAY_FLAG_CULL_FRONT_FACING_TRIANGLES; #endif // !ENABLE_TWO_SIDED_GEOMETRY // Light index is packed into IndirectIrradiance as well, so is covered by the save/restore of IrradianceColor above Payload.SetLightIndex(LightIndex); #if ENABLE_FAR_FIELD_TRACING bool bMissFarField = true; { // Rebase origin for far-field rays FRayDesc FarFieldShadowRay = ShadowRay; FDefaultPayload DefaultPayload = (FDefaultPayload)0; TraceRay(FarFieldTLAS, RayFlags, InstanceInclusionMask, RAY_TRACING_SHADER_SLOT_SHADOW, RAY_TRACING_NUM_SHADER_SLOTS, RAY_TRACING_MISS_SHADER_SLOT_DEFAULT, FarFieldShadowRay.GetNativeDesc(), DefaultPayload); bMissFarField = DefaultPayload.IsMiss(); } // use the bound miss shader lighting evaluation if lighting, else nothing if (bMissFarField) #endif // ENABLE_FAR_FIELD_TRACING { uint MissShaderIndex = Lit ? LightMissShaderIndex : RAY_TRACING_MISS_SHADER_SLOT_DEFAULT; TraceShadowRayMissShaderLighting(ShadowRay, RayFlags, InstanceInclusionMask, TLAS, MissShaderIndex, Payload); } } DirectLighting = Payload.GetRadiance(); Payload.SetRadiance(OldRadiance); // Restore aliased fields #if SUBTRATE_GBUFFER_FORMAT==1 Payload.SceneInstanceIndex = OldSceneInstanceIndex; Payload.TransmittancePreCoverage = OldTransmittancePreCoverage; Payload.Flags = OldFlags; #else Payload.FlagsAndMipBias = OldFlags; Payload.PackedIndirectIrradiance[0] = OldIndirectIrradiance.x; // Set back indirect irradiance (aliased with ShadowVisibility and LightIndex) Payload.PackedIndirectIrradiance[1] = OldIndirectIrradiance.y; #endif Payload.PackedRayCone = OldRayCone; return DirectLighting; } void ComputeBottomLayerMaterialProperties(FRayDesc Ray, inout FMaterialClosestHitPayload Payload) { // #dxr_todo: Remove me } void AccumulateResults( inout FPackedMaterialClosestHitPayload Payload, in float3 TranslatedWorldPosition, in float3 ViewDirection, in RaytracingAccelerationStructure TLAS, in RaytracingAccelerationStructure FarFieldTLAS, inout RandomSequence RandSequence, in uint2 PixelCoord, in float ShadowMaxNormalBias, float MaxTraceDistance, in uint ShadowsType, in uint ShadowsTranslucencyType, in uint ShouldDoDirectLighting, in uint ShouldDoEmissiveAndIndirectLighting, in bool bRayTraceSkyLightContribution, in bool bDecoupleSampleGeneration, inout FRayCone RayCone, inout float3 Radiance) { if (Payload.IsMiss()) { return; } float3 DirectLighting = 0; if (ShouldDoDirectLighting && Payload.IsValid()) { // Save and restore original payload HitT, as it's modified during shadow ray tracing float OldHitT = Payload.HitT; DirectLighting = ComputeDirectLighting(TranslatedWorldPosition, ViewDirection, RayCone, TLAS, FarFieldTLAS, Payload, RandSequence, ShadowsType, ShadowsTranslucencyType, ShadowMaxNormalBias, MaxTraceDistance); Payload.HitT = OldHitT; } // Transform NaNs to black, transform negative colors to black. DirectLighting = -min(-DirectLighting, float3(0, 0, 0)); Radiance += DirectLighting; if (ShouldDoEmissiveAndIndirectLighting) { // Emissive & indirect contribution Radiance += Payload.GetRadiance() * Payload.GetOpacity(); // Indirect contribution const float3 IndirectLighting = ComputeIndirectLighting( TranslatedWorldPosition, ViewDirection, TLAS, PixelCoord, Payload, bRayTraceSkyLightContribution, bDecoupleSampleGeneration); Radiance += IndirectLighting; } } FMaterialClosestHitPayload TraceRayAndAccumulateResults( in FRayDesc Ray, in RaytracingAccelerationStructure TLAS, in RaytracingAccelerationStructure FarFieldTLAS, in uint RayFlags, in uint InstanceInclusionMask, inout RandomSequence RandSequence, in uint2 PixelCoord, in float ShadowMaxNormalBias, float MaxTraceDistance, in uint ShadowsType, in uint ShadowsTranslucencyType, in uint ShouldDoDirectLighting, in uint ShouldDoEmissiveAndIndirectLighting, in bool bRayTraceSkyLightContribution, in bool bDecoupleSampleGeneration, in bool bIsCameraRay, inout FRayCone RayCone, in bool bEnableSkyLightContribution, inout float3 Radiance) { if (bRayTraceSkyLightContribution) { // Disable precomputed sky light contribution from hit shaders when ray tracing sky light contribution bEnableSkyLightContribution = false; } FPackedMaterialClosestHitPayload Payload = (FPackedMaterialClosestHitPayload)0; TraceMaterialRayPacked( Payload, TLAS, RayFlags, InstanceInclusionMask, Ray, RayCone, bEnableSkyLightContribution, bIsCameraRay); float3 TranslatedWorldPosition = Ray.Origin + Ray.Direction * Payload.HitT; float3 ViewDirection = Ray.Direction; AccumulateResults( Payload, TranslatedWorldPosition, ViewDirection, TLAS, FarFieldTLAS, RandSequence, PixelCoord, ShadowMaxNormalBias, MaxTraceDistance, ShadowsType, ShadowsTranslucencyType, ShouldDoDirectLighting, ShouldDoEmissiveAndIndirectLighting, bRayTraceSkyLightContribution, bDecoupleSampleGeneration, RayCone, Radiance); return UnpackRayTracingPayload(Payload, Ray); }