Files
UnrealEngine/Engine/Shaders/Private/PathTracing/PathTracingDebug.usf
2025-05-18 13:04:45 +08:00

474 lines
18 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#define LIGHT_LOOP_METHOD 2 // 0: stochastic (like ref PT) 1: loop over all lights 2: RIS (take N samples but trace a single shadow ray)
#define USE_BSDF_SAMPLING 1 // 0: light sampling only (no MIS) 1: light sampling and BSDF sampling (with MIS)
#define USE_PATH_TRACING_LIGHT_GRID 1
#define USE_RAY_TRACING_DECAL_GRID 1
#include "../Common.ush"
#include "../BlueNoise.ush"
#include "../RayTracing/RayTracingCommon.ush"
#include "../RayTracing/RayTracingDecalGrid.ush"
#include "PathTracingCommon.ush"
#include "Material/PathTracingMaterialSampling.ush"
#include "Utilities/PathTracingRandomSequence.ush"
#include "Utilities/PathTracingRIS.ush"
#include "Light/PathTracingLightSampling.ush"
#include "Light/PathTracingLightGrid.ush"
#if PATH_TRACER_USE_SER
#define NV_HITOBJECT_USE_MACRO_API
#include "/Engine/Shared/ThirdParty/NVIDIA/nvHLSLExtns.h"
#endif
RWTexture2D<float4> RWSceneColor;
RWTexture2D<float> RWSceneDepth;
RaytracingAccelerationStructure TLAS;
int DebugMode;
int NumLightSamples;
int NumIndirectSamples;
float3 TraceShadowRay(FRayDesc Ray, float PathRoughness, float PreviousPathRoughness, uint MissShaderIndex, bool bCastShadows)
{
if (!bCastShadows && MissShaderIndex == 0)
{
// no work to do
return 1.0;
}
const uint RayFlags = RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH | RAY_FLAG_SKIP_CLOSEST_HIT_SHADER;
const uint InstanceInclusionMask = PATHTRACER_MASK_SHADOW;
const uint RayContributionToHitGroupIndex = RAY_TRACING_SHADER_SLOT_SHADOW;
const uint MultiplierForGeometryContributionToShaderIndex = RAY_TRACING_NUM_SHADER_SLOTS;
FPackedPathTracingPayload PackedPayload = InitPathTracingVisibilityPayload(PathRoughness);
if (!bCastShadows)
{
// ray should not cast shadows, make it degenerate so we can still run the miss shader
Ray.TMin = POSITIVE_INFINITY;
Ray.TMax = POSITIVE_INFINITY;
}
TraceRay(
TLAS,
RayFlags,
InstanceInclusionMask,
RayContributionToHitGroupIndex,
MultiplierForGeometryContributionToShaderIndex,
MissShaderIndex,
Ray.GetNativeDesc(),
PackedPayload);
if (PackedPayload.IsHit())
{
// We didn't run the miss shader, therefore we must have hit something opaque (or reached full opacity)
return 0.0;
}
float3 Throughput = PackedPayload.GetRayThroughput();
return Throughput;
}
struct PathTracingResult {
float4 Radiance;
float Z;
};
PathTracingResult MakeResult(float3 Radiance, float Alpha, float Z)
{
PathTracingResult Result;
Result.Radiance.rgb = Radiance;
Result.Radiance.a = Alpha;
Result.Z = Z;
return Result;
}
float3 GetLightingChannelMaskDebugColor(int LightingChannelMask)
{
float r = LightingChannelMask & 1 ? 1.0 : 0.1;
float g = LightingChannelMask & 2 ? 1.0 : 0.1;
float b = LightingChannelMask & 4 ? 1.0 : 0.1;
return float3(r, g, b);
}
float3 AnisoDebugColor(float InAniso)
{
// Follow same mapping than raster buffer visualization (BP located in Engine/Contents/Anisotropy)
float AniG = saturate( InAniso);
float AniB = saturate(-InAniso);
return float3(0.0, pow(AniG, 2.2), pow(AniB, 2.2));
}
#define INCLUDE_TRANSLUCENT 0
PathTracingResult PathTracingDebugCore(FRayDesc Ray, uint2 LaunchIndex)
{
const uint RayFlags = RAY_FLAG_CULL_BACK_FACING_TRIANGLES;
#if INCLUDE_TRANSLUCENT
const uint InstanceInclusionMask = PATHTRACER_MASK_CAMERA | PATHTRACER_MASK_CAMERA_TRANSLUCENT;
#else
const uint InstanceInclusionMask = PATHTRACER_MASK_CAMERA;
#endif
const uint MissShaderIndex = 0;
FPackedPathTracingPayload PackedPayload = InitPathTracingPayload(PATHTRACER_SCATTER_CAMERA, 0.0);
{
RandomSequence RandSequence;
RandomSequence_Initialize(RandSequence, LaunchIndex, 0, View.StateFrameIndex, 1);
PackedPayload.SetStochasticSlabRand(RandomSequence_GenerateSample1D(RandSequence));
}
#if !INCLUDE_TRANSLUCENT
PackedPayload.SetFlag(PATH_TRACING_PAYLOAD_INPUT_FLAG_IGNORE_TRANSLUCENT);
#endif
TraceRay(
TLAS,
RayFlags,
InstanceInclusionMask,
RAY_TRACING_SHADER_SLOT_MATERIAL,
RAY_TRACING_NUM_SHADER_SLOTS,
MissShaderIndex,
Ray.GetNativeDesc(),
PackedPayload);
float4 Result = float4(0, 0, 0, 1);
float Z = 0;
if (PackedPayload.IsHit())
{
const float3 ViewZ = View.ViewToTranslatedWorld[2].xyz;
Z = ConvertToDeviceZ(dot(Ray.Origin + PackedPayload.HitT * Ray.Direction, ViewZ));
}
if (DebugMode == PATH_TRACER_DEBUG_VIZ_VOLUME_LIGHT_COUNT)
{
float TMax = PackedPayload.IsHit() ? PackedPayload.HitT : Ray.TMax;
int NumLights = 0;
for (int LightId = SceneInfiniteLightCount; LightId < SceneLightCount; LightId++)
{
FVolumeLightSampleSetup LightSetup = PrepareLightVolumeSample(LightId, Ray.Origin, Ray.Direction, Ray.TMin, TMax);
if (LightSetup.IsValid())
{
float LightProb = LightSetup.LightImportance * GetVolumetricScatteringIntensity(LightId);
if (LightProb > 0)
{
NumLights++;
if (NumLights == RAY_TRACING_LIGHT_COUNT_MAXIMUM)
{
// reached max
break;
}
}
}
}
float NumLightsMax = min(SceneLightCount - SceneInfiniteLightCount, RAY_TRACING_LIGHT_COUNT_MAXIMUM);
Result.rgb = NumLights > 0 ? BlueGreenRedColorMap(saturate(float(NumLights) / NumLightsMax)) : 0.18;
if (PackedPayload.IsHit())
{
Result.rgb *= abs(dot(PackedPayload.UnpackWorldNormal(), Ray.Direction));
}
return MakeResult(Result.rgb, Result.a, Z);
}
float LightPickingCdf[RAY_TRACING_LIGHT_COUNT_MAXIMUM];
float LightPickingCdfSum = 0;
float3 PathThroughput = 1;
if (PackedPayload.IsHit())
{
const float3 TranslatedWorldPos = Ray.Origin + PackedPayload.HitT * Ray.Direction;
const float3 WorldNormal = PackedPayload.UnpackWorldNormal();
const uint PrimitiveLightingChannelMask = PackedPayload.GetPrimitiveLightingChannelMask();
FLightLoopCount LightLoopCount = LightGridLookup(TranslatedWorldPos);
switch (DebugMode)
{
case PATH_TRACER_DEBUG_VIZ_RADIANCE: return MakeResult(PackedPayload.UnpackRadiance() * View.PreExposure, 0.0, Z);
case PATH_TRACER_DEBUG_VIZ_WORLD_NORMAL: return MakeResult(WorldNormal * 0.5 + 0.5, 0.0, Z);
case PATH_TRACER_DEBUG_VIZ_WORLD_SMOOTH_NORMAL: return MakeResult(PackedPayload.UnpackWorldSmoothNormal() * 0.5 + 0.5, 0.0, Z);
case PATH_TRACER_DEBUG_VIZ_WORLD_GEO_NORMAL: return MakeResult(PackedPayload.UnpackWorldGeoNormal() * 0.5 + 0.5, 0.0, Z);
default:
#if SUBSTRATE_ENABLED
// TODO: implement more visualizations for this case
case PATH_TRACER_DEBUG_VIZ_BASE_COLOR: return MakeResult(lerp(PackedPayload.UnpackDiffuseColor(), PackedPayload.UnpackSpecularColor(), F0RGBToMetallic(PackedPayload.UnpackSpecularColor())), 0.0, Z);
case PATH_TRACER_DEBUG_VIZ_DIFFUSE_COLOR: return MakeResult(PackedPayload.UnpackDiffuseColor(), 0.0, Z);
case PATH_TRACER_DEBUG_VIZ_SPECULAR_COLOR: return MakeResult(PackedPayload.UnpackSpecularColor(), 0.0, Z);
case PATH_TRACER_DEBUG_VIZ_METALLIC: return MakeResult(F0RGBToMetallic(PackedPayload.UnpackSpecularColor()), 0.0, Z);
case PATH_TRACER_DEBUG_VIZ_SPECULAR: return MakeResult(F0RGBToDielectricSpecular(PackedPayload.UnpackSpecularColor()), 0.0, Z);
case PATH_TRACER_DEBUG_VIZ_ROUGHNESS: return MakeResult(PackedPayload.UnpackRoughnessAniso().xyz, 0.0, Z);
case PATH_TRACER_DEBUG_VIZ_ANISOTROPY: return MakeResult(AnisoDebugColor(PackedPayload.UnpackRoughnessAniso().w), 0.0, Z);
#else
case PATH_TRACER_DEBUG_VIZ_BASE_COLOR: return MakeResult(PackedPayload.UnpackBaseColor(), 0.0, Z);
case PATH_TRACER_DEBUG_VIZ_DIFFUSE_COLOR: return MakeResult(PackedPayload.UnpackBaseColor() * (1 - PackedPayload.UnpackMetallic()), 0.0, Z);
case PATH_TRACER_DEBUG_VIZ_SPECULAR_COLOR: return MakeResult(lerp(0.08 * PackedPayload.UnpackSpecular(), PackedPayload.UnpackBaseColor(), PackedPayload.UnpackMetallic()), 0.0, Z);
case PATH_TRACER_DEBUG_VIZ_METALLIC: return MakeResult(PackedPayload.UnpackMetallic(), 0.0, Z);
case PATH_TRACER_DEBUG_VIZ_SPECULAR: return MakeResult(PackedPayload.UnpackSpecular(), 0.0, Z);
case PATH_TRACER_DEBUG_VIZ_ROUGHNESS: return MakeResult(PackedPayload.UnpackRoughness(), 0.0, Z);
case PATH_TRACER_DEBUG_VIZ_ANISOTROPY: return MakeResult(AnisoDebugColor(PackedPayload.UnpackAnisotropy()), 0.0, Z);
case PATH_TRACER_DEBUG_VIZ_CUSTOM_DATA0: return MakeResult(PackedPayload.UnpackCustomData0().rgb * View.PreExposure, PackedPayload.UnpackCustomData0().a, Z);
case PATH_TRACER_DEBUG_VIZ_CUSTOM_DATA1: return MakeResult(PackedPayload.UnpackCustomData1().rgb * View.PreExposure, PackedPayload.UnpackCustomData1().a, Z);
#endif
case PATH_TRACER_DEBUG_VIZ_OPACITY: return MakeResult(PackedPayload.UnpackTransparencyColor(), 0.0, Z);
case PATH_TRACER_DEBUG_VIZ_IOR: return MakeResult(PackedPayload.UnpackIor(), 0.0, Z);
case PATH_TRACER_DEBUG_VIZ_SHADING_MODEL: return MakeResult(GetShadingModelColor(PackedPayload.GetShadingModelID()), 0.0, Z);
case PATH_TRACER_DEBUG_VIZ_LIGHTING_CHANNEL_MASK: return MakeResult(GetLightingChannelMaskDebugColor(PackedPayload.GetPrimitiveLightingChannelMask()), 0.0, Z);
case PATH_TRACER_DEBUG_VIZ_WORLD_POSITION: {
float3 AbsoluteWorldPosition = DFDemote(DFFastSubtract(TranslatedWorldPos, ResolvedView.PreViewTranslation));
const float CellWidth = 10.0f; // 10cm
int3 Coord = int3(round(AbsoluteWorldPosition / CellWidth));
uint HashIndex = Rand3DPCG32(Coord).x;
float3 Color = HUEtoRGB((HashIndex & 0xFFFFFF) * 5.96046447754e-08);
Color *= abs(dot(WorldNormal, Ray.Direction));
return MakeResult(Color * View.PreExposure, 0.0, Z);
}
case PATH_TRACER_DEBUG_VIZ_PRIMARY_RAYS: break; // fallthrough to implementation below
case PATH_TRACER_DEBUG_VIZ_WORLD_TANGENT: return MakeResult(PackedPayload.UnpackWorldTangent() * 0.5 + 0.5, 0.0, Z);
case PATH_TRACER_DEBUG_VIZ_LIGHT_GRID_COUNT:
case PATH_TRACER_DEBUG_VIZ_LIGHT_GRID_AXIS:
{
float3 Color = LightGridVisualize(LightLoopCount, DebugMode == PATH_TRACER_DEBUG_VIZ_LIGHT_GRID_AXIS ? 4 : 1);
Color *= abs(dot(WorldNormal, Ray.Direction));
return MakeResult(Color, 0.0, Z);
}
case PATH_TRACER_DEBUG_VIZ_DECAL_GRID_COUNT:
case PATH_TRACER_DEBUG_VIZ_DECAL_GRID_AXIS:
{
FDecalLoopCount DecalLoopCount = DecalGridLookup(TranslatedWorldPos);
float3 Color = PackedPayload.IsDecalReceiver() ? DecalGridVisualize(DecalLoopCount, DebugMode == PATH_TRACER_DEBUG_VIZ_DECAL_GRID_AXIS ? 3 : 1) : 0.18;
Color *= abs(dot(WorldNormal, Ray.Direction));
return MakeResult(Color, 0.0, Z);
}
case PATH_TRACER_DEBUG_VIZ_HITKIND:
{
return MakeResult((PackedPayload.IsFrontFace() ? float3(0, 1, 0) : float3(1, 0, 0)) * abs(dot(WorldNormal, Ray.Direction)), 0.0, Z);
}
}
FPathTracingPayload Payload = UnpackPathTracingPayload(PackedPayload, Ray);
AdjustPayloadAfterUnpack(Payload, true);
#if LIGHT_LOOP_METHOD == 0
for (uint Index = 0, Num = LightLoopCount.NumLights; Index < Num; ++Index)
{
uint LightIndex = GetLightId(Index, LightLoopCount);
float LightEstimate = EstimateLight(LightIndex, TranslatedWorldPos, WorldNormal, PrimitiveLightingChannelMask, false);
LightPickingCdfSum += LightEstimate;
LightPickingCdf[Index] = LightPickingCdfSum;
}
if (LightPickingCdfSum > 0)
{
// init worked
int LightId;
float LightPickPdf = 0;
for (int Sample = 0; Sample < NumLightSamples; Sample++)
{
RandomSequence RandSequence;
RandomSequence_Initialize(RandSequence, LaunchIndex, Sample, View.StateFrameIndex, NumLightSamples);
float4 RandSample = RandomSequence_GenerateSample4D(RandSequence);
FMaterialSample MaterialSample = SampleMaterial(-Ray.Direction, Payload, RandSample.xyz);
SelectLight(RandSample.x * LightPickingCdfSum, LightLoopCount.NumLights, LightPickingCdf, LightId, LightPickPdf);
LightPickPdf /= LightPickingCdfSum;
float2 LightRandSample = RandSample.yz;
LightId = GetLightId(LightId, LightLoopCount);
#else
for (int Sample = 0; Sample < NumLightSamples; Sample++)
{
RandomSequence RandSequence;
RandomSequence_Initialize(RandSequence, LaunchIndex, Sample, View.StateFrameIndex, NumLightSamples);
float3 RandSample = RandomSequence_GenerateSample3D(RandSequence);
float2 LightRandSample = RandSample.xy;
#if LIGHT_LOOP_METHOD == 2
FRISContext TraceSample = InitRISContext(RandSample.z);
FRayDesc ShadowRaySample;
float3 ShadowRayContrib = 0;
uint ShadowRayLightId;
#endif
FMaterialSample MaterialSample = SampleMaterial(-Ray.Direction, Payload, RandSample.xyz);
for (uint Index = 0, Num = LightLoopCount.NumLights; Index < Num; ++Index)
{
uint LightId = GetLightId(Index, LightLoopCount);
const float LightPickPdf = 1;
#endif
FLightSample LightSample = SampleLight(LightId, LightRandSample, TranslatedWorldPos, WorldNormal);
if (LightSample.Pdf > 0)
{
LightSample.RadianceOverPdf /= LightPickPdf;
LightSample.Pdf *= LightPickPdf;
#if 1
FMaterialEval MaterialEval = EvalMaterial(-Ray.Direction, LightSample.Direction, Payload, 1.0);
float3 LightContrib = PathThroughput * LightSample.RadianceOverPdf * MaterialEval.Weight * MaterialEval.Pdf;
#if USE_BSDF_SAMPLING
LightContrib *= MISWeightPower(LightSample.Pdf, MaterialEval.Pdf);
#endif
#else
#if SUBSTRATE_ENABLED
float3 MaterialEvalWeight = PackedPayload.UnpackDiffuseColor();
#else
float3 MaterialEvalWeight = PackedPayload.UnpackBaseColor();
#endif
float MaterialEvalPdf = saturate(dot(WorldNormal, LightSample.Direction)) / PI;
float3 LightContrib = PathThroughput * LightSample.RadianceOverPdf * MaterialEvalWeight * MaterialEvalPdf;
#endif
{
FRayDesc ShadowRay;
ShadowRay.Origin = TranslatedWorldPos;
ShadowRay.Direction = LightSample.Direction;
ShadowRay.TMin = 0;
ShadowRay.TMax = LightSample.Distance;
ApplyRayBias(ShadowRay.Origin, PackedPayload.HitT, PackedPayload.UnpackWorldGeoNormal());
#if LIGHT_LOOP_METHOD == 2
float SelectionWeight = LightContrib.x + LightContrib.y + LightContrib.z;
if (TraceSample.Accept(SelectionWeight))
{
ShadowRaySample = ShadowRay;
ShadowRayContrib = LightContrib / SelectionWeight;
ShadowRayLightId = LightId;
}
#else
const bool bCastShadows = CastsShadow(LightId);
const uint MissShaderIndex = GetLightMissShaderIndex(LightId);
Result.xyz += LightContrib * TraceShadowRay(ShadowRay, 1.0, 0.0, MissShaderIndex, bCastShadows);
#endif
}
}
#if USE_BSDF_SAMPLING
{
FRayDesc ShadowRay;
ShadowRay.Origin = TranslatedWorldPos;
ShadowRay.Direction = MaterialSample.Direction;
ShadowRay.TMin = 0;
ShadowRay.TMax = RAY_DEFAULT_T_MAX;
ApplyRayBias(ShadowRay.Origin, PackedPayload.HitT, PackedPayload.UnpackWorldGeoNormal());
FLightHit LightResult = TraceLight(ShadowRay, LightId);
if (LightResult.IsHit())
{
ShadowRay.TMax = LightResult.HitT;
float3 LightContrib = PathThroughput * MaterialSample.Weight * LightResult.Radiance;
LightContrib *= MISWeightPower(MaterialSample.Pdf, LightResult.Pdf * LightPickPdf);
#if LIGHT_LOOP_METHOD == 2
float SelectionWeight = LightContrib.x + LightContrib.y + LightContrib.z;
if (TraceSample.Accept(SelectionWeight))
{
ShadowRaySample = ShadowRay;
ShadowRayContrib = LightContrib / SelectionWeight;
ShadowRayLightId = LightId;
}
#else
const bool bCastShadows = CastsShadow(LightId);
const uint MissShaderIndex = GetLightMissShaderIndex(LightId);
Result.xyz += LightContrib * TraceShadowRay(ShadowRay, 1.0, 0.0, MissShaderIndex, bCastShadows);
#endif
}
}
#endif // USE_BSDF_SAMPLING
}
#if LIGHT_LOOP_METHOD == 2
if (TraceSample.HasSample())
{
const bool bCastShadows = CastsShadow(ShadowRayLightId);
const uint MissShaderIndex = GetLightMissShaderIndex(ShadowRayLightId);
ShadowRayContrib *= TraceSample.GetNormalization();
Result.xyz += ShadowRayContrib * TraceShadowRay(ShadowRaySample, 1.0, 0.0, MissShaderIndex, bCastShadows);
}
#endif
}
Result.rgb = Result.rgb * (NumLightSamples > 1 ? 1.0 / NumLightSamples : 1.0) + PackedPayload.UnpackRadiance();
float3 IndirectRadiance = 0;
for (int Sample = 0; Sample < NumIndirectSamples; Sample++)
{
RandomSequence RandSequence;
RandomSequence_Initialize(RandSequence, LaunchIndex, Sample, View.StateFrameIndex, NumIndirectSamples);
float3 RandSample = RandomSequence_GenerateSample3D(RandSequence);
FMaterialSample MaterialSample = SampleMaterial(-Ray.Direction, Payload, RandSample.xyz);
const uint RayFlags = 0;
const uint InstanceInclusionMask = PATHTRACER_MASK_INDIRECT;
const uint RayContributionToHitGroupIndex = RAY_TRACING_SHADER_SLOT_MATERIAL;
const uint MultiplierForGeometryContributionToShaderIndex = RAY_TRACING_NUM_SHADER_SLOTS;
FRayDesc Ray;
Ray.Origin = TranslatedWorldPos;
Ray.Direction = MaterialSample.Direction;
Ray.TMin = 0;
Ray.TMax = RAY_DEFAULT_T_MAX;
ApplyRayBias(Ray.Origin, PackedPayload.HitT, PackedPayload.UnpackWorldGeoNormal());
FPackedPathTracingPayload PackedPayloadIndirect = InitPathTracingPayload(MaterialSample.ScatterType, MaterialSample.Roughness);
#if PATH_TRACER_USE_SER
NvHitObject Hit;
NvTraceRayHitObject(
TLAS,
RayFlags,
InstanceInclusionMask,
RayContributionToHitGroupIndex,
MultiplierForGeometryContributionToShaderIndex,
MissShaderIndex,
Ray.GetNativeDesc(),
PackedPayloadIndirect,
Hit);
NvReorderThread(Hit);
NvInvokeHitObject(TLAS, Hit, PackedPayloadIndirect);
#else
TraceRay(
TLAS,
RayFlags,
InstanceInclusionMask,
RayContributionToHitGroupIndex,
MultiplierForGeometryContributionToShaderIndex,
MissShaderIndex,
Ray.GetNativeDesc(),
PackedPayloadIndirect);
#endif
IndirectRadiance += MaterialSample.Weight * PackedPayloadIndirect.UnpackRadiance();
}
Result.rgb += IndirectRadiance * (NumIndirectSamples > 1 ? 1.0 / NumIndirectSamples : 1.0);
Result.a = 0;
}
return MakeResult(Result.rgb * View.PreExposure, Result.a, Z);
}
RAY_TRACING_ENTRY_RAYGEN(PathTracingDebugRG)
{
uint2 DispatchIdx = DispatchRaysIndex().xy;
const uint2 DispatchDim = DispatchRaysDimensions().xy;
ResolvedView = ResolveView();
uint2 LaunchIndex = DispatchIdx + View.ViewRectMin.xy;
const float2 AAJitter = 0.5; // trace through pixel center
const float2 ViewportUV = (LaunchIndex + AAJitter) * View.BufferSizeAndInvSize.zw;
PathTracingResult Result = PathTracingDebugCore(CreatePrimaryRay(ViewportUV), LaunchIndex);
RWSceneColor[DispatchIdx] = Result.Radiance;
RWSceneDepth[DispatchIdx] = Result.Z;
}