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

604 lines
19 KiB
HLSL

// 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);
}