Open yasukichi opened 8 months ago
void FD3D12DynamicRHI::Init()
{
:
if (IsRHIDeviceNVIDIA() && bAllowVendorDevice)
{
const NvAPI_Status NvStatus = NvAPI_Initialize();
if (NvStatus == NVAPI_OK)
{
NvAPI_D3D12_IsNvShaderExtnOpCodeSupported(GetAdapter().GetD3DDevice(), NV_EXTN_OP_UINT64_ATOMIC, &bHasVendorSupportForAtomic64);
NvAPI_Status NvStatusSER = NvAPI_D3D12_IsNvShaderExtnOpCodeSupported(GetAdapter().GetD3DDevice(), NV_EXTN_OP_HIT_OBJECT_REORDER_THREAD, &GRHISupportsShaderExecutionReordering);
:
}
NvU32 DriverVersion = UINT32_MAX;
NvAPI_ShortString BranchString("");
NvAPI_SYS_GetDriverAndBranchVersion(&DriverVersion, BranchString);
:
} // if NVIDIA
:
}
## PrepareRTXDLumenReflections, PrepareRTXDILumenSurfaceCache
- C++(DeferredShadingRenderer.cpp)
```C++
void FDeferredShadingSceneRenderer::Render(FRDGBuilder& GraphBuilder)
{
:
FRDGTextureRef ViewFamilyTexture = TryCreateViewFamilyTexture(GraphBuilder, ViewFamily);
if (RendererOutput == ERendererOutput::DepthPrepassOnly)
{
:
}
else
{
GVRSImageManager.PrepareImageBasedVRS(GraphBuilder, ViewFamily, SceneTextures);
:
FRDGBufferRef DynamicGeometryScratchBuffer = nullptr;
#if RHI_RAYTRACING
// Async AS builds can potentially overlap with BasePass.
bool bNeedToWaitForRayTracingScene = DispatchRayTracingWorldUpdates(GraphBuilder, DynamicGeometryScratchBuffer);
/** Should be called somewhere before "WaitForRayTracingScene" */
SetupRayTracingLightDataForViews(GraphBuilder);
#endif
if (!bHasRayTracedOverlay)
{
#if RHI_RAYTRACING
// Lumen scene lighting requires ray tracing scene to be ready if HWRT shadows are desired
if (bNeedToWaitForRayTracingScene && Lumen::UseHardwareRayTracedSceneLighting(ViewFamily))
{
WaitForRayTracingScene(GraphBuilder, DynamicGeometryScratchBuffer);
bNeedToWaitForRayTracingScene = false;
}
#endif
LLM_SCOPE_BYTAG(Lumen);
BeginGatheringLumenSurfaceCacheFeedback(GraphBuilder, Views[0], LumenFrameTemporaries);
RenderLumenSceneLighting(GraphBuilder, LumenFrameTemporaries, InitViewTaskDatas.LumenDirectLighting);
}
:
// Shadows, lumen and fog after base pass
if (!bHasRayTracedOverlay)
{
#if RHI_RAYTRACING
// When Lumen HWRT is running async we need to wait for ray tracing scene before dispatching the work
if (bNeedToWaitForRayTracingScene && Lumen::UseAsyncCompute(ViewFamily) && Lumen::UseHardwareInlineRayTracing(ViewFamily))
{
WaitForRayTracingScene(GraphBuilder, DynamicGeometryScratchBuffer);
bNeedToWaitForRayTracingScene = false;
}
#endif // RHI_RAYTRACING
DispatchAsyncLumenIndirectLightingWork(
GraphBuilder,
CompositionLighting,
SceneTextures,
LumenFrameTemporaries,
LightingChannelsTexture,
bHasLumenLights,
AsyncLumenIndirectLightingOutputs);
:
#if RHI_RAYTRACING
// Lumen scene lighting requires ray tracing scene to be ready if HWRT shadows are desired
if (bNeedToWaitForRayTracingScene && Lumen::UseHardwareRayTracedSceneLighting(ViewFamily))
{
WaitForRayTracingScene(GraphBuilder, DynamicGeometryScratchBuffer);
bNeedToWaitForRayTracingScene = false;
}
#endif // RHI_RAYTRACING
}
:
#if RHI_RAYTRACING
// If Lumen did not force an earlier ray tracing scene sync, we must wait for it here.
if (bNeedToWaitForRayTracingScene)
{
WaitForRayTracingScene(GraphBuilder, DynamicGeometryScratchBuffer);
bNeedToWaitForRayTracingScene = false;
}
#endif // RHI_RAYTRACING
:
if (bRenderDeferredLighting)
{
:
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_Lighting));
RenderLights(GraphBuilder, SceneTextures, TranslucencyLightingVolumeTextures, LightingChannelsTexture, SortedLightSet);
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_AfterLighting));
:
}
:
}
C++(DeferredShadingRenderer.cpp)
void FDeferredShadingSceneRenderer::WaitForRayTracingScene(FRDGBuilder& GraphBuilder, FRDGBufferRef DynamicGeometryScratchBuffer)
{
check(bAnyRayTracingPassEnabled);
TRACE_CPUPROFILER_EVENT_SCOPE(FDeferredShadingSceneRenderer::WaitForRayTracingScene);
// Keep mask the same as what's already set (which will be the view mask) if TLAS updates should be masked to the view
RDG_GPU_MASK_SCOPE(GraphBuilder, GRayTracingMultiGpuTLASMask ? GraphBuilder.RHICmdList.GetGPUMask() : FRHIGPUMask::All());
SetupRayTracingPipelineStates(GraphBuilder);
const int32 ReferenceViewIndex = 0;
FViewInfo& ReferenceView = Views[ReferenceViewIndex];
bool bAnyLumenHardwareInlineRayTracingPassEnabled = false;
for (const FViewInfo& View : Views)
{
bAnyLumenHardwareInlineRayTracingPassEnabled |= Lumen::AnyLumenHardwareInlineRayTracingPassEnabled(Scene, View);
}
if (bAnyLumenHardwareInlineRayTracingPassEnabled)
{
SetupLumenHardwareRayTracingHitGroupBuffer(GraphBuilder, ReferenceView);
}
if (Lumen::UseHardwareRayTracing(ViewFamily))
{
SetupLumenHardwareRayTracingUniformBuffer(GraphBuilder, ReferenceView);
}
:
}
bool FDeferredShadingSceneRenderer::SetupRayTracingPipelineStates(FRDGBuilder& GraphBuilder) { : if (GRHISupportsRayTracingShaders) { // #dxr_todo: UE-72565: refactor ray tracing effects to not be member functions of DeferredShadingRenderer. // Should register each effect at startup and just loop over them automatically to gather all required shaders.
TArray<FRHIRayTracingShader*> RayGenShaders;
// We typically see ~120 raygen shaders, but allow some headroom to avoid reallocation if our estimate is wrong.
RayGenShaders.Reserve(256);
if (bIsPathTracing)
{
// This view only needs the path tracing raygen shaders as all other
// passes should be disabled.
PreparePathTracing(ViewFamily, *Scene, RayGenShaders);
}
else
{
// Path tracing is disabled, get all other possible raygen shaders
PrepareRayTracingDebug(RayGenShaders);
// These other cases do potentially depend on the camera position since they are
// driven by FinalPostProcessSettings, which is why we need to merge them across views
if (!IsForwardShadingEnabled(ShaderPlatform))
{
for (const FViewInfo& View : Views)
{
PrepareRayTracingReflections(View, *Scene, RayGenShaders);
PrepareSingleLayerWaterRayTracingReflections(View, *Scene, RayGenShaders);
PrepareRayTracingShadows(View, *Scene, RayGenShaders);
PrepareRayTracingAmbientOcclusion(View, RayGenShaders);
PrepareRayTracingSkyLight(View, *Scene, RayGenShaders);
PrepareRayTracingGlobalIllumination(View, RayGenShaders);
PrepareRayTracingGlobalIlluminationPlugin(View, RayGenShaders);
PrepareRayTracingTranslucency(View, RayGenShaders);
PrepareRayTracingVolumetricFogShadows(View, *Scene, RayGenShaders);
if (DoesPlatformSupportLumenGI(ShaderPlatform) && Lumen::UseHardwareRayTracing(ViewFamily))
{
PrepareLumenHardwareRayTracingScreenProbeGather(View, RayGenShaders);
PrepareLumenHardwareRayTracingShortRangeAO(View, RayGenShaders);
PrepareLumenHardwareRayTracingRadianceCache(View, RayGenShaders);
PrepareLumenHardwareRayTracingReflections(View, RayGenShaders);
PrepareLumenHardwareRayTracingVisualize(View, RayGenShaders);
PrepareRTXDILumenReflections(View, RayGenShaders);
}
PrepareRayTracingSampledDirectLighting(View, RayGenShaders);
}
}
DeduplicateRayGenerationShaders(RayGenShaders);
}
if (RayGenShaders.Num())
{
// Create RTPSO and kick off high-level material parameter binding tasks which will be consumed during RDG execution in BindRayTracingMaterialPipeline()
ReferenceView.RayTracingMaterialPipeline = CreateRayTracingMaterialPipeline(GraphBuilder, ReferenceView, RayGenShaders);
}
}
// Add deferred material gather shaders
if (GRHISupportsRayTracingShaders)
{
TArray<FRHIRayTracingShader*> DeferredMaterialRayGenShaders;
if (!IsForwardShadingEnabled(ShaderPlatform))
{
for (const FViewInfo& View : Views)
{
PrepareRayTracingReflectionsDeferredMaterial(View, *Scene, DeferredMaterialRayGenShaders);
PrepareRayTracingDeferredReflectionsDeferredMaterial(View, *Scene, DeferredMaterialRayGenShaders);
PrepareRayTracingGlobalIlluminationDeferredMaterial(View, DeferredMaterialRayGenShaders);
if (DoesPlatformSupportLumenGI(ShaderPlatform))
{
PrepareLumenHardwareRayTracingReflectionsDeferredMaterial(View, DeferredMaterialRayGenShaders);
PrepareLumenHardwareRayTracingRadianceCacheDeferredMaterial(View, DeferredMaterialRayGenShaders);
PrepareLumenHardwareRayTracingScreenProbeGatherDeferredMaterial(View, DeferredMaterialRayGenShaders);
PrepareLumenHardwareRayTracingVisualizeDeferredMaterial(View, DeferredMaterialRayGenShaders);
}
}
}
DeduplicateRayGenerationShaders(DeferredMaterialRayGenShaders);
if (DeferredMaterialRayGenShaders.Num())
{
ReferenceView.RayTracingMaterialGatherPipeline = CreateRayTracingDeferredMaterialGatherPipeline(GraphBuilder.RHICmdList, ReferenceView, DeferredMaterialRayGenShaders);
}
}
// Add Lumen hardware ray tracing materials
if (GRHISupportsRayTracingShaders)
{
TArray<FRHIRayTracingShader*> LumenHardwareRayTracingRayGenShaders;
if (DoesPlatformSupportLumenGI(ShaderPlatform))
{
for (const FViewInfo& View : Views)
{
PrepareLumenHardwareRayTracingVisualizeLumenMaterial(View, LumenHardwareRayTracingRayGenShaders);
PrepareLumenHardwareRayTracingRadianceCacheLumenMaterial(View, LumenHardwareRayTracingRayGenShaders);
PrepareLumenHardwareRayTracingTranslucencyVolumeLumenMaterial(View, LumenHardwareRayTracingRayGenShaders);
PrepareLumenHardwareRayTracingRadiosityLumenMaterial(View, LumenHardwareRayTracingRayGenShaders);
PrepareLumenHardwareRayTracingReflectionsLumenMaterial(View, LumenHardwareRayTracingRayGenShaders);
PrepareLumenHardwareRayTracingScreenProbeGatherLumenMaterial(View, LumenHardwareRayTracingRayGenShaders);
PrepareLumenHardwareRayTracingDirectLightingLumenMaterial(View, LumenHardwareRayTracingRayGenShaders);
PrepareRTXDILumenSurfaceCache(View, LumenHardwareRayTracingRayGenShaders);
}
}
DeduplicateRayGenerationShaders(LumenHardwareRayTracingRayGenShaders);
if (LumenHardwareRayTracingRayGenShaders.Num())
{
ReferenceView.LumenHardwareRayTracingMaterialPipeline = CreateLumenHardwareRayTracingMaterialPipeline(GraphBuilder.RHICmdList, ReferenceView, LumenHardwareRayTracingRayGenShaders);
}
}
// Initialize common resources used for lighting in ray tracing effects
for (int32 ViewIndex = 0; ViewIndex < AllFamilyViews.Num(); ++ViewIndex)
{
// TODO: It would make more sense for common ray tracing resources to be in a shared structure, rather than copied into each FViewInfo.
// A goal is to have the FViewInfo structure only be visible to the scene renderer that owns it, to avoid dependencies being created
// that could lead to maintenance issues or interfere with paralellism goals. For now, this works though...
FViewInfo* View = const_cast<FViewInfo*>(static_cast<const FViewInfo*>(AllFamilyViews[ViewIndex]));
// Send common ray tracing resources from reference view to all others.
if (View->bHasAnyRayTracingPass && View != &ReferenceView)
{
View->RayTracingMaterialPipeline = ReferenceView.RayTracingMaterialPipeline;
}
}
return true;
}
- C++(SampledLightRendering.cpp)
```C++
void FDeferredShadingSceneRenderer::PrepareRayTracingSampledDirectLighting(const FViewInfo& View, TArray<FRHIRayTracingShader*>& OutRayGenShaders)
{
// Declare all RayGen shaders that require material closest hit shaders to be bound
if (!ShouldRenderRayTracingSampledLighting())
{
return;
}
if (ShouldRenderRayTracedSampledLightingForTranslucency())
{
PrepareTranslucentRayTracingSampledDirectLighting(View, OutRayGenShaders);
}
const int32 LightingMode = CVarSampledLightingMode.GetValueOnRenderThread();
const int32 PresampleLightsMode = (int32)GetSampledLightPresamplingMode(View);
const bool bAvoidSelfIntersection = CVarRayTracingSampledLightingAvoidSelfIntersectionTraceDistance.GetValueOnRenderThread() != 0.0f;
const bool UsePairwiseMIS = CVarSampledLightingSpatialUsePairwiseMIS.GetValueOnRenderThread() != 0;
const bool AlwaysSampleDirLight = CVarSampledLightingAlwaysSampleDirLight.GetValueOnRenderThread() != 0;
const bool bEvaluateStrandBasedHair = CVarRayTracingSampledLightingHair.GetValueOnRenderThread() != 0;
const bool bUseBrdfSamples = ShouldUseBrdfSamples();
// strand based hair handles aniso independently and never can self intersect
if (bEvaluateStrandBasedHair)
{
FDirectLightRGS::FPermutationDomain PermutationVector;
PermutationVector.Set<FDirectLightRGS::FHairShadingDim>(true);
PermutationVector.Set<FDirectLightRGS::FRisSampleLocalLightsDim>(PresampleLightsMode);
PermutationVector.Set<FDirectLightRGS::FAvoidSelfIntersectionDim>(false);
PermutationVector.Set<FDirectLightRGS::FSampleDirectionalLightOnly>(false);
PermutationVector.Set<FDirectLightRGS::FAnisotropicMaterialsDim>(false);
PermutationVector.Set<FDirectLightRGS::FSupportBrdfRayDim>(false);
PermutationVector.Set<FDirectLightRGS::FHairLightingDim>(true);
auto RayGenShader = View.ShaderMap->GetShader<FDirectLightRGS>(PermutationVector);
OutRayGenShaders.Add(RayGenShader.GetRayTracingShader());
}
// aniso can change as a result of what is in the view, so always bind aniso and non-aniso versions if they can be present
for (int32 AnisoPermutation = 0; AnisoPermutation < 2; AnisoPermutation++)
{
for (int32 Permutation = 0; Permutation < 2; Permutation++)
{
if (LightingMode == 0)
{
FDirectLightRGS::FPermutationDomain PermutationVector;
PermutationVector.Set<FDirectLightRGS::FHairShadingDim>(false);
PermutationVector.Set<FDirectLightRGS::FRisSampleLocalLightsDim>(PresampleLightsMode);
PermutationVector.Set<FDirectLightRGS::FAvoidSelfIntersectionDim>(bAvoidSelfIntersection);
PermutationVector.Set<FDirectLightRGS::FSampleDirectionalLightOnly>(false);
PermutationVector.Set<FDirectLightRGS::FAnisotropicMaterialsDim>(AnisoPermutation != 0);
PermutationVector.Set<FDirectLightRGS::FSupportBrdfRayDim>(bUseBrdfSamples);
PermutationVector.Set<FDirectLightRGS::FHairLightingDim>(Permutation != 0);
auto RayGenShader = View.ShaderMap->GetShader<FDirectLightRGS>(PermutationVector);
OutRayGenShaders.Add(RayGenShader.GetRayTracingShader());
}
else if (AlwaysSampleDirLight)
{
FDirectLightRGS::FPermutationDomain PermutationVector;
PermutationVector.Set<FDirectLightRGS::FHairShadingDim>(false);
PermutationVector.Set<FDirectLightRGS::FRisSampleLocalLightsDim>(0); // we won't use RIS in this permutation
PermutationVector.Set<FDirectLightRGS::FAvoidSelfIntersectionDim>(bAvoidSelfIntersection);
PermutationVector.Set<FDirectLightRGS::FSampleDirectionalLightOnly>(true);
PermutationVector.Set<FDirectLightRGS::FAnisotropicMaterialsDim>(AnisoPermutation != 0);
PermutationVector.Set<FDirectLightRGS::FSupportBrdfRayDim>(false);
PermutationVector.Set<FDirectLightRGS::FHairLightingDim>(Permutation != 0);
auto RayGenShader = View.ShaderMap->GetShader<FDirectLightRGS>(PermutationVector);
OutRayGenShaders.Add(RayGenShader.GetRayTracingShader());
}
}
{
FGenerateInitialSamplesRGS::FPermutationDomain PermutationVector;
PermutationVector.Set<FGenerateInitialSamplesRGS::FRisSampleLocalLightsDim>(PresampleLightsMode);
PermutationVector.Set<FGenerateInitialSamplesRGS::FAnisotropicMaterialsDim>(AnisoPermutation != 0);
PermutationVector.Set<FGenerateInitialSamplesRGS::FSupportBrdfRayDim>(bUseBrdfSamples);
AddShaderPermutation<FGenerateInitialSamplesRGS>(PermutationVector, View, OutRayGenShaders);
}
{
FApplySpatialResamplingRGS::FPermutationDomain PermutationVector;
PermutationVector.Set<FApplySpatialResamplingRGS::FPairwiseMISDim>(UsePairwiseMIS);
PermutationVector.Set<FApplySpatialResamplingRGS::FAnisotropicMaterialsDim>(AnisoPermutation != 0);
AddShaderPermutation< FApplySpatialResamplingRGS>(PermutationVector, View, OutRayGenShaders);
}
AddShaderPermutation<FComputeTemporalGradientsRGS>(View, OutRayGenShaders);
for (int32 Permutation = 0; Permutation < 2; Permutation++)
{
FApplyTemporalResamplingRGS::FPermutationDomain PermutationVector;
PermutationVector.Set<FApplyTemporalResamplingRGS::FFuseInitialSamplingDim>(Permutation != 0);
PermutationVector.Set<FApplyTemporalResamplingRGS::FRisSampleLocalLightsDim>(Permutation != 0 ? PresampleLightsMode : 0);
PermutationVector.Set<FApplyTemporalResamplingRGS::FAnisotropicMaterialsDim>(AnisoPermutation != 0);
PermutationVector.Set<FApplyTemporalResamplingRGS::FSupportBrdfRayDim>(bUseBrdfSamples && Permutation != 0);
AddShaderPermutation< FApplyTemporalResamplingRGS>(PermutationVector, View, OutRayGenShaders);
}
for (int32 Permutation = 0; Permutation < 2; Permutation++)
{
FEvaluateSampledLightingRGS::FPermutationDomain PermutationVector;
PermutationVector.Set<FEvaluateSampledLightingRGS::FHairLightingDim>(Permutation != 0);
PermutationVector.Set<FEvaluateSampledLightingRGS::FAvoidSelfIntersectionDim>(bAvoidSelfIntersection);
PermutationVector.Set<FEvaluateSampledLightingRGS::FAnisotropicMaterialsDim>(AnisoPermutation != 0);
AddShaderPermutation< FEvaluateSampledLightingRGS>(PermutationVector, View, OutRayGenShaders);
}
}
}
C++(RTXDLumenReflections.cpp)
class FRTXDILumenReflections : public FLumenHardwareRayTracingShaderBase
{
DECLARE_LUMEN_RAYTRACING_SHADER(FRTXDILumenReflections, Lumen::ERayTracingShaderDispatchSize::DispatchSize1D)
class FEnableFarFieldTracing : SHADER_PERMUTATION_BOOL("DIM_FAR_FIELD_TRACING");
class FHairStrandsOcclusionDim : SHADER_PERMUTATION_BOOL("DIM_HAIRSTRANDS_VOXEL");
class FPresamplingMode : SHADER_PERMUTATION_ENUM_CLASS("RIS_SAMPLE_LOCAL_LIGHTS", ERTXDIPresamplingMode);
class FRecursiveReflectionTraces : SHADER_PERMUTATION_BOOL("RECURSIVE_REFLECTION_TRACES");
class FSupportBrdfRayDim : SHADER_PERMUTATION_BOOL("SAMPLE_BRDF_RAYS");
class FShaderExecutionReorderingDim : SHADER_PERMUTATION_BOOL("LUMEN_HARDWARE_SHADER_EXECUTION_REORDERING");
using FPermutationDomain = TShaderPermutationDomain<FEnableFarFieldTracing, FHairStrandsOcclusionDim, FPresamplingMode, FRecursiveReflectionTraces, FSupportBrdfRayDim, FShaderExecutionReorderingDim>;
:
}
void FDeferredShadingSceneRenderer::PrepareRTXDILumenReflections(const FViewInfo& View, TArray<FRHIRayTracingShader*>& OutRayGenShaders) { const bool bLumenGIEnabled = GetViewPipelineState(View).DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen; if (!ShouldRenderRTXDILumenReflections(View, bLumenGIEnabled)) return;
const int32 MaxBounces = LumenReflections::GetMaxReflectionBounces(View);
const bool bUseBrdfSampling = GetRTXDILumenReflectionsNumBrdfSamples() > 0;
const bool bUseReordering = LumenReflections::UseShaderExecutionReordering(*View.Family);
// 2 permutations bound:
// 2 hair occlusion modes
for (int32 HairOcclusion = 0; HairOcclusion < 2; HairOcclusion++)
{
FRTXDILumenReflections::FPermutationDomain PermutationVector;
PermutationVector.Set<FRTXDILumenReflections::FEnableFarFieldTracing>(IsRTXDILumenReflectionsFarFieldEnabled());
PermutationVector.Set<FRTXDILumenReflections::FHairStrandsOcclusionDim>(HairOcclusion >= 1);
PermutationVector.Set<FRTXDILumenReflections::FPresamplingMode>(GetRTXDILumenReflectionsPresamplingMode(View));
PermutationVector.Set<FRTXDILumenReflections::FRecursiveReflectionTraces>(MaxBounces > 1);
PermutationVector.Set<FRTXDILumenReflections::FSupportBrdfRayDim>(bUseBrdfSampling);
PermutationVector.Set<FRTXDILumenReflections::FShaderExecutionReorderingDim>(bUseReordering);
TShaderRef<FRTXDILumenReflectionsRGS> RayGenerationShader = View.ShaderMap->GetShader<FRTXDILumenReflectionsRGS>(PermutationVector);
OutRayGenShaders.Add(RayGenerationShader.GetRayTracingShader());
}
}
- C++(RTXDLumenSurfaceCache.cpp)
```C++
class FLumenCardSampledDirectLighting : public FLumenHardwareRayTracingShaderBase
{
DECLARE_LUMEN_RAYTRACING_SHADER(FLumenCardSampledDirectLighting, Lumen::ERayTracingShaderDispatchSize::DispatchSize2D)
// Permutations
class FPresamplingMode : SHADER_PERMUTATION_ENUM_CLASS("RIS_SAMPLE_LOCAL_LIGHTS", ERTXDIPresamplingMode);
using FPermutationDomain = TShaderPermutationDomain<FPresamplingMode>;
:
}
void FDeferredShadingSceneRenderer::PrepareRTXDILumenSurfaceCache(const FViewInfo& View, TArray<FRHIRayTracingShader*>& OutRayGenShaders)
{
if (!ShouldRenderRTXDILumenSurfaceCache(View))
return;
for (int32 PresamplingModeIndex = 0; PresamplingModeIndex < (int32)ERTXDIPresamplingMode::MAX; PresamplingModeIndex++)
{
FLumenCardSampledDirectLightingRGS::FPermutationDomain PermutationVector;
PermutationVector.Set<FLumenCardSampledDirectLightingRGS::FPresamplingMode>((ERTXDIPresamplingMode)PresamplingModeIndex);
TShaderRef<FLumenCardSampledDirectLightingRGS> RayGenerationShader = View.ShaderMap->GetShader<FLumenCardSampledDirectLightingRGS>(PermutationVector);
OutRayGenShaders.Add(RayGenerationShader.GetRayTracingShader());
}
}
C++(LumenReflectionHardwareRayTracing.cpp)
void RenderLumenHardwareRayTracingReflections(
FRDGBuilder& GraphBuilder,
const FSceneTextures& SceneTextures,
const FSceneTextureParameters& SceneTextureParameters,
const FScene* Scene,
const FViewInfo& View,
const FLumenCardTracingParameters& TracingParameters,
const FLumenReflectionTracingParameters& ReflectionTracingParameters,
const FLumenReflectionTileParameters& ReflectionTileParameters,
float MaxTraceDistance,
bool bUseRadianceCache,
const LumenRadianceCache::FRadianceCacheInterpolationParameters& RadianceCacheParameters,
bool bSampleSceneColorAtHit,
bool bLumenGIEnabled,
ERDGPassFlags ComputePassFlags
)
{
#if RHI_RAYTRACING
// If supported and enabled, use sampled lighting with hit lighting
const bool bSampledLighting = ShouldRenderRTXDILumenReflections(View, bLumenGIEnabled);
if (bSampledLighting)
{
FCompactedReflectionTraceParameters CompactedTraceParameters = LumenReflections::CompactTraces(
GraphBuilder,
View,
TracingParameters,
ReflectionTracingParameters,
ReflectionTileParameters,
false,
0.0f,
MaxTraceDistance,
ComputePassFlags);
/* ココ */
RenderSampledDirectLightingForLumenReflections(
GraphBuilder,
View,
bLumenGIEnabled,
SceneTextureParameters,
TracingParameters,
ReflectionTracingParameters,
CompactedTraceParameters
);
return;
}
// ここから先はHit lightingがSampled Direct Lightingではない場合の処理
:
}
- C++(RTXDILumenReflections.cpp)
```C++
void RenderSampledDirectLightingForLumenReflections(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
const bool bLumenGIEnabled,
const FSceneTextureParameters& SceneTextureParameters,
const FLumenCardTracingParameters& TracingParameters,
const FLumenReflectionTracingParameters& ReflectionTracingParameters,
const FCompactedReflectionTraceParameters& CompactedTraceParameters)
{
check(View.SampledLightingState.IsValid());
const FSampledLightingResources& Resources = *View.SampledLightingState.Get();
checkf(View.HasRayTracingScene(), TEXT("TLAS does not exist. Verify that the current pass is represented in Lumen::AnyLumenHardwareRayTracingPassEnabled()."));
FRDGBufferRef CompactedTraceTexelAllocator = CompactedTraceParameters.CompactedTraceTexelAllocator->Desc.Buffer;
FRDGBufferRef CompactedTraceTexelData = CompactedTraceParameters.CompactedTraceTexelData->Desc.Buffer;
// Reflection Ray Trace
FIntPoint BufferSize = ReflectionTracingParameters.ReflectionTracingBufferSize;
int32 RayCount = BufferSize.X * BufferSize.Y;
static auto CVarSampledLightingFaceCull = IConsoleManager::Get().FindConsoleVariable(TEXT("r.RayTracing.SampledLighting.FaceCull"));
static auto CVarSampledLightingApproximateMode = IConsoleManager::Get().FindConsoleVariable(TEXT("r.RayTracing.SampledLighting.ApproximateVisibilityMode"));
static auto CVarSampledLightingAvoidSelfIntersectionTraceDistance = IConsoleManager::Get().FindConsoleVariable(TEXT("r.RayTracing.SampledLighting.AvoidSelfIntersectionTraceDistance"));
const int32 MaxBounces = LumenReflections::GetMaxReflectionBounces(View);
const int32 NumBrdfSamples = GetRTXDILumenReflectionsNumBrdfSamples();
// Set the pass parameters
FRTXDILumenReflections::FParameters* PassParameters = GraphBuilder.AllocParameters<FRTXDILumenReflections::FParameters>();
PassParameters->RayTracingIndirectArgs = CompactedTraceParameters.RayTraceDispatchIndirectArgs;
PassParameters->CompactedTraceTexelAllocator = GraphBuilder.CreateSRV(CompactedTraceTexelAllocator, PF_R32_UINT);
PassParameters->CompactedTraceTexelData = GraphBuilder.CreateSRV(CompactedTraceTexelData, PF_R32_UINT);
// Set shared parameters
SetLumenHardwareRayTracingSharedParameters(GraphBuilder, SceneTextureParameters, View, TracingParameters, &PassParameters->SharedParameters);
// Constants
PassParameters->ApplySkyLight = true;
PassParameters->FarFieldReferencePos = (FVector3f)Lumen::GetFarFieldReferencePos();
PassParameters->PullbackBias = Lumen::GetHardwareRayTracingPullbackBias();
PassParameters->ReflectionNormalBias = GetRTXDILumenReflectionsNormalBias();
PassParameters->ReflectionViewBias = GetRTXDILumenReflectionsViewBias();
// RTXDI
{
PassParameters->RTXDIParameters.InitialSampleParameters = GetSampledLightingInitialSampleParameters(GraphBuilder, Resources);
PassParameters->RTXDIParameters.RayVisibilityParameters = GetSampledLightingRayVisibilityParameters();
PassParameters->RTXDIParameters.NumLocalIterations = FMath::Max(0, GetRTXDILumenReflectionsNumLocalIterations());
PassParameters->RTXDIParameters.NumLocalCandidates = FMath::Max(0, GetRTXDILumenReflectionsNumLocalCandidates());
PassParameters->RTXDIParameters.NumDirectionalIterations = FMath::Max(0, GetRTXDILumenReflectionsNumDirectionalIterations());
PassParameters->RTXDIParameters.NumDirectionalCandidates = FMath::Max(0, GetRTXDILumenReflectionsNumDirectionalCandidates());
PassParameters->RTXDIParameters.NumSkyIterations = FMath::Max(0, GetRTXDILumenReflectionsNumSkyIterations());
PassParameters->RTXDIParameters.NumSkyCandidates = FMath::Max(0, GetRTXDILumenReflectionsNumSkyCandidates());
PassParameters->RTXDIParameters.NumBrdfCandidates = FMath::Max(0, NumBrdfSamples);
PassParameters->RTXDIParameters.bEvaluateAllLights = IsRTXDILumenReflectionsEvaluateAllLightsEnabled();
PassParameters->RTXDIParameters.SkylightTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::Create(Resources.SkyLightRIS.EnvTexture));
PassParameters->RTXDIParameters.SkylightTextureSampler = TStaticSamplerState<SF_Bilinear>::GetRHI();
PassParameters->RTXDIParameters.SampledLightData = Resources.SampledLightDataUniformBuffer;
PassParameters->RTXDIParameters.DebugScreenCoords = FUintVector2(GetRTXDILumenDebugScreenCoordX(), GetRTXDILumenDebugScreenCoordY());
PassParameters->LightTLAS = !ShouldBuildLightBvh(View, bLumenGIEnabled) ? nullptr : View.GetRayTracingSceneLayerViewChecked(ERayTracingSceneLayer::SampledLights);
#if SUPPORT_RTXDI_LUMENREFLECTIONS_DECALS
PassParameters->DecalParameters = View.RayTracingDecalUniformBuffer;
#endif
}
// Resources
PassParameters->ReflectionTracingParameters = ReflectionTracingParameters;
extern int32 GLumenReflectionHairStrands_VoxelTrace;
const bool bNeedTraceHairVoxel = HairStrands::HasViewHairStrandsVoxelData(View) && GLumenReflectionHairStrands_VoxelTrace > 0;
if (bNeedTraceHairVoxel)
{
PassParameters->HairStrandsVoxel = HairStrands::BindHairStrandsVoxelUniformParameters(View);
}
const bool bUseReordering = LumenReflections::UseShaderExecutionReordering(*View.Family);
// Get the ray generation shader permutation
FRTXDILumenReflectionsRGS::FPermutationDomain PermutationVector;
PermutationVector.Set<FRTXDILumenReflectionsRGS::FEnableFarFieldTracing>(IsRTXDILumenReflectionsFarFieldEnabled());
PermutationVector.Set<FRTXDILumenReflectionsRGS::FHairStrandsOcclusionDim>(bNeedTraceHairVoxel);
PermutationVector.Set<FRTXDILumenReflectionsRGS::FPresamplingMode>(GetRTXDILumenReflectionsPresamplingMode(View));
PermutationVector.Set<FRTXDILumenReflectionsRGS::FRecursiveReflectionTraces>(MaxBounces > 1);
PermutationVector.Set<FRTXDILumenReflectionsRGS::FSupportBrdfRayDim>(NumBrdfSamples > 0);
PermutationVector.Set<FRTXDILumenReflectionsRGS::FShaderExecutionReorderingDim>(bUseReordering);
TShaderRef<FRTXDILumenReflectionsRGS> RayGenerationShader = View.ShaderMap->GetShader<FRTXDILumenReflectionsRGS>(PermutationVector);
AddLumenRayTraceDispatchIndirectPass(GraphBuilder, RDG_EVENT_NAME("RTXDILumenReflectionsRGS <indirect>"), RayGenerationShader, PassParameters, PassParameters->RayTracingIndirectArgs, 0, View, false);
}
C++(RTXDLumenReflections.cpp)
class FRTXDILumenReflections : public FLumenHardwareRayTracingShaderBase
{
DECLARE_LUMEN_RAYTRACING_SHADER(FRTXDILumenReflections, Lumen::ERayTracingShaderDispatchSize::DispatchSize1D)
class FEnableFarFieldTracing : SHADER_PERMUTATION_BOOL("DIM_FAR_FIELD_TRACING");
class FHairStrandsOcclusionDim : SHADER_PERMUTATION_BOOL("DIM_HAIRSTRANDS_VOXEL");
class FPresamplingMode : SHADER_PERMUTATION_ENUM_CLASS("RIS_SAMPLE_LOCAL_LIGHTS", ERTXDIPresamplingMode);
/*
enum class ERTXDIPresamplingMode
{
// Presampling permutations
DIRECT = 0, // Don't presample, use uniform randoms for selecting local lights
RIS, // Presample using RIS buffer (sampling strictly on global power)
REGIR, // Presample using ReGIR (sampling on world-space grid)
MAX
};
*/
class FRecursiveReflectionTraces : SHADER_PERMUTATION_BOOL("RECURSIVE_REFLECTION_TRACES");
class FSupportBrdfRayDim : SHADER_PERMUTATION_BOOL("SAMPLE_BRDF_RAYS");
class FShaderExecutionReorderingDim : SHADER_PERMUTATION_BOOL("LUMEN_HARDWARE_SHADER_EXECUTION_REORDERING");
using FPermutationDomain = TShaderPermutationDomain<FEnableFarFieldTracing, FHairStrandsOcclusionDim, FPresamplingMode, FRecursiveReflectionTraces, FSupportBrdfRayDim, FShaderExecutionReorderingDim>;
:
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, Lumen::ERayTracingShaderDispatchType ShaderDispatchType, FShaderCompilerEnvironment& OutEnvironment)
{
FLumenHardwareRayTracingShaderBase::ModifyCompilationEnvironment(Parameters, ShaderDispatchType, Lumen::ESurfaceCacheSampling::HighResPages, OutEnvironment);
OutEnvironment.SetDefine(TEXT("RTXDI_INTEGRATION_VERSION"), 4270);
OutEnvironment.SetDefine(TEXT("USE_RTXDI_DEBUG"), LUMEN_REFLECTION_DEBUG_ENABLED);
OutEnvironment.SetDefine(TEXT("USE_RTXDI_DEBUG_PRINT"), LUMEN_REFLECTION_DEBUG_ENABLED);
OutEnvironment.SetDefine(TEXT("USE_RTXDI_SINGLE_PASS_SAMPLING"), 1);
OutEnvironment.SetDefine(TEXT("USE_RTXDI_SINGLE_PASS_SAMPLING_ALLOW_ALLLIGHTS_EVAL"), 1);
if (SupportTransparentShadow())
{
OutEnvironment.SetDefine(TEXT("USE_RTXDI_TRANSPARENT_SHADOW"), 1);
}
FPermutationDomain PermutationVector(Parameters.PermutationId);
if (PermutationVector.Get<FSupportBrdfRayDim>())
{
if (GRHISupportsInlineRayTracingInNvRTX)
OutEnvironment.CompilerFlags.Add(CFLAG_InlineRayTracing);
}
// Ignore sampled lights in regular lumen reflection light evaluation path
OutEnvironment.SetDefine(TEXT("IGNORE_SAMPLED_LIGHTS"), 1);
#if SUPPORT_RTXDI_LUMENREFLECTIONS_DECALS
OutEnvironment.CompilerFlags.Add(CFLAG_ForceDXC);
OutEnvironment.SetDefine(TEXT("SUPPORT_RTXDI_LUMENREFLECTIONS_DECALS"), 1);
#endif
}
:
};
- シェーダー(RTXDILumenReflections.usf)
```C++
[numthreads(INLINE_RAY_TRACING_THREAD_GROUP_SIZE_X, INLINE_RAY_TRACING_THREAD_GROUP_SIZE_Y, 1)]
void RTXDILumenReflectionsCS(
uint3 DispatchThreadIndex : SV_DispatchThreadID,
uint3 DispatchGroupId : SV_GroupID,
uint DispatchGroupIndex : SV_GroupIndex)
{
const uint DispatchThreadId = DispatchThreadIndex.x;
const uint TotalTraceCount = CompactedTraceTexelAllocator[0];
if (DispatchThreadId < TotalTraceCount)
{
const FReflectionTracingCoord ReflectionTracingCoord = DecodeTraceTexel(CompactedTraceTexelData[DispatchThreadId]);
bool bUnused;
float TraceHitDistance = DecodeRayDistance(RWTraceHit[ReflectionTracingCoord.CoordFlatten], bUnused);
float2 ScreenUV = GetScreenUVFromReflectionTracingCoord(ReflectionTracingCoord.CoordFlatten);
float2 ScreenCoord = ScreenUV * View.BufferSizeAndInvSize.xy;
uint LinearCoord = ScreenCoord.y * View.BufferSizeAndInvSize.x + ScreenCoord.x;
float SceneDepth = DownsampledDepth.Load(int3(ReflectionTracingCoord.CoordFlatten, 0)).x;
float3 TranslatedWorldPosition = GetTranslatedWorldPositionFromScreenUV(ScreenUV, SceneDepth);
const float3 WorldNormal = ReadMaterialData(uint2(ScreenCoord)).WorldNormal;
FRayData RayData = GetRayData(ReflectionTracingCoord.CoordFlatten);
float ClippedNearFieldMaxTraceDistance = ClipAndDitherNearFieldMaxTraceDistance(
TranslatedWorldPosition,
RayData.Direction,
ReflectionTracingCoord.CoordFlatten,
NearFieldSceneRadius,
NearFieldMaxTraceDistance,
NearFieldMaxTraceDistanceDitherScale);
float RayBias = 0.05f;
// Construct the reflection ray
FRayDesc Ray;
Ray.Origin = TranslatedWorldPosition;
Ray.Direction = RayData.Direction;
Ray.TMin = max(TraceHitDistance - PullbackBias, RayBias);
Ray.TMax = max(ClippedNearFieldMaxTraceDistance - PullbackBias, RayBias);
// Bias the reflection ray origin along the view (camera) ray and normal direction to avoid self-intersection
float3 ViewDirection = (TranslatedWorldPosition - View.TranslatedWorldCameraOrigin);
Ray.Origin -= normalize(ViewDirection) * ReflectionViewBias;
Ray.Origin += WorldNormal * ReflectionNormalBias;
FRayCone RayCone = (FRayCone)0;
RayCone.SpreadAngle = View.EyeToPixelSpreadAngle;
RayCone = PropagateRayCone(RayCone, RayData.ConeHalfAngle, SceneDepth);
const bool bHiResSurface = true;
const uint CullingMode = RAY_FLAG_CULL_BACK_FACING_TRIANGLES;
FRayTracedLightingContext Context = CreateRayTracedLightingContext(RayCone, ReflectionTracingCoord.Coord, LinearCoord, CullingMode, MaxTraversalIterations, bHiResSurface);
Context.MaxReflectionBounces = MaxReflectionBounces;
Context.FarFieldMaxTraceDistance = FarFieldMaxTraceDistance;
Context.FarFieldReferencePos = FarFieldReferencePos;
Context.bUseBookmark = false;
Context.bAlphaMasking = true;
// Trace the reflection ray and evaluating lighting (near field)
int LightingMode = EVALUATE_MATERIAL_AND_DIRECT_LIGHTING;
/*ココ*/
FRayTracedLightingResult Result = TraceReflectionRay(ReflectionTracingCoord.Coord, TLAS, Ray, Context, LightingMode, RTXDI_OPTIONAL_PASS_DEBUG_PARAM(DebugContext));
#if DIM_HAIRSTRANDS_VOXEL
float HairTraceDistance = min(Ray.TMax, Result.TraceHitDistance);
bool bHairHit;
float HairTransparency;
float HairHitT;
TraceHairVoxels(
ReflectionTracingCoord.Coord,
SceneDepth,
// Use (Translated)WorldPosition instead of SamplePosition, as the bias is too strong otherwise. This is not an issue as
// the voxel structure does not cause any self shadowing issue
TranslatedWorldPosition,
Ray.Direction,
HairTraceDistance,
true,
bHairHit,
HairTransparency,
HairHitT);
if (bHairHit && HairHitT < HairTraceDistance)
{
Result.Radiance *= HairTransparency;
//Result.Transparency *= HairTransparency;
Result.TraceHitDistance = min(HairHitT, Result.TraceHitDistance);
Result.bIsHit = true;
}
#endif // DIM_HAIRSTRANDS_VOXEL
// Skylight
if ((ApplySkyLight != 0) && !Result.bIsHit)
{
FConeTraceResult TraceResult;
TraceResult.Lighting = Result.Radiance;
TraceResult.Transparency = 1;
ApplySkylightToTraceResult(RayData.Direction, TraceResult);
Result.TraceHitDistance = ClippedNearFieldMaxTraceDistance;
Result.Radiance = TraceResult.Lighting;
Result.bIsHit = true;
}
Result.Radiance += GetSkylightLeaking(Ray.Direction, Result.TraceHitDistance);
Result.Radiance *= View.PreExposure;
float MaxLighting = max3(Result.Radiance.x, Result.Radiance.y, Result.Radiance.z);
if (MaxLighting > MaxRayIntensity)
{
Result.Radiance *= MaxRayIntensity / MaxLighting;
}
RWTraceRadiance[ReflectionTracingCoord.Coord] = Result.Radiance;
RWTraceHit[ReflectionTracingCoord.Coord] = EncodeRayDistance(Result.TraceHitDistance, Result.bIsHit);
}
}
Nvidia Shader Execution Reordering
FRayTracedLightingResult TraceReflectionRay(
uint2 TraceCoord,
in RaytracingAccelerationStructure TLAS,
in FRayDesc Ray,
in FRayTracedLightingContext Context,
int LightingMode,
RTXDI_OPTIONAL_DEBUG_PARAM(DebugContext)
)
{
FLumenRayHitBookmark Bookmark;
RandomSequence RandSequence;
RandomSequence_Initialize(RandSequence, Context.LinearCoord, View.StateFrameIndex);
FRayTracedLightingResult Result = CreateRayTracedLightingResult();
Result.bIsHit = false;
Result.TraceHitDistance = Ray.TMax;
float PathRoughness = 0.0f;
float3 PathThroughput = 1.0f;
bool bReachedFarField = false;
#if !RECURSIVE_REFLECTION_TRACES
{
Context.MaxReflectionBounces = 1;
}
#endif
for (uint ReflectionBounceIndex = 0; ReflectionBounceIndex < Context.MaxReflectionBounces; ++ReflectionBounceIndex)
{
float NextReflectionRayAlpha = 0.0f;
FPackedMaterialClosestHitPayload Payload = (FPackedMaterialClosestHitPayload)0;
Payload.SetLumenPayload();
Payload.SetIgnoreTranslucentMaterials();
// Offset ray origin if tracing against far-field
float3 RayOrigin = Ray.Origin;
// Use the set culling mode since near/far field surface cache traces don't run when SER is enabled
NvHitObject HitObject;
NvTraceRayHitObject(TLAS, Context.CullingMode, Context.InstanceMask, RAY_TRACING_SHADER_SLOT_MATERIAL, RAY_TRACING_NUM_SHADER_SLOTS, 0, Ray.GetNativeDesc(), Payload, HitObject);
// Nvidia Shader Execution Reordering https://developer.nvidia.com/blog/improve-shader-performance-and-in-game-frame-rates-with-shader-execution-reordering/
//Ray.Origin = RayOrigin;
NvReorderThread(HitObject);
#if DIM_FAR_FIELD_TRACING
if (!HitObject.IsHit() && ReflectionBounceIndex == 0)
{
// missed nearfield, try to hit far field geometry
// Adjust the ray origin, TMin, and TMax for the far field
Ray.Origin += Context.FarFieldReferencePos;
Ray.TMin = max(Ray.TMax - PullbackBias, 0.f);
Ray.TMax = FarFieldMaxTraceDistance;
Context.InstanceMask = RAY_TRACING_MASK_FAR_FIELD;
NvTraceRayHitObject(TLAS, Context.CullingMode, Context.InstanceMask, RAY_TRACING_SHADER_SLOT_MATERIAL, RAY_TRACING_NUM_SHADER_SLOTS, 0, Ray.GetNativeDesc(), Payload, HitObject);
// Reset ray origin into the original frame
Ray.Origin -= Context.FarFieldReferencePos;
bReachedFarField = true;
// is it worth a reorder here?
NvReorderThread(HitObject);
}
#endif // DIM_FAR_FIELD_TRACING
if (HitObject.IsHit())
{
// Reorder threads and invoke hit shaders
NvInvokeHitObject(TLAS, HitObject, Payload);
bool bTraceNextReflectionRay = false;
// Apply emissive material
float3 Radiance = Payload.GetRadiance() * Payload.GetOpacity();
FLumenHitLightingMaterial LumenMaterial = GetLumenHitLightingMaterial(Payload, Ray);
// Accumulate roughness in order to terminate recursive rays earlier on rough surfaces
PathRoughness = 1.0f - (1.0f - PathRoughness) * (1.0f - LumenMaterial.TopLayerRoughness);
float3 RayHitTranslatedWorldPosition = Ray.Origin + Ray.Direction * Payload.HitT;
// Direct lighting evaluation
{
// Contribution from non-sampled light
Radiance += CalculateDirectLighting(TLAS, Ray, Context, Payload, RandSequence, RayHitTranslatedWorldPosition, Payload.GetWorldNormal());
// Create the surface
FGBufferSurface Surface = (FGBufferSurface)0;
Surface.GBuffer = GetGBufferDataFromPayload(Payload);
Surface.ViewDirection = Ray.Direction;
Surface.TranslatedWorldPosition = Ray.Origin + (Ray.Direction * Payload.HitT);
Surface.LightingChannelMask = Payload.GetPrimitiveLightingChannelMask();
Surface.IsSecondary = true;
#if SUPPORT_RTXDI_LUMENREFLECTIONS_DECALS
#if PLATFORM_SUPPORTS_CALLABLE_SHADERS
{
FDecalData DecalData;
DecalData.BaseColor = float4(0, 0, 0, 1);
DecalData.WorldNormal = float4(0, 0, 0, 1);
DecalData.MetallicSpecularRoughness = float4(0, 0, 0, 1);
DecalData.Emissive = 0;
FDecalLoopCount DecalLoopCount = DecalGridLookup(Surface.TranslatedWorldPosition);
for (uint Index = 0, Num = DecalLoopCount.NumDecals; Index < Num; ++Index)
{
uint DecalId = GetDecalId(Index, DecalLoopCount);
FDecalShaderPayload DecalPayload = (FDecalShaderPayload)0;
DecalPayload.SetInputTranslatedWorldPosition(Surface.TranslatedWorldPosition);
CallShader(DecalId, DecalPayload);
CombineDecal(DecalPayload, DecalData);
}
DecalData.ApplyDecal(Surface.GBuffer);
}
#endif
#endif
// Perform lighting of sampled lights
{
// Setup the RNG contexts, using the reflection trace coordinates as the seed
uint LinearIndex = CalcLinearIndex(TraceCoord);
FRandomContext RandContext = FRandomContext::Create(LinearIndex, View.StateFrameIndex);
uint CoherentLinearIndex = CalcLinearIndex(TraceCoord / 8);
FRandomContext CoherentRandContext = FRandomContext::Create(CoherentLinearIndex, View.StateFrameIndex + 63);
// Evaluate RTXDI hit lighting
/* ココ */
const HitLightingSamplingResult HitLightingResult = EvaluateHitLighting(TraceCoord, Surface, RandContext, CoherentRandContext, RTXDI_OPTIONAL_PASS_DEBUG_PARAM(DebugContext));
Radiance += HitLightingResult.DiffuseExitantRadiance + HitLightingResult.SpecularExitantRadiance;
}
}
{
float3 RayHitGeometryWorldNormal = Payload.GetGeometryNormal();
// The Surface Cache lighting was computed using the front face, don't leak to the backface
if (dot(Ray.Direction, RayHitGeometryWorldNormal) <= 0 || Payload.IsTwoSided())
{
// 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 (LightingMode == EVALUATE_MATERIAL)
{
Radiance += Diffuse_Lambert(LumenMaterial.ApproxFullyRoughDiffuseColor) * (SurfaceCacheSample.DirectLighting + SurfaceCacheSample.IndirectLighting);
}
else
{
// Blend between rough reflections approximation and a single reflection ray
if (ReflectionBounceIndex + 1 < Context.MaxReflectionBounces)
{
NextReflectionRayAlpha = LumenCombineReflectionsAlpha(PathRoughness, /*bHasBackfaceDiffuse*/ false);
}
// Add diffuse part
Radiance += Diffuse_Lambert(LumenMaterial.DiffuseColor) * SurfaceCacheSample.IndirectLighting;
// Add rough specular part
Radiance += Diffuse_Lambert(LumenMaterial.ApproxFullyRoughSpecularColor) * (1.0f - NextReflectionRayAlpha) * SurfaceCacheSample.IndirectLighting;
}
}
}
Result.Radiance += PathThroughput * Radiance;
// Capture hit properties at first hit
if (ReflectionBounceIndex == 0)
{
Result.bIsHit = true;
Result.TraceHitDistance = Payload.HitT;
Result.GeometryWorldNormal = Payload.GetGeometryNormal();
}
if (NextReflectionRayAlpha > 0.0f)
{
// Fresnel
const float NoV = saturate(dot(-Ray.Direction, Payload.GetWorldNormal()));
PathThroughput *= EnvBRDF(LumenMaterial.TopLayerSpecularColor, LumenMaterial.TopLayerRoughness, NoV);
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)
{
Result.Radiance += PathThroughput * EvaluateSkyRadiance(Ray.Direction);
}
break;
}
if (bReachedFarField)
{
//always terminate at far field
break;
}
}
return Result;
}
C++(RTXDLumenSurfaceCache.cpp)
void RenderSampledDirectLightingIntoLumenCards(
FRDGBuilder& GraphBuilder,
const FScene* Scene,
const TArray<FViewInfo>& Views,
const FLumenDirectLightingTaskData* DirectLightingTaskData,
const FLumenCardTileUpdateContext& CardTileUpdateContext,
const FSceneTextureParameters& SceneTextureParameters,
const FSampledLightingResources& Resources,
const FLumenSceneFrameTemporaries& FrameTemporaries,
FRDGBufferSRVRef CardTilesSRV,
FRDGTextureUAVRef DirectLightingAtlasUAV,
ERDGPassFlags ComputePassFlags)
{
const FViewInfo& ReferenceView = Views[0];
FLumenCardTracingParameters TracingParameters;
GetLumenCardTracingParameters(GraphBuilder, ReferenceView, *Scene->GetLumenSceneData(ReferenceView), FrameTemporaries, false, TracingParameters);
// Set the pass parameters
FLumenCardSampledDirectLightingRGS::FParameters* PassParameters = GraphBuilder.AllocParameters<FLumenCardSampledDirectLightingRGS::FParameters>();
PassParameters->IndirectArgs = CardTileUpdateContext.DispatchCardTilesIndirectArgs;
PassParameters->bAtlasContainsLight = (uint32)(DirectLightingTaskData->GatheredLights.Num() > 0); // from non-sampled lights or temporal feedback enabled
PassParameters->MaxTraversalIterations = LumenHardwareRayTracing::GetMaxTraversalIterations();
PassParameters->MaxTraceDistance = Lumen::GetMaxTraceDistance(ReferenceView);
PassParameters->FarFieldMaxTraceDistance = Lumen::GetFarFieldMaxTraceDistance();
PassParameters->HardwareRayTracingShadowRayBias = LumenSceneDirectLighting::GetHardwareRayTracingShadowRayBias();
PassParameters->CardTiles = CardTilesSRV;
PassParameters->RWDirectLightingAtlas = DirectLightingAtlasUAV;
// RTXDI
{
PassParameters->RTXDIParameters.InitialSampleParameters = GetSampledLightingInitialSampleParameters(GraphBuilder, Resources);
PassParameters->RTXDIParameters.RayVisibilityParameters = GetSampledLightingRayVisibilityParameters();
PassParameters->RTXDIParameters.NumLocalIterations = FMath::Max(0, GetRTXDILumenSurfaceCacheNumLocalIterations());
PassParameters->RTXDIParameters.NumLocalCandidates = FMath::Max(0, GetRTXDILumenSurfaceCacheNumLocalCandidates());
PassParameters->RTXDIParameters.NumDirectionalIterations = FMath::Max(1, GetRTXDILumenSurfaceCacheNumDirectionalIterations());
PassParameters->RTXDIParameters.NumDirectionalCandidates = FMath::Max(1, GetRTXDILumenSurfaceCacheNumDirectionalCandidates());
PassParameters->RTXDIParameters.NumSkyIterations = FMath::Max(0, GetRTXDILumenSurfaceCacheNumSkyIterations());
PassParameters->RTXDIParameters.NumSkyCandidates = FMath::Max(0, GetRTXDILumenSurfaceCacheNumSkyCandidates());
PassParameters->RTXDIParameters.bEvaluateAllLights = IsRTXDILumenSurfaceCacheEvaluateAllLightsEnabled();
PassParameters->RTXDIParameters.SkylightTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::Create(Resources.SkyLightRIS.EnvTexture));
PassParameters->RTXDIParameters.SkylightTextureSampler = TStaticSamplerState<SF_Bilinear>::GetRHI();
PassParameters->RTXDIParameters.SampledLightData = Resources.SampledLightDataUniformBuffer;
PassParameters->RTXDIParameters.DebugScreenCoords = FUintVector2(GetRTXDILumenDebugScreenCoordX(), GetRTXDILumenDebugScreenCoordY());
}
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
PassParameters->PreViewTranslation[ViewIndex] = FVector4f((FVector3f)Views[ViewIndex].ViewMatrices.GetPreViewTranslation(), 0.0f);
}
// Set shared parameters
SetLumenHardwareRayTracingSharedParameters(GraphBuilder, SceneTextureParameters, ReferenceView, TracingParameters, &PassParameters->SharedParameters);
// Get the RGS permutation
FLumenCardSampledDirectLightingRGS::FPermutationDomain PermutationVector;
PermutationVector.Set<FLumenCardSampledDirectLightingRGS::FPresamplingMode>(GetRTXDILumenSurfaceCachePresamplingMode(ReferenceView));
TShaderRef<FLumenCardSampledDirectLightingRGS> RayGenerationShader = ReferenceView.ShaderMap->GetShader<FLumenCardSampledDirectLightingRGS>(PermutationVector);
const bool bUseMinimalPayload = true;
FString Resolution = FString::Printf(TEXT("<indirect>"));
AddLumenRayTraceDispatchIndirectPass(
GraphBuilder,
RDG_EVENT_NAME("LumenCardSampledDirectLightingRGS %s", *Resolution),
RayGenerationShader,
PassParameters,
PassParameters->IndirectArgs,
(uint32)ELumenDispatchCardTilesIndirectArgsOffset::RayGeneration,
ReferenceView,
bUseMinimalPayload);
}
C++(RTXDLumenSurfaceCache.cpp)
class FLumenCardSampledDirectLighting : public FLumenHardwareRayTracingShaderBase
{
:
// Permutations
class FPresamplingMode : SHADER_PERMUTATION_ENUM_CLASS("RIS_SAMPLE_LOCAL_LIGHTS", ERTXDIPresamplingMode);
using FPermutationDomain = TShaderPermutationDomain<FPresamplingMode>;
:
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, Lumen::ERayTracingShaderDispatchType ShaderDispatchType, FShaderCompilerEnvironment& OutEnvironment)
{
FLumenHardwareRayTracingShaderBase::ModifyCompilationEnvironment(Parameters, ShaderDispatchType, Lumen::ESurfaceCacheSampling::AlwaysResidentPagesWithoutFeedback, OutEnvironment);
OutEnvironment.SetDefine(TEXT("RTXDI_INTEGRATION_VERSION"), 4270);
OutEnvironment.SetDefine(TEXT("USE_RTXDI_SINGLE_PASS_SAMPLING"), 1);
OutEnvironment.SetDefine(TEXT("USE_RTXDI_SINGLE_PASS_SAMPLING_ALLOW_ALLLIGHTS_EVAL"), 1);
}
:
};
- シェーダー(RTXDLumenSurfaceCache.usf)
```C++
uint bAtlasContainsLight;
// Lighting Estimates Per Surface Card Tile Texel
// (NumLocalIterations * NumLocalCandidates) + (NumDirectionalIterations * NumDirectionalCandidates) + (NumSkyIterations * NumSkyCandidates)
// Rays Traces Per Surface Card Tile Texel
// (NumLocalIterations + NumDirectionalIterations + NumSkyIterations)
RWTexture2D<float3> RWDirectLightingAtlas;
[numthreads(INLINE_RAY_TRACING_THREAD_GROUP_SIZE_X, INLINE_RAY_TRACING_THREAD_GROUP_SIZE_Y, 1)]
void LumenCardSampledDirectLightingCS(
uint3 DispatchThreadIndex : SV_DispatchThreadID,
uint3 DispatchGroupId : SV_GroupID,
uint DispatchGroupIndex : SV_GroupIndex)
{
uint CardTileIndex = DispatchThreadIndex.x / CARD_TILE_SIZE;
uint2 TexelCoordsInCardTile = uint2(DispatchThreadIndex.x % CARD_TILE_SIZE, DispatchThreadIndex.y);
// Load the Lumen surface data
FLumenSurface Surface = GetLumenSurface(CardTileIndex, TexelCoordsInCardTile);
// Early out: surface cache is not valid or it represents an emissive surface
if (!Surface.CardData.bValid || any(Surface.CardData.Emissive))
{
RWDirectLightingAtlas[Surface.AtlasCoords] = 0;
return;
}
const uint2 PixelCoord = uint2(0, 0);
// Setup the RNG contexts, using the surface cache atlas coords as the seed
uint LinearIndex = CalcLinearIndex(Surface.AtlasCoords);
FRandomContext RandContext = FRandomContext::Create(LinearIndex, View.StateFrameIndex);
uint CoherentLinearIndex = CalcLinearIndex(Surface.AtlasCoords / 8);
FRandomContext CoherentRandContext = FRandomContext::Create(CoherentLinearIndex, View.StateFrameIndex + 63);
// Evaluate RTXDI hit lighting
/* ココ */
HitLightingSamplingResult HitLightingResult = EvaluateHitLighting( PixelCoord, Surface, RandContext, CoherentRandContext );
if (bAtlasContainsLight > 0)
{
// Load existing irradiance from the atlas, if it exists from non-sampled lights or for temporal feedback
HitLightingResult.Irradiance += RWDirectLightingAtlas[Surface.AtlasCoords].rgb;
}
// Write the irradiance
RWDirectLightingAtlas[Surface.AtlasCoords] = HitLightingResult.Irradiance;
}
// Load data for a Lumen surface cache card given a card tile index and texel coords in the tile
FLumenSurface GetLumenSurface(uint CardTileIndex, uint2 TexelCoordsInCardTile)
{
FLumenSurface Surface = (FLumenSurface)0;
Surface.TexelCoordsInCardTile = TexelCoordsInCardTile;
// Load card tile and page data
FCardTileData CardTile = UnpackCardTileData(CardTiles[CardTileIndex]);
FLumenCardPageData CardPage = GetLumenCardPageData(CardTile.CardPageIndex);
// Compute the surface's atlas coordinates
uint2 CoordsInCardPage = CARD_TILE_SIZE * CardTile.TileCoord + TexelCoordsInCardTile;
Surface.AtlasCoords = CardPage.PhysicalAtlasCoord + CoordsInCardPage;
// Get the card data
float2 AtlasUV = CardPage.PhysicalAtlasUVRect.xy + CardPage.PhysicalAtlasUVTexelScale * (CoordsInCardPage + 0.5f);
float2 CardUV = CardPage.CardUVRect.xy + CardPage.CardUVTexelScale * (CoordsInCardPage + 0.5f);
FLumenCardData Card = GetLumenCardData(CardPage.CardIndex);
// Load the surface cache data
Surface.CardData = GetSurfaceCacheData(Card, CardUV, AtlasUV);
Surface.LightingChannelMask = Card.LightingChannelMask;
// Compute the LWC position if the cache has valid data
if (Surface.CardData.bValid)
{
Surface.TranslatedWorldPosition = Surface.CardData.WorldPosition + LWCHackToFloat(PreViewTranslation[0]).xyz;
}
return Surface;
}
template<typename SurfaceType>
HitLightingSamplingResult EvaluateHitLighting(uint2 TraceCoord,
SurfaceType Surface,
FRandomContext RandContext,
FRandomContext CoherentRandContext,
RTXDI_OPTIONAL_DEBUG_PARAM(DebugContext)
)
{
return EvaluateHitLightingTemplate<HitLightingSamplingResult, SurfaceType>(TraceCoord, Surface, RandContext, CoherentRandContext, RTXDI_OPTIONAL_PASS_DEBUG_PARAM(DebugContext));
}
template<typename AccumulationType, typename SurfaceType> AccumulationType EvaluateHitLightingTemplate(uint2 TraceCoord, SurfaceType Surface, FRandomContext RandContext, FRandomContext CoherentRandContext, RTXDI_OPTIONAL_DEBUG_PARAM(DebugContext) ) { AccumulationType LightingResult = (AccumulationType)0;
// This is the cursed control-flow structure of a for-case statement
// It allows us to evaluate multiple passes with slightly different
// parameters while ensuring that DXC doesn't make multiple copies of the
// function due to inlining. It saves on code bloat and instruction cache
// pressure.
LOOP
for (int LightReservoirType = 0; LightReservoirType < 3; LightReservoirType++)
{
InitialCandidateOptions Options = (InitialCandidateOptions)0;
int NumIterations = 0;
switch (LightReservoirType)
{
case 0:
// local lights
Options.NumLocalSamples = NumLocalCandidates;
Options.NumDirectionalSamples = 0;
Options.NumSkyLightSamples = 0;
Options.NumBrdfSamples = GetBrdfCandidates();
NumIterations = NumLocalIterations;
break;
case 1:
// directional lights
Options.NumLocalSamples = 0;
Options.NumDirectionalSamples = NumDirectionalCandidates;
Options.NumSkyLightSamples = 0;
Options.NumBrdfSamples = 0;
NumIterations = NumDirectionalIterations;
break;
case 2:
// skylight
Options.NumLocalSamples = 0;
Options.NumDirectionalSamples = 0;
Options.NumSkyLightSamples = NumSkyCandidates;
Options.NumBrdfSamples = GetBrdfCandidates();
NumIterations = NumSkyIterations;
break;
};
Options.InitialSampleVisibility = 0;
/ ココ / EvalLightingPass<AccumulationType, SurfaceType>( TraceCoord, Surface, RandContext, CoherentRandContext, Options, NumIterations, LightingResult, RTXDI_OPTIONAL_PASS_DEBUG_PARAM(DebugContext) ); }
return LightingResult;
}
template<typename AccumulationType, typename SurfaceType> void EvalLightingPass( uint2 PixelCoord, SurfaceType Surface, inout FRandomContext RandContext, inout FRandomContext CoherentRandContext, InitialCandidateOptions Options, uint Iterations, inout AccumulationType Result, RTXDI_OPTIONAL_DEBUG_PARAM(DebugContext) ) { // Local light shading
for (uint SampleIndex = 0; SampleIndex < Iterations; ++SampleIndex)
{
/ ココ / RTXDI_SDK_Reservoir Reservoir = ProduceInitialSample( PixelCoord, Surface, Options, RandContext, CoherentRandContext);
// Skip this iteration, the reservoir doesn't contain a valid sample
if (!Reservoir.sampleRef.IsValid())
{
continue;
}
const RTXDI_SDK_LightSampleRef SampleRef = Reservoir.sampleRef;
const float UnbiasedContributionWeight = Reservoir.weightSum;
uint DirectionalLightIndex = uint(RandContext.GenerateSample1D() * SampledLightData.DirectionalLightCount);
DirectionalLightIndex = min(DirectionalLightIndex, SampledLightData.DirectionalLightCount - 1) + SampledLightData.DirectionalLightStart;
// Use blue noise to sample directional light
const uint SmallPrime = 17;
const float2 SampleLocation = BlueNoiseVec2(PixelCoord, View.StateFrameIndex + SampleIndex * SmallPrime);
const RTXDI_SDK_LightSampleRef SampleRef = RTXDI_SDK_LightSampleRef::Create(DirectionalLightIndex, SampleLocation);
const float UnbiasedContributionWeight = (float)SampledLightData.DirectionalLightCount;
AccumulationType LocalResult = EvalLightingInternal<AccumulationType,SurfaceType>(
SampleRef,
UnbiasedContributionWeight,
PixelCoord,
Surface,
false,
RandContext,
RTXDI_OPTIONAL_PASS_DEBUG_PARAM(DebugContext)
);
Result.Accumulate(LocalResult, 1.f / (float)max(1, Iterations));
}
}
template<typename AccumulationType, typename SurfaceType>
AccumulationType EvalLightingInternal(
in const RTXDI_SDK_LightSampleRef SampleRef,
in const float UnbiasedContributionWeight,
in const uint2 PixelCoord,
in const SurfaceType Surface,
in const bool bInexactShadowedGeometry,
inout FRandomContext RandContext,
RTXDI_OPTIONAL_DEBUG_PARAM(DebugContext)
)
{
FSampledLightData Light;
FLightSampleLocation LightSample;
GetLightSampleData(Surface, SampleRef, LightSample, Light);
// Load the sampled light data and generate a sample location for the given GBuffer/Lumen Surface and light combination
/* template
Light = GetSampledDeferredLightDataWithSkylight(LightIndex, LightUV, SkylightTexture, SkylightTextureSampler);
LightSample = ComputeLightSampleLocation(Light, Surface.TranslatedWorldPosition, Surface.GetWorldNormal(), Light.LightType, LightUV);
*/
return EvalLightingInternal<AccumulationType,SurfaceType>(
Light, LightSample, SampleRef,
UnbiasedContributionWeight, PixelCoord,
Surface, bInexactShadowedGeometry, RandContext,
RTXDI_OPTIONAL_PASS_DEBUG_PARAM(DebugContext)
);
}
C++(SampledLightRendering.cpp)
void FDeferredShadingSceneRenderer::RenderSampledDirectLighting(
FRDGBuilder& GraphBuilder,
TRDGUniformBufferRef<FSceneTextureUniformParameters> SceneTexturesUniformBuffer,
FRDGTextureRef SceneColorTexture,
FRDGTextureRef LightingChannelsTexture)
{
RDG_EVENT_SCOPE(GraphBuilder, "SampledDirectLighting");
:
const int32 LightingMode = CVarSampledLightingMode.GetValueOnRenderThread();
/*
static TAutoConsoleVariable<int32> CVarSampledLightingMode(
TEXT("r.RayTracing.SampledLighting.Mode"), 1,
TEXT("Which mode to process sampled lighting with\n")
TEXT(" 0 - monolithic single pass \n")
TEXT(" 1 - multipass ReSTIRs style (Default)"),
ECVF_RenderThreadSafe);
*/
:
// Whether using pairwise MIS
const bool UsePairwiseMIS = CVarSampledLightingSpatialUsePairwiseMIS.GetValueOnRenderThread() != 0;
/*
static TAutoConsoleVariable<int32> CVarSampledLightingSpatialUsePairwiseMIS(
TEXT("r.RayTracing.SampledLighting.Spatial.PairwiseMIS"), 0,
TEXT("Whether to use pairwise MIS sampling during spatial reuse (default 0)"),
ECVF_RenderThreadSafe);
*/
:
// evaluate lighting
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
:
if (LightingMode == 0)
{
// single pass mode sampling independently per pixel
FDirectLightRGS::FParameters* PassParameters = GraphBuilder.AllocParameters<FDirectLightRGS::FParameters>();
PassParameters->TLAS = RayTracingScene;
PassParameters->LightTLAS = LightRayTracingScene;
PassParameters->ViewUniformBuffer = View.ViewUniformBuffer;
PassParameters->SceneTextures = GetSceneTextureParameters(GraphBuilder,View);
PassParameters->SceneLightingChannels = GetSceneLightingChannelParameters(GraphBuilder, LightingChannelsTexture);
PassParameters->RWDiffuseUAV = GraphBuilder.CreateUAV(Diffuse);
PassParameters->RWSpecularUAV = GraphBuilder.CreateUAV(Specular);
PassParameters->RWRayDistanceUAV = GraphBuilder.CreateUAV(RayHitDistance);
PassParameters->SinglePassModeParameters = SinglePassModeParameters;
PassParameters->DemodulateMaterials = CVarSampledLightingDemodulateMaterials.GetValueOnRenderThread();
PassParameters->BlueNoise = BlueNoiseUniformBuffer;
PassParameters->RTXDICloudShadow = RTXDICloudShadow;
if (bUseHairLighting)
{
PassParameters->VirtualVoxel = HairStrands::BindHairStrandsVoxelUniformParameters(View);
PassParameters->bUseHairVoxel = bUseHairVoxel;
}
FDirectLightRGS::FPermutationDomain PermutationVector;
PermutationVector.Set<FDirectLightRGS::FHairShadingDim>(false);
PermutationVector.Set<FDirectLightRGS::FRisSampleLocalLightsDim>(Resources.PresampleLightsMode);
PermutationVector.Set<FDirectLightRGS::FAvoidSelfIntersectionDim>(bAvoidSelfIntersection);
PermutationVector.Set<FDirectLightRGS::FSampleDirectionalLightOnly>(false);
PermutationVector.Set<FDirectLightRGS::FAnisotropicMaterialsDim>(bSupportAnisotropyPermutation);
PermutationVector.Set<FDirectLightRGS::FSupportBrdfRayDim>(bUseBrdfSamples);
PermutationVector.Set<FDirectLightRGS::FHairLightingDim>(bUseHairLighting);
auto RayGenShaLightder = View.ShaderMap->GetShader<FDirectLightRGS>(PermutationVector);
ClearUnusedGraphResources(RayGenShader, PassParameters);
/* ココ */
GraphBuilder.AddPass(
RDG_EVENT_NAME("SinglePassSampledLighting"),
PassParameters,
ERDGPassFlags::Compute,
[PassParameters, this, &View, RayGenShader, LightingResolution](FRHIRayTracingCommandList& RHICmdList)
{
FRayTracingShaderBindingsWriter GlobalResources;
SetShaderParameters(GlobalResources, RayGenShader, *PassParameters);
FRHIRayTracingScene* RayTracingSceneRHI = View.GetRayTracingSceneChecked();
RHICmdList.RayTraceDispatch(View.RayTracingMaterialPipeline, RayGenShader.GetRayTracingShader(), RayTracingSceneRHI, GlobalResources, LightingResolution.X, LightingResolution.Y);
});
}
else
{
for (int32 Reservoir = 0; Reservoir < NumReservoirs; Reservoir++)
{
const bool bUseFusedSampling = CVarSampledLightingTemporal.GetValueOnRenderThread() != 0 && !bCameraCut && Reservoir < PrevHistoryCount && bEnableFusedSampling;
// Initial sampling pass to select a light candidate
if (!bUseFusedSampling)
{
FGenerateInitialSamplesRGS::FParameters* PassParameters = GraphBuilder.AllocParameters<FGenerateInitialSamplesRGS::FParameters>();
PassParameters->ViewUniformBuffer = View.ViewUniformBuffer;
PassParameters->SampledLightData = Resources.SampledLightDataUniformBuffer;
PassParameters->SceneTextures = GetSceneTextureParameters(GraphBuilder,View);
PassParameters->SceneLightingChannels = GetSceneLightingChannelParameters(GraphBuilder, LightingChannelsTexture);
PassParameters->OutputSlice = Reservoir;
PassParameters->HistoryReservoir = Reservoir;
PassParameters->InitialSampleSetup = InitialSampleSetup;
PassParameters->SampledLightingCommonParameters = CommonParameters;
PassParameters->InitialSampleParameters = InitialSampleParameters;
FGenerateInitialSamplesRGS::FPermutationDomain PermutationVector;
PermutationVector.Set<FGenerateInitialSamplesRGS::FRisSampleLocalLightsDim>(Resources.PresampleLightsMode);
PermutationVector.Set<FGenerateInitialSamplesRGS::FAnisotropicMaterialsDim>(bSupportAnisotropyPermutation);
PermutationVector.Set<FGenerateInitialSamplesRGS::FSupportBrdfRayDim>(bUseBrdfSamples);
auto RayGenShader = GetShaderPermutation<FGenerateInitialSamplesRGS>(PermutationVector, View);
ClearUnusedGraphResources(RayGenShader, PassParameters);
/* ココ */
GraphBuilder.AddPass(
RDG_EVENT_NAME("CreateInitialSamples"),
PassParameters,
ERDGPassFlags::Compute,
[PassParameters, this, &View, RayGenShader, LightingResolution](FRHIRayTracingCommandList& RHICmdList)
{
FRayTracingShaderBindingsWriter GlobalResources;
SetShaderParameters(GlobalResources, RayGenShader, *PassParameters);
FRHIRayTracingScene* RayTracingSceneRHI = View.GetRayTracingSceneChecked();
RHICmdList.RayTraceDispatch(View.RayTracingMaterialPipeline, RayGenShader.GetRayTracingShader(), RayTracingSceneRHI, GlobalResources, LightingResolution.X, LightingResolution.Y);
});
}
// Temporal candidate merge pass, optionally merged with initial candidate pass
if (bTemporalPass && Reservoir < PrevHistoryCount)
{
{
FApplyTemporalResamplingRGS::FParameters* PassParameters = GraphBuilder.AllocParameters<FApplyTemporalResamplingRGS::FParameters>();
PassParameters->ViewUniformBuffer = View.ViewUniformBuffer;
PassParameters->SampledLightData = Resources.SampledLightDataUniformBuffer;
PassParameters->SceneTextures = GetSceneTextureParameters(GraphBuilder,View);
PassParameters->SceneLightingChannels = GetSceneLightingChannelParameters(GraphBuilder, LightingChannelsTexture);
PassParameters->ReservoirHistoryBufferDim = ReservoirHistoryBufferDim;
PassParameters->InputSlice = Reservoir;
PassParameters->OutputSlice = Reservoir;
PassParameters->HistoryReservoir = Reservoir;
PassParameters->TemporalDepthRejectionThreshold = FMath::Clamp(CVarSampledLightingTemporalDepthRejectionThreshold.GetValueOnRenderThread(), 0.0f, 1.0f);
PassParameters->TemporalNormalRejectionThreshold = FMath::Clamp(CVarSampledLightingTemporalNormalRejectionThreshold.GetValueOnRenderThread(), -1.0f, 1.0f);
PassParameters->ApplyApproximateVisibilityTest = CVarSampledLightingTemporalApplyApproxVisibility.GetValueOnAnyThread();
PassParameters->InitialSampleSetup = InitialSampleSetup;
PassParameters->SpatiallyHashTemporalReprojection = FMath::Clamp(CVarSampledLightingTemporalApplySpatialHash.GetValueOnRenderThread(), 0, 1);
PassParameters->LightReservoirHistory = GraphBuilder.CreateSRV(GraphBuilder.RegisterExternalBuffer(ReferenceView.PrevViewInfo.SampledLightHistory.LightReservoirs));
PassParameters->NormalHistory = RegisterExternalTextureWithFallback(GraphBuilder, ReferenceView.PrevViewInfo.GBufferA, GSystemTextures.BlackDummy);
PassParameters->DepthHistory = RegisterExternalTextureWithFallback(GraphBuilder, ReferenceView.PrevViewInfo.DepthBuffer, GSystemTextures.BlackDummy);
PassParameters->PermutationSamplingMask = EnablePermutationSampling ? PermutationSamplingMask : GSystemTextures.GetBlackDummy(GraphBuilder);
PassParameters->RWPreviousSampleUAV = GraphBuilder.CreateUAV(PreviousSample);
PassParameters->SampledLightingCommonParameters = CommonParameters;
PassParameters->InitialSampleParameters = InitialSampleParameters;
FApplyTemporalResamplingRGS::FPermutationDomain PermutationVector;
PermutationVector.Set<FApplyTemporalResamplingRGS::FFuseInitialSamplingDim>(bEnableFusedSampling);
PermutationVector.Set<FApplyTemporalResamplingRGS::FRisSampleLocalLightsDim>(bEnableFusedSampling ? Resources.PresampleLightsMode : 0);
PermutationVector.Set<FApplyTemporalResamplingRGS::FAnisotropicMaterialsDim>(bSupportAnisotropyPermutation);
PermutationVector.Set<FApplyTemporalResamplingRGS::FSupportBrdfRayDim>(bUseBrdfSamples && bEnableFusedSampling);
auto RayGenShader = GetShaderPermutation<FApplyTemporalResamplingRGS>(PermutationVector, View);
ClearUnusedGraphResources(RayGenShader, PassParameters);
/* ココ */
GraphBuilder.AddPass(
RDG_EVENT_NAME("%sTemporalResample", bEnableFusedSampling ? TEXT("FusedInitialCandidateAnd") : TEXT("")),
PassParameters,
ERDGPassFlags::Compute,
[PassParameters, this, &View, RayGenShader, LightingResolution](FRHIRayTracingCommandList& RHICmdList)
{
FRayTracingShaderBindingsWriter GlobalResources;
SetShaderParameters(GlobalResources, RayGenShader, *PassParameters);
FRHIRayTracingScene* RayTracingSceneRHI = View.GetRayTracingSceneChecked();
RHICmdList.RayTraceDispatch(View.RayTracingMaterialPipeline, RayGenShader.GetRayTracingShader(), RayTracingSceneRHI, GlobalResources, LightingResolution.X, LightingResolution.Y);
});
}
// Boiling filter pass to prevent runaway samples
if (CVarSampledLightingApplyBoilingFilter.GetValueOnRenderThread() != 0)
{
FApplyBoilingFilterCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FApplyBoilingFilterCS::FParameters>();
PassParameters->ViewUniformBuffer = View.ViewUniformBuffer;
PassParameters->RWLightReservoirUAV = GraphBuilder.CreateUAV(LightReservoirs);
PassParameters->ReservoirBufferDim = ReservoirBufferDim;
PassParameters->InputSlice = Reservoir;
PassParameters->OutputSlice = Reservoir;
PassParameters->BoilingFilterStrength = FMath::Clamp(CVarSampledLightingBoilingFilterStrength.GetValueOnRenderThread(), 0.00001f, 1.0f);
auto ComputeShader = View.ShaderMap->GetShader<FApplyBoilingFilterCS>();
ClearUnusedGraphResources(ComputeShader, PassParameters);
FIntPoint GridSize = FMath::DivideAndRoundUp<FIntPoint>(View.ViewRect.Size(), FApplyBoilingFilterCS::GetThreadGroupSize());
/* ココ */
FComputeShaderUtils::AddPass(GraphBuilder, RDG_EVENT_NAME("BoilingFilter"), ComputeShader, PassParameters, FIntVector(GridSize.X, GridSize.Y, 1));
}
}
}
// Spatial resampling passes, one per reservoir
for (int32 Reservoir = NumReservoirs; Reservoir > 0; Reservoir--)
{
if (CVarSampledLightingSpatial.GetValueOnRenderThread() != 0)
{
FApplySpatialResamplingRGS::FParameters* PassParameters = GraphBuilder.AllocParameters<FApplySpatialResamplingRGS::FParameters>();
PassParameters->ViewUniformBuffer = View.ViewUniformBuffer;
PassParameters->SampledLightData = Resources.SampledLightDataUniformBuffer;
PassParameters->SceneTextures = GetSceneTextureParameters(GraphBuilder, View);
PassParameters->SceneLightingChannels = GetSceneLightingChannelParameters(GraphBuilder, LightingChannelsTexture);
PassParameters->InputSlice = Reservoir - 1;
PassParameters->OutputSlice = Reservoir;
PassParameters->HistoryReservoir = Reservoir - 1;
PassParameters->SpatialSamples = FMath::Max(CVarSampledLightingSpatialSamples.GetValueOnRenderThread(), 1);
PassParameters->SpatialSamplesBoost = FMath::Max(CVarSampledLightingSpatialSamplesBoost.GetValueOnRenderThread(), 1);
PassParameters->SpatialSamplingRadius = FMath::Max(1.0f, CVarSampledLightingSpatialSamplingRadius.GetValueOnRenderThread());
PassParameters->SpatialDepthRejectionThreshold = FMath::Clamp(CVarSampledLightingSpatialDepthRejectionThreshold.GetValueOnRenderThread(), 0.0f, 1.0f);
PassParameters->SpatialNormalRejectionThreshold = FMath::Clamp(CVarSampledLightingSpatialNormalRejectionThreshold.GetValueOnRenderThread(), -1.0f, 1.0f);
PassParameters->ApplyApproximateVisibilityTest = CVarSampledLightingSpatialApplyApproxVisibility.GetValueOnRenderThread();
PassParameters->DiscountNaiveSamples = CVarSampledLightingSpatialDiscountNaiveSamples.GetValueOnRenderThread();
PassParameters->NeighborOffsetMask = GDiscSampleBuffer.NumSamples - 1;
PassParameters->NeighborOffsets = GDiscSampleBuffer.DiscSampleBufferSRV;
PassParameters->SampledLightingCommonParameters = CommonParameters;
FApplySpatialResamplingRGS::FPermutationDomain PermutationVector;
PermutationVector.Set<FApplySpatialResamplingRGS::FPairwiseMISDim>(UsePairwiseMIS);
PermutationVector.Set<FApplySpatialResamplingRGS::FAnisotropicMaterialsDim>(bSupportAnisotropyPermutation);
auto RayGenShader = GetShaderPermutation<FApplySpatialResamplingRGS>(PermutationVector, View);
ClearUnusedGraphResources(RayGenShader, PassParameters);
/* ココ */
GraphBuilder.AddPass(
RDG_EVENT_NAME("SpatialResample"),
PassParameters,
ERDGPassFlags::Compute,
[PassParameters, this, &View, RayGenShader, LightingResolution](FRHIRayTracingCommandList& RHICmdList)
{
FRayTracingShaderBindingsWriter GlobalResources;
SetShaderParameters(GlobalResources, RayGenShader, *PassParameters);
FRHIRayTracingScene* RayTracingSceneRHI = View.GetRayTracingSceneChecked();
RHICmdList.RayTraceDispatch(View.RayTracingMaterialPipeline, RayGenShader.GetRayTracingShader(), RayTracingSceneRHI, GlobalResources, LightingResolution.X, LightingResolution.Y);
});
InitialSlice = Reservoir;
}
}
// Shading evaluation pass
{
FEvaluateSampledLightingRGS::FParameters* PassParameters = GraphBuilder.AllocParameters<FEvaluateSampledLightingRGS::FParameters>();
PassParameters->ViewUniformBuffer = View.ViewUniformBuffer;
PassParameters->SampledLightData = Resources.SampledLightDataUniformBuffer;
PassParameters->SceneTextures = GetSceneTextureParameters(GraphBuilder, View);
PassParameters->SceneLightingChannels = GetSceneLightingChannelParameters(GraphBuilder, LightingChannelsTexture);
PassParameters->RWDiffuseUAV = GraphBuilder.CreateUAV(Diffuse);
PassParameters->RWSpecularUAV = GraphBuilder.CreateUAV(Specular);
PassParameters->RWRayDistanceUAV = GraphBuilder.CreateUAV(RayHitDistance);
PassParameters->RWLuminanceUAV = GraphBuilder.CreateUAV(LuminanceHistory);
PassParameters->ReservoirHistoryBufferDim = ReservoirHistoryBufferDim;
PassParameters->RWLightReservoirHistoryUAV = GraphBuilder.CreateUAV(LightReservoirsHistory);
PassParameters->InputSlice = InitialSlice;
PassParameters->NumReservoirs = NumReservoirs;
PassParameters->DemodulateMaterials = CVarSampledLightingDemodulateMaterials.GetValueOnRenderThread();
PassParameters->FeedbackVisibility = CVarRayTracingSampledLightingFeedbackVisibility.GetValueOnRenderThread();
PassParameters->SecondarySpatialPassSamples = FMath::Clamp(CVarSampledLightingSpatialSecondaryPassSamples.GetValueOnRenderThread(), 0, 16);;
PassParameters->NeighborOffsetMask = GDiscSampleBuffer.NumSamples - 1;
PassParameters->NeighborOffsets = GDiscSampleBuffer.DiscSampleBufferSRV;
PassParameters->AlwaysSampleDirLight = AlwaysSampleDirLight;
if (bUseHairLighting)
{
PassParameters->bUseHairVoxel = (bUseHairDeepShadow && bUseHairVoxel) ? 1 : 0;
//EHartNV ToDo: change to strand data
// old PassParameters->HairCategorizationTexture = HairResources.CategorizationTexture;
// new? PassParameters->HairStrands = HairStrands::BindHairStrandsViewUniformParameters(View);
PassParameters->HairLightChannelMaskTexture = View.HairStrandsViewData.VisibilityData.LightChannelMaskTexture;
PassParameters->VirtualVoxel = HairStrands::BindHairStrandsVoxelUniformParameters(View);
}
PassParameters->SampledLightingCommonParameters = CommonParameters;
FEvaluateSampledLightingRGS::FPermutationDomain PermutationVector;
PermutationVector.Set<FEvaluateSampledLightingRGS::FHairLightingDim>(bUseHairLighting);
PermutationVector.Set<FEvaluateSampledLightingRGS::FAvoidSelfIntersectionDim>(bAvoidSelfIntersection);
PermutationVector.Set<FEvaluateSampledLightingRGS::FAnisotropicMaterialsDim>(bSupportAnisotropyPermutation);
auto RayGenShader = GetShaderPermutation<FEvaluateSampledLightingRGS>(PermutationVector, View);
ClearUnusedGraphResources(RayGenShader, PassParameters);
/* ココ */
GraphBuilder.AddPass(
RDG_EVENT_NAME("ShadeSamples"),
PassParameters,
ERDGPassFlags::Compute,
[PassParameters, this, &View, RayGenShader, LightingResolution](FRHIRayTracingCommandList& RHICmdList)
{
FRayTracingShaderBindingsWriter GlobalResources;
SetShaderParameters(GlobalResources, RayGenShader, *PassParameters);
FRHIRayTracingScene* RayTracingSceneRHI = View.GetRayTracingSceneChecked();
RHICmdList.RayTraceDispatch(View.RayTracingMaterialPipeline, RayGenShader.GetRayTracingShader(), RayTracingSceneRHI, GlobalResources, LightingResolution.X, LightingResolution.Y);
});
}
// When always sample dir light is enabled, we choose to explicitly sample dir light rather than using the ReStir methods done above.
if (AlwaysSampleDirLight)
{
// single pass mode sampling independently per pixel
FDirectLightRGS::FParameters* PassParameters = GraphBuilder.AllocParameters<FDirectLightRGS::FParameters>();
PassParameters->TLAS = RayTracingScene;
PassParameters->LightTLAS = LightRayTracingScene;
PassParameters->ViewUniformBuffer = View.ViewUniformBuffer;
PassParameters->SceneTextures = GetSceneTextureParameters(GraphBuilder, View);
PassParameters->SceneLightingChannels = GetSceneLightingChannelParameters(GraphBuilder, LightingChannelsTexture);
PassParameters->RWDiffuseUAV = GraphBuilder.CreateUAV(Diffuse);
PassParameters->RWSpecularUAV = GraphBuilder.CreateUAV(Specular);
PassParameters->RWRayDistanceUAV = GraphBuilder.CreateUAV(RayHitDistance);
PassParameters->SinglePassModeParameters = SinglePassModeParameters;
// override the single pass mode setup
PassParameters->SinglePassModeParameters.NumLocalIterations = 0;
PassParameters->SinglePassModeParameters.NumLocalCandidates = 0;
PassParameters->SinglePassModeParameters.NumDirectionalIterations = FMath::Max(0, CVarRayTracingSampledLightingDirectNumDirectionalSamples.GetValueOnRenderThread());
PassParameters->SinglePassModeParameters.NumDirectionalCandidates = 1;
PassParameters->SinglePassModeParameters.NumSkyIterations = 0;
PassParameters->SinglePassModeParameters.NumBrdfCandidates = 0;
PassParameters->SinglePassModeParameters.bEvaluateAllLights = false;
PassParameters->DemodulateMaterials = CVarSampledLightingDemodulateMaterials.GetValueOnRenderThread();
PassParameters->BlueNoise = BlueNoiseUniformBuffer;
PassParameters->RTXDICloudShadow = RTXDICloudShadow;
if (bUseHairLighting)
{
PassParameters->VirtualVoxel = HairStrands::BindHairStrandsVoxelUniformParameters(View);
PassParameters->bUseHairVoxel = bUseHairVoxel;
}
FDirectLightRGS::FPermutationDomain PermutationVector;
PermutationVector.Set<FDirectLightRGS::FHairShadingDim>(false);
PermutationVector.Set<FDirectLightRGS::FRisSampleLocalLightsDim>(0);
PermutationVector.Set<FDirectLightRGS::FAvoidSelfIntersectionDim>(bAvoidSelfIntersection);
PermutationVector.Set<FDirectLightRGS::FSampleDirectionalLightOnly>(true);
PermutationVector.Set<FDirectLightRGS::FAnisotropicMaterialsDim>(bSupportAnisotropyPermutation);
PermutationVector.Set<FDirectLightRGS::FSupportBrdfRayDim>(false);
PermutationVector.Set<FDirectLightRGS::FHairLightingDim>(bUseHairLighting);
auto RayGenShader = View.ShaderMap->GetShader<FDirectLightRGS>(PermutationVector);
ClearUnusedGraphResources(RayGenShader, PassParameters);
/* ココ */
GraphBuilder.AddPass(
RDG_EVENT_NAME("SinglePassSampledLighting"),
PassParameters,
ERDGPassFlags::Compute,
[PassParameters, this, &View, RayGenShader, LightingResolution](FRHIRayTracingCommandList& RHICmdList)
{
FRayTracingShaderBindingsWriter GlobalResources;
SetShaderParameters(GlobalResources, RayGenShader, *PassParameters);
FRHIRayTracingScene* RayTracingSceneRHI = View.GetRayTracingSceneChecked();
RHICmdList.RayTraceDispatch(View.RayTracingMaterialPipeline, RayGenShader.GetRayTracingShader(), RayTracingSceneRHI, GlobalResources, LightingResolution.X, LightingResolution.Y);
});
}
:
// evaluate denoiser
if (!bAccumulationModePerView)
{
const int32 DenoiserMode = CVarSampledLightingDenoiser.GetValueOnRenderThread();
if (View.ViewState)
View.ViewState->LightSamplingState->AccummulatedFrameNum = 0;
const IScreenSpaceDenoiser* DefaultDenoiser = IScreenSpaceDenoiser::GetDefaultDenoiser();
const IScreenSpaceDenoiser* DenoiserToUse = DenoiserMode == 1 ? DefaultDenoiser : GScreenSpaceDenoiser;
static IModuleInterface* NRDModule = FModuleManager::GetModulePtr<IModuleInterface>(TEXT("NRD"));
const bool bHasNRDPluginEnabled = NRDModule != nullptr;
if (DenoiserMode == 2 && bHasNRDPluginEnabled && DenoiserToUse != DefaultDenoiser)
{
RDG_EVENT_SCOPE(GraphBuilder, "%s%s(Diffuse + Specular) %dx%d",
DenoiserToUse != DefaultDenoiser ? TEXT("ThirdParty ") : TEXT(""),
DenoiserToUse->GetDebugName(),
View.ViewRect.Width(), View.ViewRect.Height());
const bool bApplyConfidence = CVarRayTracingSampledLightingDenoiserApplyConfidence.GetValueOnRenderThread() != 0;
IScreenSpaceDenoiser::FPolychromaticPenumbraHarmonics DenoiserInputs;
DenoiserInputs.Diffuse.Harmonics[0] = Diffuse;
DenoiserInputs.Diffuse.Harmonics[1] = RayHitDistance;
DenoiserInputs.Specular.Harmonics[0] = Specular;
DenoiserInputs.Specular.Harmonics[1] = RayHitDistance;
DenoiserInputs.Diffuse.Harmonics[2] = bApplyConfidence ? DiffuseConfidence : nullptr;
DenoiserInputs.Specular.Harmonics[2] = bApplyConfidence ? SpecularConfidence : nullptr;
IScreenSpaceDenoiser::FPolychromaticPenumbraOutputs DenoiserOutputs = DenoiserToUse->DenoisePolychromaticPenumbraHarmonics(
GraphBuilder,
View,
(FPreviousViewInfo*)&View.PrevViewInfo,
SceneTextures,
DenoiserInputs
);
Diffuse = DenoiserOutputs.Diffuse;
Specular = DenoiserOutputs.Specular;
}
}
else
{
:
}
:
}
:
}
C++(SampledLightRendering.cpp
class FDirectLightRGS : public FGlobalShader
{
:
class FHairShadingDim : SHADER_PERMUTATION_BOOL("HAIR_SHADING");
class FRisSampleLocalLightsDim : SHADER_PERMUTATION_INT("RIS_SAMPLE_LOCAL_LIGHTS", 3);
class FAvoidSelfIntersectionDim : SHADER_PERMUTATION_BOOL("AVOID_SELF_INTERSECTION_TRACE");
class FSampleDirectionalLightOnly : SHADER_PERMUTATION_BOOL("SAMPLE_DIRECTIONALLIGHT_ONLY");
class FAnisotropicMaterialsDim : SHADER_PERMUTATION_BOOL("SUPPORTS_ANISOTROPIC_MATERIALS");
class FSupportBrdfRayDim : SHADER_PERMUTATION_BOOL("SAMPLE_BRDF_RAYS");
class FHairLightingDim : SHADER_PERMUTATION_BOOL("USE_HAIR_LIGHTING");
:
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
ApplySampledLightingGlobalSettings(OutEnvironment);
FPermutationDomain PermutationVector(Parameters.PermutationId);
if (PermutationVector.Get<FSupportBrdfRayDim>())
{
if(GRHISupportsInlineRayTracingInNvRTX)
OutEnvironment.CompilerFlags.Add(CFLAG_InlineRayTracing);
}
OutEnvironment.SetDefine(TEXT("USE_RTXDI_SINGLE_PASS_SAMPLING"), 1);
OutEnvironment.SetDefine(TEXT("USE_RTXDI_NEED_AVG_DISTANCE"), 1);
OutEnvironment.SetDefine(TEXT("USE_RTXDI_DEBUG"), RTXDI_MONOLITHIC_SINGLE_PASS_DEBUG_ENABLED);
OutEnvironment.SetDefine(TEXT("USE_RTXDI_DEBUG_PRINT"), RTXDI_MONOLITHIC_SINGLE_PASS_DEBUG_ENABLED);
}
:
};
- シェーダー(SampledDirectLighting.usf)
```C++
/***************************************************************************************************
*
* SampledDirectLighting
*
* Single-pass RGS to sample and shade one pass. Uses no history and no spatial resampling.
* Is a semi-brute-force implementation of sampled lighting.
*
***************************************************************************************************/
[shader("raygeneration")]
void SampledDirectLightingRGS()
{
uint2 PixelCoord = DispatchRaysIndex().xy + View.ViewRectMin.xy;
uint LinearIndex = CalcLinearIndex(PixelCoord);
FRandomContext RandContext = FRandomContext::Create(LinearIndex, View.StateFrameIndex);
// seed an RNG to be coherent across a small tile
uint CoherentLinearIndex = CalcLinearIndex(PixelCoord / 8);
FRandomContext CoherentRandContext = FRandomContext::Create(CoherentLinearIndex, View.StateFrameIndex + 63);
// Get Surface data
FGBufferSurface Surface = GetGBufferSurfaceFromSceneTexturesLoad(PixelCoord);
Surface.LightingChannelMask = GetSceneLightingChannel(PixelCoord);
const float LocalCoverage = 1.0f;
bool DebugEnabled = true;
float RayDistance = 0.0;
float HitCount = 0.0;
float3 SpecularExitantRadiance = 0.0;
float3 DiffuseExitantRadiance = 0.0;
// Mask out depth values that are infinitely far away
const bool bTraceRay = ((Surface.DeviceZ > 0.0) && (Surface.GBuffer.ShadingModelID != SHADINGMODELID_UNLIT));
if (bTraceRay)
{
// Evaluate RTXDI hit lighting
const HitLightingSamplingResult HitLightingResult = EvaluateHitLighting( PixelCoord,
Surface, RandContext, CoherentRandContext, RTXDI_OPTIONAL_PASS_DEBUG_PARAM(DebugContext) );
RayDistance = HitLightingResult.RayDistance;
HitCount = HitLightingResult.HitCount;
SpecularExitantRadiance = HitLightingResult.SpecularExitantRadiance;
DiffuseExitantRadiance = HitLightingResult.DiffuseExitantRadiance;
}
if (DemodulateMaterials && Surface.GBuffer.ShadingModelID != SHADINGMODELID_HAIR)
{
DiffuseExitantRadiance /= Surface.GBuffer.DiffuseColor * 0.999 + 0.001;
SpecularExitantRadiance /= Surface.GBuffer.SpecularColor * 0.999 + 0.001;
}
float3 OutputDiffuseRadiance = DiffuseExitantRadiance * View.PreExposure;
float3 OutputSpecularRadiance = SpecularExitantRadiance * View.PreExposure;
OutputDiffuseRadiance = ClampToHalfFloatRange(OutputDiffuseRadiance * LocalCoverage);
OutputSpecularRadiance = ClampToHalfFloatRange(OutputSpecularRadiance * LocalCoverage);
#if SAMPLE_DIRECTIONALLIGHT_ONLY
const float RayCount = RWDiffuseUAV[PixelCoord].a;
RWRayDistanceUAV[PixelCoord] = (RWRayDistanceUAV[PixelCoord] * RayCount + RayDistance * NumDirectionalCandidates) / (NumDirectionalCandidates + RayCount);
RWDiffuseUAV[PixelCoord].rgb += OutputDiffuseRadiance;
RWDiffuseUAV[PixelCoord].a = 1.0;
RWSpecularUAV[PixelCoord].rgb += OutputSpecularRadiance;
RWSpecularUAV[PixelCoord].a = 1.0;
#elif !HAIR_SHADING
RWDiffuseUAV[PixelCoord].rgb = OutputDiffuseRadiance;
RWDiffuseUAV[PixelCoord].a = 1.0;
RWSpecularUAV[PixelCoord].rgb = OutputSpecularRadiance;
RWSpecularUAV[PixelCoord].a = 1.0;
RWRayDistanceUAV[PixelCoord] = RayDistance / max(1.0, HitCount);
#else
// performing the blend op for lighting here as we only touch each sample once
float4 Source = RWSpecularUAV[ThreadCoord];
RWSpecularUAV[ThreadCoord].rgb = Source.rgb + OutputSpecularRadiance + OutputDiffuseRadiance; // hair is technically only specular
RWSpecularUAV[ThreadCoord].a = max(Source.a, LocalCoverage);
#endif
}
C++(SampledLightRendering.cpp)
class FGenerateInitialSamplesRGS : public FGlobalShader
{
:
class FRisSampleLocalLightsDim : SHADER_PERMUTATION_INT("RIS_SAMPLE_LOCAL_LIGHTS", 3);
class FAnisotropicMaterialsDim : SHADER_PERMUTATION_BOOL("SUPPORTS_ANISOTROPIC_MATERIALS");
class FSupportBrdfRayDim : SHADER_PERMUTATION_BOOL("SAMPLE_BRDF_RAYS");
:
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
ApplySampledLightingGlobalSettings(OutEnvironment);
FPermutationDomain PermutationVector(Parameters.PermutationId);
if (PermutationVector.Get<FSupportBrdfRayDim>())
{
if (GRHISupportsInlineRayTracingInNvRTX)
OutEnvironment.CompilerFlags.Add(CFLAG_InlineRayTracing);
}
OutEnvironment.SetDefine(TEXT("RTXDI_IGNORE_BRDF_BASECOLOR"), 1);
}
:
};
- シェーダー(GenerateInitialSamples.usf)
```C++
/***************************************************************************************************
*
* GenerateInitialSamples
*
* Draw random samples from the light list and evaluate an approximate luminance to select
* a weighted random sample. After selection, test the sample for visibility, and reject if not
* visible.
*
***************************************************************************************************/
[shader("raygeneration")]
void GenerateInitialSamplesRGS()
{
const uint2 PixelCoord = DispatchRaysIndex().xy + View.ViewRectMin.xy;
uint LinearIndex = CalcLinearIndex(PixelCoord);
FRandomContext RandContext = FRandomContext::Create(LinearIndex, View.StateFrameIndex + HistoryReservoir * 32 + INITIAL_SAMPLE_PASS_NUM * 63);
// seed an RNG to be coherent across a small tile
uint CoherentLinearIndex = CalcLinearIndex(DispatchRaysIndex().xy / 8);
FRandomContext CoherentRandContext = FRandomContext::Create(CoherentLinearIndex, View.StateFrameIndex + HistoryReservoir * 32 * INITIAL_SAMPLE_PASS_NUM * 63);
// Get Surface data
FGBufferSurface Surface = GetGBufferSurfaceFromSceneTexturesLoad(PixelCoord);
Surface.LightingChannelMask = GetSceneLightingChannel(PixelCoord);
RTXDI_SDK_Reservoir state = RTXDI_SDK_Reservoir::Empty();
const bool bIsDepthValid = Surface.DeviceZ > 0.0;
const bool bIsValidPixel = Surface.GBuffer.ShadingModelID != SHADINGMODELID_UNLIT && bIsDepthValid;
if (bIsValidPixel)
{
InitialCandidateOptions Options;
Options.NumLocalSamples = NumInitialLocalCandidates;
Options.NumDirectionalSamples = NumInitialDirectionalCandidates;
Options.NumSkyLightSamples = RISSkylightBufferTiles > 0 ? NumInitialSkyCandidates : 0;
Options.NumBrdfSamples = NumInitialBrdfCandidates;
Options.InitialSampleVisibility = InitialSampleVisibility;
state = ProduceInitialSample(
PixelCoord,
Surface,
Options,
RandContext,
CoherentRandContext);
}
WriteReservoirData(PixelCoord,OutputSlice, state.Store());
}
C++(SampledLightRendering.cpp)
class FApplyTemporalResamplingRGS : public FGlobalShader
{
:
class FFuseInitialSamplingDim : SHADER_PERMUTATION_BOOL("FUSE_TEMPORAL_AND_INITIAL_SAMPLING");
class FRisSampleLocalLightsDim : SHADER_PERMUTATION_INT("RIS_SAMPLE_LOCAL_LIGHTS", 3);
class FAnisotropicMaterialsDim : SHADER_PERMUTATION_BOOL("SUPPORTS_ANISOTROPIC_MATERIALS");
class FSupportBrdfRayDim : SHADER_PERMUTATION_BOOL("SAMPLE_BRDF_RAYS");
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
ApplySampledLightingGlobalSettings(OutEnvironment);
FPermutationDomain PermutationVector(Parameters.PermutationId);
if (PermutationVector.Get<FSupportBrdfRayDim>())
{
if (GRHISupportsInlineRayTracingInNvRTX)
OutEnvironment.CompilerFlags.Add(CFLAG_InlineRayTracing);
}
OutEnvironment.SetDefine(TEXT("RTXDI_IGNORE_BRDF_BASECOLOR"), 1);
}
:
};
- シェーダー(ApplyTemporalResampling.usf)
```C++
RWTexture2D<uint> RWPreviousSampleUAV;
Texture2D<float> DepthHistory;
Texture2D<float4> NormalHistory;
Texture2D<uint> PermutationSamplingMask;
#if FUSE_TEMPORAL_AND_INITIAL_SAMPLING
int NumInitialLocalCandidates;
int NumInitialDirectionalCandidates;
int NumInitialBrdfCandidates;
int NumInitialSkyCandidates;
int InitialSampleVisibility;
#endif
int InputSlice;
int OutputSlice;
int HistoryReservoir;
int MaxTemporalHistory;
int MaxSSSTemporalHistory;
int ApplyApproximateVisibilityTest;
int SpatiallyHashTemporalReprojection;
float TemporalDepthRejectionThreshold;
float TemporalNormalRejectionThreshold;
void ReconstructPrevTranslatedWorldPositionAndCameraDirectionFromDeviceZ(uint2 PixelCoord, float DeviceZ, out float3 OutTranslatedWorldPosition, out float3 OutCameraDirection)
{
// reprojection to prior frame based on TSRDilateVelocity
float2 PixelPos = PixelCoord - View.ViewRectMin.xy;
float2 ViewportUV = (PixelPos.xy + 0.5f) * View.ViewSizeAndInvSize.zw;
float2 PrevScreenPos = ViewportUVToScreenPos(ViewportUV);
float PrevDepth = ConvertFromDeviceZ(DeviceZ);
float4 PrevClipPosition = float4(PrevScreenPos * (View.ViewToClip[3][3] < 1.0f ? PrevDepth : 1.0f), PrevDepth, 1);
float3 PreViewTranslationOffset = LWCToFloat(LWCSubtract(PrimaryView.PreViewTranslation, PrimaryView.PrevPreViewTranslation));
float3 PrevTranslatedWorldPosition = mul(PrevClipPosition, View.PrevScreenToTranslatedWorld).xyz + PreViewTranslationOffset;
OutTranslatedWorldPosition = PrevTranslatedWorldPosition.xyz;
OutCameraDirection = normalize(PrevTranslatedWorldPosition.xyz - View.PrevTranslatedWorldCameraOrigin);
}
FGBufferSurface CreateRTXDIPrevSurface(uint2 Coord, FGBufferData CurrentGBuffer) {
float3 PrevWorldNormal = normalize(DecodeNormal(NormalHistory.Load(int3(Coord, 0)).xyz));
FGBufferSurface Surface = (FGBufferSurface)0;
Surface.GBuffer = CurrentGBuffer;
Surface.GBuffer.WorldNormal = PrevWorldNormal;
Surface.DeviceZ = DepthHistory.Load(int3(Coord, 0)).r;
Surface.GBuffer.Depth = ConvertFromDeviceZ(Surface.DeviceZ);
ReconstructPrevTranslatedWorldPositionAndCameraDirectionFromDeviceZ(Coord, Surface.DeviceZ, Surface.TranslatedWorldPosition, Surface.ViewDirection);
return Surface;
}
// Permutation for temporal sampling to increase noise
int2 ApplyPermutationSampling(int2 InPosition)
{
// Hash the frame index to produce a high frequency perturbation on the temporal sample selected
// This introduces more noise, but reduces larger scale artifacts
// Presently using the weaker hash function, no quality difference was seen with StrongIntegerHash
uint UniformRandom = WeakIntegerHash(View.StateFrameIndex);
int2 HashOffset = { UniformRandom & 3, (UniformRandom >> 2) & 3 };
InPosition -= HashOffset;
InPosition.xy ^= 3;
InPosition += HashOffset;
return InPosition;
}
/***************************************************************************************************
*
* ApplyTemporalResampling
*
* Shader to handle temporal resampling of light reservoirs.
*
***************************************************************************************************/
[shader("raygeneration")]
void ApplyTemporalResamplingRGS()
{
uint2 PixelCoord = DispatchRaysIndex().xy + View.ViewRectMin.xy;
if (any(DispatchRaysIndex().xy > View.ViewSizeAndInvSize.xy))
{
return;
}
uint LinearIndex = CalcLinearIndex(PixelCoord);
FRandomContext RandContext = FRandomContext::Create(LinearIndex, View.StateFrameIndex + HistoryReservoir * 32 + 63 * TEMPORAL_SAMPLE_PASS_NUM);
// seed an RNG to be coherent across a small tile
uint CoherentLinearIndex = CalcLinearIndex(DispatchRaysIndex().xy / 8);
FRandomContext CoherentRandContext = FRandomContext::Create(CoherentLinearIndex, View.StateFrameIndex + HistoryReservoir * 32 + 63 * INITIAL_SAMPLE_PASS_NUM);
// Get Surface Data
FGBufferSurface Surface = GetGBufferSurfaceFromSceneTexturesLoad(PixelCoord);
Surface.LightingChannelMask = GetSceneLightingChannel(PixelCoord);
int LocalMaxTemporalHistory = UseSubsurfaceProfile(Surface.GBuffer.ShadingModelID) ? MaxSSSTemporalHistory : MaxTemporalHistory;
RTXDI_SDK_Reservoir state = RTXDI_SDK_Reservoir::Empty();
int2 SelectedPrevSample = -1;
const bool bIsDepthValid = Surface.DeviceZ > 0.0;
const bool bIsValidPixel = Surface.GBuffer.ShadingModelID != SHADINGMODELID_UNLIT && bIsDepthValid;
bool UsePermutationSampling = false;
if (SpatiallyHashTemporalReprojection != 0)
{
UsePermutationSampling = PermutationSamplingMask[PixelCoord].r;
}
if (bIsValidPixel)
{
#if !FUSE_TEMPORAL_AND_INITIAL_SAMPLING
RTXDI_SDK_Reservoir curSample = RTXDI_SDK_Reservoir::Load(ReadReservoirData(PixelCoord, InputSlice));
#else
RTXDI_SDK_Reservoir curSample = RTXDI_SDK_Reservoir::Empty();
InitialCandidateOptions Options;
Options.NumLocalSamples = NumInitialLocalCandidates;
Options.NumDirectionalSamples = NumInitialDirectionalCandidates;
Options.NumSkyLightSamples = RISSkylightBufferTiles > 0 ? NumInitialSkyCandidates : 0;
Options.NumBrdfSamples = NumInitialBrdfCandidates;
Options.InitialSampleVisibility = InitialSampleVisibility;
curSample = ProduceInitialSample(
PixelCoord,
Surface,
Options,
RandContext,
CoherentRandContext);
#endif
int historyLimit = min(RTXDI_SDK_Reservoir::MaxM,LocalMaxTemporalHistory * curSample.M);
int selectedLightPrevID = -1;
if (curSample.sampleRef.IsValid())
{
uint currentLightID = curSample.sampleRef.GetLightIndex();
// map back to the previous frame's light list enab le better bias correction
selectedLightPrevID = SampledLightData.LightIndexBackwardRemapTable[currentLightID];
}
#if RESERVOIR_CORRECTION_IN_RESAMPLING
ReverseCorrectReservoir(curSample, Surface);
#endif
state.CombineReservoirs(curSample, /* random = */ 0.5, curSample.targetPdf);
// Backproject this pixel to last frame
// start by just using our sample position
int2 prevPos = PixelCoord;
float ExpectedPrevLinearDepth = Surface.GBuffer.Depth;
float2 ViewUV = (DispatchRaysIndex().xy + 0.5) * View.ViewSizeAndInvSize.zw;
float4 NDC = float4(ViewUV * float2(2, -2) + float2(-1, 1), Surface.DeviceZ, 1);
if (Surface.GBuffer.Velocity.x > 0.0)
{
float2 Velocity = DecodeVelocityFromTexture(Surface.GBuffer.Velocity).xy;
float2 PrevNDC = NDC.xy - Velocity;
float2 PrevUV = (PrevNDC.xy * float2(1, -1) + 1) * 0.5;
// randomize selection within the footprint of the back-projected sample and clamp to the viewport extents
if(!UsePermutationSampling)
PrevUV = PrevUV + (RandContext.GenerateSample2D() - 0.5) * View.ViewSizeAndInvSize.zw;
prevPos = saturate(PrevUV) * View.ViewSizeAndInvSize.xy + View.ViewRectMin.xy;
ExpectedPrevLinearDepth = ConvertFromDeviceZ(Surface.DeviceZ - DecodeVelocityFromTexture(Surface.GBuffer.Velocity).z);
}
else
{
float4 PrevNDC = mul(NDC, View.ClipToPrevClip);
PrevNDC.xyz /= PrevNDC.w;
float2 PrevUV = (PrevNDC.xy * float2(1, -1) + 1) * 0.5;
// randomize selection within the footprint of the back-projected sample and clamp to the viewport extents
PrevUV = saturate(PrevUV + (RandContext.GenerateSample2D() - 0.5) * View.ViewSizeAndInvSize.zw);
prevPos = PrevUV * View.ViewSizeAndInvSize.xy + View.ViewRectMin.xy;
ExpectedPrevLinearDepth = ConvertFromDeviceZ(PrevNDC.z);
}
//ToDo - full GBuffer not available for last frame, so we're going to need to approximate using current + channels we have
// could make a better approximation than what is used here
FGBufferSurface PrevSurface;
bool foundNeighbor = false;
const float radius = 4;
// Try to find a matching surface in the neighborhood of the reprojected pixel
for (int i = 0; i < 9; i++)
{
int2 offset = 0;
int2 idx = prevPos;
if (i > 0)
{
offset = int2((RandContext.GenerateSample2D() - 0.5f) * radius);
idx = prevPos + offset;
}
else if(UsePermutationSampling)
{
idx = ApplyPermutationSampling(idx);
}
PrevSurface = CreateRTXDIPrevSurface(idx, Surface.GBuffer);
// TODO: refine sample rejection tests
if (dot(Surface.GBuffer.WorldNormal, PrevSurface.GBuffer.WorldNormal) < TemporalNormalRejectionThreshold)
{
continue;
}
if (abs(ExpectedPrevLinearDepth - PrevSurface.GBuffer.Depth) / ExpectedPrevLinearDepth > TemporalDepthRejectionThreshold)
{
continue;
}
prevPos = idx;
foundNeighbor = true;
break;
}
bool selectedPreviousSample = false;
uint previousM = 0;
float previousWeight = 0;
if (foundNeighbor)
{
// Resample the previous frame sample into the current reservoir, but reduce the light's weight
// according to the bilinear weight of the current pixel
RTXDI_SDK_Reservoir prevSample = RTXDI_SDK_Reservoir::Load(ReadReservoirHistoryData(prevPos, HistoryReservoir));
prevSample.M = min(prevSample.M, historyLimit);
int originalPrevLightID = prevSample.sampleRef.GetLightIndex();
// Map the light ID from the previous frame into the current frame, if it still exists
if (prevSample.sampleRef.IsValid())
{
int mappedLightID = -1;
SelectedPrevSample = prevPos;
// remap light data
mappedLightID = SampledLightData.LightIndexRemapTable[prevSample.sampleRef.GetLightIndex()];
// invalid index
if (mappedLightID == -1)
{
// Kill the reservoir
prevSample.weightSum = 0;
prevSample.sampleRef = RTXDI_SDK_LightSampleRef::Invalid();
}
else
{
// Sample is valid - modify the light ID stored
prevSample.sampleRef.SetLightIndex(mappedLightID);
}
}
if (prevSample.sampleRef.IsValid())
{
bool Visible = true;
#if (VISIBILITY_BEFORE_COMBINE)
if (ApplyApproximateVisibilityTest)
{
Visible = LoadAndCheckApproximateVisibility(PixelCoord, Surface, prevSample.sampleRef);
}
#endif
// skip combining if we know that the light is not visible from this sample to avoid merging the history
if (Visible)
{
FSampledLightData Light;
FLightSampleLocation LightSample;
GetLightSampleData(Surface, prevSample.sampleRef, LightSample, Light);
const uint PrevLightIdx = prevSample.sampleRef.GetLightIndex();
const bool ValidPrevSample = (LightSample.Pdf > 0.f) && (Surface.LightingChannelMask & GetSampledLightChannelMask(PrevLightIdx));
previousM = prevSample.M;
previousWeight = ValidPrevSample ? GetApproximateLightSampleWeightWithLight(Surface, LightSample, Light) : 0.f;
#if RESERVOIR_CORRECTION_IN_RESAMPLING
if (LightSample.Pdf > 0.f)
previousWeight /= LightSample.Pdf;
#endif
if (state.CombineReservoirs(prevSample, RandContext.GenerateSample1D(), previousWeight))
{
selectedPreviousSample = true;
selectedLightPrevID = int(originalPrevLightID);
}
}
}
}
// Use prior light history to improve normalization
// Compute the unbiased normalization term (instead of using 1/M)
float pi = state.targetPdf; // Since it was selected, this is known to be equiv to lightWeight(state.sampleRef, context)
float piSum = state.targetPdf * curSample.M;
if (state.sampleRef.IsValid() && selectedLightPrevID >= 0 && previousM > 0)
{
// remap into last frame's light list
RTXDI_SDK_LightSampleRef SampleRef = state.sampleRef;
SampleRef.SetLightIndex(selectedLightPrevID + SampledLightData.LightHistoryOffset);
#if RESERVOIR_CORRECTION_IN_RESAMPLING
FSampledLightData Light;
FLightSampleLocation LightSample;
GetLightSampleData(PrevSurface, SampleRef, LightSample, Light);
float pt = GetApproximateLightSampleWeightWithLight(PrevSurface, LightSample, Light);
pt = (LightSample.Pdf > 0.f) ? (pt / LightSample.Pdf) : 0.f;
#else
float pt = GetApproximateLightSampleWeight(PrevSurface, SampleRef);
#endif
#if (!VISIBILITY_BEFORE_COMBINE)
//ToDo - does this need to be a permutation?
if (ApplyApproximateVisibilityTest && pt > 0)
{
bool Visible = LoadAndCheckApproximateVisibility(PixelCoord, Surface, state.sampleRef);
if (!Visible)
{
pt = 0;
}
}
#endif
pi = selectedPreviousSample ? pt : pi;
piSum += pt * previousM;
}
#if RESERVOIR_CORRECTION_IN_RESAMPLING
CorrectReservoir(state, Surface);
#endif
state.FinalizeResampling(pi, piSum);
}
WriteReservoirData(PixelCoord, OutputSlice, state.Store());
uint EncodedSample = (SelectedPrevSample.x & 0xffff) | ((SelectedPrevSample.y & 0xffff) << 16);
RWPreviousSampleUAV[PixelCoord] = EncodedSample;
}
C++(SampledLightRendering.cpp)
class FApplyBoilingFilterCS : public FGlobalShader
{
:
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.CompilerFlags.Add(CFLAG_ForceDXC);
ApplySampledLightingGlobalSettings(OutEnvironment);
OutEnvironment.SetDefine(TEXT("RTXDI_BOILING_FILTER_GROUP_SIZE"), GetThreadGroupSize());
}
:
};
- シェーダー(BoilingFilter.usf)
```C++
int InputSlice;
int OutputSlice;
float BoilingFilterStrength;
/***************************************************************************************************
*
* Boiling Filter
*
* Identify runaway samples and kill them to avoid them dominating the result.
*
***************************************************************************************************/
#define RTXDI_BOILING_FILTER_MIN_LANE_COUNT 32
groupshared float s_weights[(RTXDI_BOILING_FILTER_GROUP_SIZE * RTXDI_BOILING_FILTER_GROUP_SIZE) / RTXDI_BOILING_FILTER_MIN_LANE_COUNT];
groupshared uint s_count[(RTXDI_BOILING_FILTER_GROUP_SIZE * RTXDI_BOILING_FILTER_GROUP_SIZE) / RTXDI_BOILING_FILTER_MIN_LANE_COUNT];
// Boiling filter that is applied at the end of the temporal resampling pass.
// The filter analyzes the weights of all reservoirs in a thread group, and discards
// the reservoirs whose weights are very high, i.e. above a certain threshold.
// If the temporal resampling pass is implemented as a ray-gen shader, and therefore
// doesn't have access to thread groups or shared memory, the boiling filter can be applied
// as a separate compute pass.
// Returns true if the reservoir in this thread passes the filter and should be kept.
bool RTXDI_SDK_BoilingFilter(uint2 LocalIndex, float FilterStrength, float weight)
{
// Apply the boiling filter.
// Boiling happens when some highly unlikely light is discovered and it is relevant
// for a large surface area around the pixel that discovered it. Then this light sample
// starts to propagate to the neighborhood through spatiotemporal reuse, which looks like
// a flash. We can detect such lights because their weight is significantly higher than
// the weight of their neighbors. So, compute the average group weight and apply a threshold.
if (FilterStrength > 0.f)
{
float boilingFilterMultiplier = 10.f / FilterStrength - 9.f;
// Start with average nonzero weight within the wavefront
float waveWeight = WaveActiveSum(weight);
uint waveCount = WaveActiveCountBits(weight > 0);
// Store the results of each wavefront into shared memory
uint linearThreadIndex = LocalIndex.x + LocalIndex.y * RTXDI_BOILING_FILTER_GROUP_SIZE;
uint waveIndex = linearThreadIndex / WaveGetLaneCount();
if (WaveIsFirstLane())
{
s_weights[waveIndex] = waveWeight;
s_count[waveIndex] = waveCount;
}
GroupMemoryBarrierWithGroupSync();
// Reduce the per-wavefront averages into a global average using one wavefront
if (linearThreadIndex < (RTXDI_BOILING_FILTER_GROUP_SIZE * RTXDI_BOILING_FILTER_GROUP_SIZE) / WaveGetLaneCount())
{
waveWeight = s_weights[linearThreadIndex];
waveCount = s_count[linearThreadIndex];
waveWeight = WaveActiveSum(waveWeight);
waveCount = WaveActiveSum(waveCount);
if (linearThreadIndex == 0)
{
s_weights[0] = (waveCount > 0) ? (waveWeight / float(waveCount)) : 0.0;
}
}
GroupMemoryBarrierWithGroupSync();
// Read the per-group average and apply the threshold
float averageNonzeroWeight = s_weights[0];
if (weight > averageNonzeroWeight * boilingFilterMultiplier)
{
return false;
}
}
return true;
}
[numthreads(RTXDI_BOILING_FILTER_GROUP_SIZE, RTXDI_BOILING_FILTER_GROUP_SIZE, 1)]
void BoilingFilterCS(uint3 GlobalIndex : SV_DispatchThreadID, uint3 LocalIndex : SV_GroupThreadID)
{
uint2 PixelCoord = GlobalIndex.xy + View.ViewRectMin.xy;
if (any(GlobalIndex.xy >= View.ViewSizeAndInvSize.xy))
{
PixelCoord -= int2(RTXDI_BOILING_FILTER_GROUP_SIZE, RTXDI_BOILING_FILTER_GROUP_SIZE);
}
RTXDI_SDK_Reservoir Sample = RTXDI_SDK_Reservoir::Load(ReadReservoirData(PixelCoord, InputSlice));
if (!RTXDI_SDK_BoilingFilter(LocalIndex.xy, BoilingFilterStrength, Sample.weightSum))
{
Sample = RTXDI_SDK_Reservoir::Empty();
}
if (all(GlobalIndex.xy < View.ViewSizeAndInvSize.xy))
{
WriteReservoirData(PixelCoord, OutputSlice, Sample.Store());
}
}
C++(SampledLightRendering.cpp)
class FApplySpatialResamplingRGS : public FGlobalShader
{
:
class FPairwiseMISDim : SHADER_PERMUTATION_BOOL("USE_PAIRWISE_MIS");
class FAnisotropicMaterialsDim : SHADER_PERMUTATION_BOOL("SUPPORTS_ANISOTROPIC_MATERIALS");
:
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
ApplySampledLightingGlobalSettings(OutEnvironment);
OutEnvironment.SetDefine(TEXT("RTXDI_IGNORE_BRDF_BASECOLOR"), 1);
}
:
};
- シェーダー(ApplySpatialResampling.usf)
```C++
int InputSlice;
int OutputSlice;
int HistoryReservoir;
int SpatialSamples;
int SpatialSamplesBoost;
int ApplyApproximateVisibilityTest;
int DiscountNaiveSamples;
float SpatialSamplingRadius;
float SpatialDepthRejectionThreshold;
float SpatialNormalRejectionThreshold;
int MaxTemporalHistory;
int MaxSSSTemporalHistory;
uint NeighborOffsetMask;
/***************************************************************************************************
*
* ApplySpatialResampling
*
* Shader to handle spatial resampling of light reservoirs.
*
***************************************************************************************************/
//
// Presently, using RGS as it allows occlusion rays. Simplifies code management to not have shader
// type not change due to runtime option
//
[shader("raygeneration")]
void ApplySpatialResamplingRGS()
{
uint2 PixelCoord = DispatchRaysIndex().xy + View.ViewRectMin.xy;
if (any(DispatchRaysIndex().xy > View.ViewSizeAndInvSize.xy))
{
return;
}
uint LinearIndex = CalcLinearIndex(PixelCoord);
FRandomContext RandContext = FRandomContext::Create(LinearIndex, View.StateFrameIndex + HistoryReservoir * 32 + SPATIAL_SAMPLE_PASS_NUM * 63);
// Get Surface data
FGBufferSurface Surface = GetGBufferSurfaceFromSceneTexturesLoad(PixelCoord);
RTXDI_SDK_Reservoir state = RTXDI_SDK_Reservoir::Empty();
const bool bIsDepthValid = Surface.DeviceZ > 0.0;
const bool bIsValidPixel = Surface.GBuffer.ShadingModelID != SHADINGMODELID_UNLIT && bIsDepthValid;
if (bIsValidPixel)
{
RTXDI_SDK_Reservoir CenterSample = RTXDI_SDK_Reservoir::Load(ReadReservoirData(PixelCoord, InputSlice));
SpatialSamplingParameters Params =
{
SpatialSamples,
SpatialSamplesBoost,
SpatialSamplingRadius,
SpatialNormalRejectionThreshold,
SpatialDepthRejectionThreshold,
InputSlice,
DiscountNaiveSamples,
ApplyApproximateVisibilityTest,
true,
UseSubsurfaceProfile(Surface.GBuffer.ShadingModelID) ? MaxSSSTemporalHistory : MaxTemporalHistory,
NeighborOffsetMask
};
/* ココ */
ApplySpatialResampling(PixelCoord, Surface, CenterSample, Params, RandContext, state);
}
WriteReservoirData(PixelCoord, OutputSlice, state.Store());
}
シェーダー(SpatialResampling.ush)
void ApplySpatialResampling(uint2 PixelCoord, FGBufferSurface Surface, RTXDI_SDK_Reservoir centerSample, SpatialSamplingParameters Param, inout FRandomContext RandContext, inout RTXDI_SDK_Reservoir state)
{
// We loop through neighbors twice. Cache the validity / edge-stopping function
// results for the 2nd time through.
uint cachedResult = 0x0u;
// This is the weight we'll use (instead of 1/M) to make our estimate unbaised (see paper).
float normalizationWeight = 1.0f;
// Since we're using our bias correction scheme, we need to remember which light selection we made
int selectedLightIdx = -1;
float2 SelectedUV = (float2)0;
int selected = -1;
const uint IndexLimit = SampledLightData.LocalLightCount + SampledLightData.DirectionalLightCount + SampledLightData.DirectionalLightStart;
if (centerSample.sampleRef.IsValid() && centerSample.sampleRef.GetLightIndex() < IndexLimit)
{
selectedLightIdx = centerSample.sampleRef.GetLightIndex();
SelectedUV = centerSample.sampleRef.GetUV();
}
ReverseCorrectReservoir(centerSample, Surface);
state.CombineReservoirs(centerSample, /* random = */ 0.5f, centerSample.targetPdf);
//
// Walk the specified number of neighbors, resampling using RIS
//
// Two sample modes for spatial resampling
// predefined low-discrepency sequence
// random data stored in local array for use with second pass
int NumSamples = Param.SpatialSamples;
if (centerSample.M < Param.MaxTemporalHistory)
{
NumSamples = max(NumSamples, Param.SpatialSamplesBoost);
}
NumSamples = min(NumSamples, MAX_SPATIAL_SAMPLES);
int2 SamplePoints[MAX_SPATIAL_SAMPLES] = (int2[MAX_SPATIAL_SAMPLES])0;
uint StartIdx = RandContext.GenerateSample1D() * Param.NeighborOffsetMask;
// using uint mask to track samples, so absolute limit is 32
NumSamples = min(NumSamples, 32);
for (int i = 0; i < NumSamples; ++i)
{
float2 Offset = RandContext.GenerateSample2D() * 2.0f - 1.0f;
int2 SampleCoord = round(float2(PixelCoord) + (Offset * Param.SpatialSamplingRadius));
SamplePoints[i] = SampleCoord;
float2 Offset = NeighborOffsets[(StartIdx + i) & Param.NeighborOffsetMask] * 2.0f - 1.0f;
int2 SampleCoord = round(float2(PixelCoord) + (Offset * Param.SpatialSamplingRadius));
if (any(SampleCoord < 0) || any(SampleCoord >= View.BufferSizeAndInvSize.xy) || all(SampleCoord == PixelCoord))
{
continue;
}
// Get adjacent Surface data
FGBufferSurface AdjSurface = GetGBufferSurfaceFromSceneTexturesLoad(SampleCoord);
// TODO: refine sample rejection tests
if (dot(Surface.GBuffer.WorldNormal, AdjSurface.GBuffer.WorldNormal) < Param.SpatialNormalRejectionThreshold)
{
continue;
}
if (abs(Surface.GBuffer.Depth - AdjSurface.GBuffer.Depth) / Surface.GBuffer.Depth > Param.SpatialDepthRejectionThreshold)
{
continue;
}
if (Surface.GBuffer.ShadingModelID != AdjSurface.GBuffer.ShadingModelID)
{
continue;
}
RTXDI_SDK_Reservoir neighborSample = RTXDI_SDK_Reservoir::Load(ReadReservoirData(SampleCoord, Param.InputSlice));
ReverseCorrectReservoir(neighborSample, AdjSurface);
if (Param.DiscountNaiveSamples && neighborSample.M <= 2)
{
// Reject the sample to avoid merging data for which we have little confidence
continue;
}
// Load that neighbor's RIS state, do resampling
float neighborWeight = 0.0f;
if (neighborSample.sampleRef.IsValid())
{
//ToDo - does this need to be a permutation?
bool Visible = true;
FSampledLightData Light;
FLightSampleLocation LightSample;
GetLightSampleData(Surface, neighborSample.sampleRef, LightSample, Light);
if (Param.ApplyApproximateVisibilityTest)
{
Visible = CheckApproximateVisibility(SampleCoord, Surface, LightSample);
}
neighborWeight = Visible ? GetApproximateLightSampleWeightWithLight(Surface, LightSample, Light) : 0;
if (LightSample.Pdf > 0.f)
neighborWeight /= LightSample.Pdf;
{
cachedResult |= (1u << uint(i));
if (state.CombineReservoirs(neighborSample, RandContext.GenerateSample1D(), neighborWeight))
{
selected = i;
selectedLightIdx = neighborSample.sampleRef.GetLightIndex();
SelectedUV = neighborSample.sampleRef.GetUV();
}
}
}
}
if (state.sampleRef.IsValid())
{
if (Param.UseMISRenormalization)
{
// Compute the unbiased normalization term (instead of using 1/M)
float pi = state.targetPdf;
float piSum = state.targetPdf * centerSample.M;
// To do this, we need to walk our neighbors again
for (int i = 0; i < NumSamples; ++i)
{
// If we skipped this neighbor above, do so again.
if ((cachedResult & (1u << uint(i))) == 0) continue;
// Handle the rare cases when there is no light
if (selectedLightIdx == -1) continue;
int2 SampleCoord = SamplePoints[i];
float2 Offset = NeighborOffsets[(StartIdx + i) & Param.NeighborOffsetMask] * 2.0f - 1.0f;
int2 SampleCoord = round(float2(PixelCoord) + (Offset * Param.SpatialSamplingRadius));
// Get adjacent Surface data
FGBufferSurface AdjSurface = GetGBufferSurfaceFromSceneTexturesLoad(SampleCoord);
FSampledLightData Light;
FLightSampleLocation LightSample;
GetLightSampleData(AdjSurface, state.sampleRef, LightSample, Light);
// Get the PDF of the sample RIS selected in the first loop, above, *at this neighbor*
float ps = GetApproximateLightSampleWeightWithLight(AdjSurface, LightSample, Light);
ps = (LightSample.Pdf > 0.f) ? (ps / LightSample.Pdf) : 0.f;
float ps = GetApproximateLightSampleWeight(AdjSurface, state.sampleRef);
//ToDo - does this need to be a permutation?
if (Param.ApplyApproximateVisibilityTest && ps > 0)
{
bool Visible = LoadAndCheckApproximateVisibility(SampleCoord, AdjSurface, state.sampleRef);
if (!Visible)
{
ps = 0;
}
}
RTXDI_SDK_Reservoir neighborSample = RTXDI_SDK_Reservoir::Load(ReadReservoirData(SampleCoord, Param.InputSlice));
// Select this sample for the (normalization) numerator if this particular neighbor pixel
// was the one we selected via RIS in the first loop, above.
pi = selected == i ? ps : pi;
// Add to the sums of weights for the (normalization) denominator
piSum += ps * neighborSample.M;
}
CorrectReservoir(state, Surface);
// Use "MIS-like" normalization
state.FinalizeResampling(pi, piSum);
}
else
{
CorrectReservoir(state, Surface);
state.FinalizeResampling(1.0, state.M);
}
}
}
C++(SampledLightRendering.cpp)
class FEvaluateSampledLightingRGS : public FGlobalShader
{
:
class FHairLightingDim : SHADER_PERMUTATION_BOOL("USE_HAIR_LIGHTING");
class FAvoidSelfIntersectionDim : SHADER_PERMUTATION_BOOL("AVOID_SELF_INTERSECTION_TRACE");
class FAnisotropicMaterialsDim : SHADER_PERMUTATION_BOOL("SUPPORTS_ANISOTROPIC_MATERIALS");
:
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
ApplySampledLightingGlobalSettings(OutEnvironment);
OutEnvironment.SetDefine(TEXT("RTXDI_IGNORE_BRDF_BASECOLOR"), 1);
}
:
};
RWTexture2D<float4> RWDiffuseUAV;
RWTexture2D<float4> RWSpecularUAV;
RWTexture2D<float> RWRayDistanceUAV;
RWTexture2D<float2> RWLuminanceUAV;
int InputSlice; int NumReservoirs;
int MaxTemporalHistory; int MaxSSSTemporalHistory; int DemodulateMaterials; int SecondarySpatialPassSamples; int FeedbackVisibility; uint AlwaysSampleDirLight; uint NeighborOffsetMask;
/*** *
***/ [shader("raygeneration")] void EvaluateSampledLightingRGS() { const uint2 PixelCoord = DispatchRaysIndex().xy + View.ViewRectMin.xy;
const uint LinearIndex = CalcLinearIndex(PixelCoord); FRandomContext RandContext = FRandomContext::Create(LinearIndex, View.StateFrameIndex + EVALUATION_PASS_NUM * 63);
// Get Surface data const bool bForceConsiderColor = true; FGBufferSurface Surface = GetGBufferSurfaceFromSceneTexturesLoad(PixelCoord, bForceConsiderColor); int LocalTMaxTemporalHistory = UseSubsurfaceProfile(Surface.GBuffer.ShadingModelID) ? MaxSSSTemporalHistory : MaxTemporalHistory;
const bool bIsDepthValid = Surface.DeviceZ > 0.0; const bool bIsValidPixel = Surface.GBuffer.ShadingModelID != SHADINGMODELID_UNLIT && bIsDepthValid;
RTXDI_SDK_Reservoir risSample = RTXDI_SDK_Reservoir::Empty(); float3 SpecularExitantRadiance = 0.0; float3 DiffuseExitantRadiance = 0.0; float RayDistance = 0.0; float HitCount = 0.0;
float2 LuminanceHistory = 0.0f;
if (bIsValidPixel) { Surface.LightingChannelMask = GetSceneLightingChannel(PixelCoord);
const uint Stencil = SceneStencilTexture.Load(int3(PixelCoord, 0)) STENCIL_COMPONENT_SWIZZLE;
const bool bInexactShadowedGeometry = (Stencil & 1) && (InexactShadows != 0);
for (int Reservoir = 0; Reservoir < NumReservoirs; Reservoir++)
{
risSample = RTXDI_SDK_Reservoir::Load(ReadReservoirData(PixelCoord, InputSlice + Reservoir));
if ( SecondarySpatialPassSamples > 0 && risSample.M < LocalTMaxTemporalHistory)
{
const float3 DiffuseColor = Surface.GBuffer.DiffuseColor;
const float3 SpecularColor = Surface.GBuffer.SpecularColor;
Surface.GBuffer.DiffuseColor = float3(1.f, 1.f, 1.f);
Surface.GBuffer.SpecularColor = float3(1.f, 1.f, 1.f);
RTXDI_SDK_Reservoir CenterSample = risSample;
RTXDI_SDK_Reservoir state = RTXDI_SDK_Reservoir::Empty();
SpatialSamplingParameters Params =
{
SecondarySpatialPassSamples,
SecondarySpatialPassSamples,
16.0f,
0.5f, // default normal threshold
0.1f, // default depth threshold
InputSlice+ Reservoir,
0, //DiscountNaiveSamples,
0, //ApplyApproximateVisibilityTest,
false,
LocalTMaxTemporalHistory,
NeighborOffsetMask
};
ApplySpatialResampling(PixelCoord, Surface, CenterSample, Params, RandContext, state);
risSample = state;
// restore the colors
Surface.GBuffer.DiffuseColor = DiffuseColor;
Surface.GBuffer.SpecularColor = SpecularColor;
}
if (risSample.sampleRef.IsValid())
{
HitLightingSamplingResult LocalResult = (HitLightingSamplingResult)0;
LocalResult = EvalLightingInternal< HitLightingSamplingResult, FGBufferSurface>( risSample.sampleRef, risSample.weightSum, /* UCW */
PixelCoord, Surface, bInexactShadowedGeometry,
RandContext);
if (LocalResult.bEvaluateContribution)
{
if (Reservoir == 0)
{
LuminanceHistory.x = Luminance(LocalResult.DiffuseExitantRadiance.xyz);
LuminanceHistory.y = Luminance(LocalResult.SpecularExitantRadiance.xyz);
}
}
else
{
if (FeedbackVisibility)
{
risSample.weightSum = 0.0f;
risSample.targetPdf = 0.0f;
}
}
DiffuseExitantRadiance += LocalResult.DiffuseExitantRadiance.xyz;
SpecularExitantRadiance += LocalResult.SpecularExitantRadiance.xyz;
HitCount += LocalResult.HitCount;
RayDistance += LocalResult.RayDistance;
ReverseCorrectReservoir(risSample, Surface);
}
else
{
// sample occluded, kill it for the history
risSample = RTXDI_SDK_Reservoir::Empty();
}
WriteReservoirHistoryData(PixelCoord, Reservoir, risSample.Store());
}
DiffuseExitantRadiance /= float(NumReservoirs);
SpecularExitantRadiance /= float(NumReservoirs);
RayDistance /= max(HitCount, 1.0);
// Apply the pre-exposure scale to the resulting shaded value
DiffuseExitantRadiance *= View.PreExposure;
SpecularExitantRadiance *= View.PreExposure;
if (DemodulateMaterials && Surface.GBuffer.ShadingModelID != SHADINGMODELID_HAIR)
{
DiffuseExitantRadiance /= Surface.GBuffer.DiffuseColor * 0.999 + 0.001;
SpecularExitantRadiance /= Surface.GBuffer.SpecularColor * 0.999 + 0.001;
}
DiffuseExitantRadiance = ClampToHalfFloatRange(DiffuseExitantRadiance);
SpecularExitantRadiance = ClampToHalfFloatRange(SpecularExitantRadiance);
}
else
{
// Invalid pixel, write empty reservoir
for (int Reservoir = 0; Reservoir < NumReservoirs; Reservoir++)
{
WriteReservoirHistoryData(PixelCoord, Reservoir, risSample.Store());
}
}
RWLuminanceUAV[PixelCoord] = ClampToHalfFloatRange(LuminanceHistory* View.PreExposure);
RWDiffuseUAV[PixelCoord].rgb = DiffuseExitantRadiance;
RWDiffuseUAV[PixelCoord].a = AlwaysSampleDirLight ? HitCount : 1.0;
RWSpecularUAV[PixelCoord].rgb = SpecularExitantRadiance;
RWSpecularUAV[PixelCoord].a = 1.0;
RWRayDistanceUAV[PixelCoord] = RayDistance;
}
シェーダー(SampleDirectLighting.ush)
void CorrectReservoir(inout RTXDI_SDK_Reservoir Reservoir, in FGBufferSurface Surface)
{
if (!Reservoir.sampleRef.IsValid())
return;
FSampledLightData Light;
FLightSampleLocation LightSample;
GetLightSampleData(Surface, Reservoir.sampleRef, LightSample, Light);
Reservoir.targetPdf = (LightSample.Pdf > 0.f) ? (Reservoir.targetPdf * LightSample.Pdf) : 0.f;
}
void ReverseCorrectReservoir(inout RTXDI_SDK_Reservoir Reservoir, in FGBufferSurface Surface) { if (!Reservoir.sampleRef.IsValid()) return;
FSampledLightData Light;
FLightSampleLocation LightSample;
GetLightSampleData(Surface, Reservoir.sampleRef, LightSample, Light);
if (LightSample.Pdf > 0.f)
{
Reservoir.weightSum *= LightSample.Pdf;
Reservoir.targetPdf /= LightSample.Pdf;
}
else
{
Reservoir.weightSum = 0.f;
Reservoir.targetPdf = 0.f;
}
}
template<> bool CheckApproximateVisibility(uint2 PixelCoord, FGBufferSurface Surface, FLightSampleLocation LightSample) { FRayDesc Ray; Ray.Origin = Surface.TranslatedWorldPosition; Ray.Direction = LightSample.Direction; Ray.TMin = 0.1f; Ray.TMax = LightSample.Distance;
if (InexactShadows != 0)
{
const uint Stencil = SceneStencilTexture.Load(int3(PixelCoord, 0)) STENCIL_COMPONENT_SWIZZLE;
if (Stencil & 1)
{
// Always bias the max amount as this is the conservative test
Ray.TMin += MaxBiasForInexactGeometry;
}
}
Ray.TMin = max(Ray.TMin, AvoidSelfIntersectionDistance);
Ray.TMin = min(Ray.TMin, Ray.TMax);
// ToDo - respect the transmissive flag on lights
float NoL = dot(Surface.GetWorldNormal(), Ray.Direction);
if (NoL > 0.0f)
{
ApplyCameraRelativeDepthBias(Ray, PixelCoord, Surface.DeviceZ, Surface.GetWorldNormal(), MaxNormalBias);
}
else
{
ApplyPositionBias(Ray, -Surface.GetWorldNormal(), MaxNormalBias);
}
uint RayFlags = RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH | RAY_FLAG_SKIP_CLOSEST_HIT_SHADER;
RayFlags |= VisibilityFaceCull == 1 ? RAY_FLAG_CULL_FRONT_FACING_TRIANGLES : 0;
RayFlags |= VisibilityFaceCull == 2 ? RAY_FLAG_CULL_BACK_FACING_TRIANGLES : 0;
RayFlags |= VisibilityApproximateTestMode == 1 ? RAY_FLAG_FORCE_OPAQUE : 0;
RayFlags |= VisibilityApproximateTestMode == 2 ? RAY_FLAG_CULL_NON_OPAQUE : 0;
const uint InstanceInclusionMask = RAY_TRACING_MASK_SHADOW;
FMinimalPayload MinimalPayload = TraceVisibilityRay(
TLAS,
RayFlags,
InstanceInclusionMask,
Ray);
return !MinimalPayload.IsHit();
}
template
FSampledLightData Light = GetSampledDeferredLightDataWithSkylight(LightIndex, LightUV, SkylightTexture, SkylightTextureSampler);
FLightSampleLocation LightSample = ComputeLightSampleLocation(Light, Surface, LightUV);
return CheckApproximateVisibility(PixelCoord, Surface, LightSample);
}
// Estimate the radiance emitted by a light that contributes to the given GBuffer Surface template<> float3 GetDynamicLightingEstimate(FGBufferSurface Surface, FLightSampleLocation LightSample, FSampledLightData Light) { const FDeferredLightData LightData = Light.LightData;
const float3 L = LightSample.Direction;
const float3 V = -Surface.ViewDirection;
const float3 N = GetClearCoatBottomNormal(Surface.GBuffer, Surface.GetWorldNormal());
const float NoL = saturate(dot(N, L));
const FShadowTerms ShadowTerms = { 0.0, 1.0, 0.0, LightData.HairTransmittance };
FDirectLighting Lighting = EvaluateBxDF(Surface.GBuffer, N, V, L, NoL, ShadowTerms);
Lighting.Specular *= LightData.SpecularScale; //extra scale to reduce/enhance specular from lights
// JCaoNV - Ideally, this doesn't need to be here. That way we can probably remove Pdf in the FLightSampleLocation structure.
const float LightMask = (LightSample.Pdf > 0.0) ? 1.f : 0.f;
// The target PDF we are currently using is the unshadowed light contribution from the rendering equation.
// Please be noted that the cosine term in the rendering equation is already encoded inside the `FDirectLighting` result.
// The only thing that is missing is the visibility and that is done on purpose to keep performance in budget.
const float3 LightRadiance = Light.GetLightRadiance();
const float3 LightEstimate = LightSample.Attenuation * LightMask * (Lighting.Diffuse + Lighting.Specular + Lighting.Transmission) * LightRadiance;
return max(LightEstimate, 0.0f);
}
//------------------------------------------------------------------------------------------------------------------------------------ // Light Sample Weight //------------------------------------------------------------------------------------------------------------------------------------
template
template
return GetApproximateLightSampleWeightWithLight(Surface, LightSample, Light);
}
template
/ ココ / RTXDI_SDK_Reservoir BrdfReservoir = ProduceBrdfSample( Surface, Options, RandContext, CoherentRandContext);
RTXDI_SDK_Reservoir State = RTXDI_SDK_Reservoir::Empty();
float LocalVisibilityFactor = 1.0f;
float DirectionalVisibilityFactor = 1.0f;
bool PostTestVisibility = Options.InitialSampleVisibility == 1;
if (Options.InitialSampleVisibility == 2)
{
if (LocalReservoir.sampleRef.IsValid())
{
LocalVisibilityFactor = LoadAndCheckApproximateVisibility(PixelCoord, Surface, LocalReservoir.sampleRef) ? 1.0f : 0.0f;
}
if (DirectionalReservoir.sampleRef.IsValid())
{
DirectionalVisibilityFactor = LoadAndCheckApproximateVisibility(PixelCoord, Surface, DirectionalReservoir.sampleRef) ? 1.0f : 0.0f;
}
}
State.CombineReservoirs(LocalReservoir, 0.5f, LocalReservoir.targetPdf * LocalVisibilityFactor);
State.CombineReservoirs(DirectionalReservoir, RandContext.GenerateSample1D(), DirectionalReservoir.targetPdf * DirectionalVisibilityFactor);
State.CombineReservoirs(SkylightReservoir, RandContext.GenerateSample1D(), SkylightReservoir.targetPdf);
State.CombineReservoirs(BrdfReservoir, RandContext.GenerateSample1D(), BrdfReservoir.targetPdf);
if (PostTestVisibility && State.sampleRef.IsValid())
{
bool Visible = LoadAndCheckApproximateVisibility(PixelCoord, Surface, State.sampleRef);
if (!Visible)
{
State.sampleRef = RTXDI_SDK_LightSampleRef::Invalid();
State.weightSum = 0;
State.targetPdf = 0;
}
}
State.FinalizeResampling(1.0f, 1.0f);
State.M = 1;
return State;
}
template
// This is fine since this will not be read
const int FakeReGIRCell = 0;
const float FakeInvApproximatedNormalizationFactor = 1.f;
ProduceLocalSampleRandom(
Surface,
Options,
FakeReGIRCell,
FakeInvApproximatedNormalizationFactor,
RandContext,
CoherentRandContext,
LocalReservoir);
float3 CellJitter = CoherentRandContext.GenerateSample3D();
CellJitter -= 0.5f;
const float JitterScale = ReGIRGetJitterScale(Surface.TranslatedWorldPosition);
const float3 SamplePosition = Surface.TranslatedWorldPosition + CellJitter * JitterScale;
const int Cell = ReGIRGetCell(SamplePosition);
if (Cell == -1)
{
// no Regir cell found, due to being outside the grid, fallback
ProduceLocalSampleRandom(
Surface,
Options,
Cell,
1.f, /* won't be used */
RandContext,
CoherentRandContext,
LocalReservoir);
}
else
{
// Please refer to the [reference 2] for further implementation detail in this code branch
const float ApproximatedNormalizationFactor = ReGIRNormalizationBuffer[Cell];
const float InvApproximatedNormalizationFactor = ApproximatedNormalizationFactor > 0.f ? (1.f / ApproximatedNormalizationFactor) : 0.f;
// Multiple importance sampling is needed here to counter the potential bad samples produced from ReGir,
// which may cause boiling issue.
ProduceLocalSampleRandom(
Surface,
Options,
Cell,
InvApproximatedNormalizationFactor,
RandContext,
CoherentRandContext,
LocalReservoir,
Options.NumLocalSamples);
ProduceLocalSampleReGIR(
Surface,
Options,
Cell,
InvApproximatedNormalizationFactor,
RandContext,
CoherentRandContext,
LocalReservoir);
}
ProduceLocalSampleRIS(
Surface,
Options,
RandContext,
CoherentRandContext,
LocalReservoir);
#error "Undefined local light sampling method"
LocalReservoir.FinalizeResampling(1.0, 1.0);
LocalReservoir.M = 1;
return LocalReservoir;
}
template
// Discrete PDF of picking the light source
const float InvLightPickingPdf = (float)SampledLightData.DirectionalLightCount;
const int FakeCell = -1;
const float FakeInvApproximatedNormalizationFactor = 1.f;
for (int i = 0; i < Options.NumDirectionalSamples; i++)
{
float3 RandSample = RandContext.GenerateSample3D();
uint DirectionalLightIndex = uint(RandSample.x * SampledLightData.DirectionalLightCount);
DirectionalLightIndex = min(DirectionalLightIndex, SampledLightData.DirectionalLightCount - 1) + SampledLightData.DirectionalLightStart;
const float2 SampleLocation = RandSample.yz;
const RTXDI_SDK_LightSampleRef SampleRef = RTXDI_SDK_LightSampleRef::Create(DirectionalLightIndex, SampleLocation);
const float LightPickingPdf = (InvLightPickingPdf > 0.f) ? (1.f / InvLightPickingPdf) : 0.f;
UpdateReservoirWithNewProposalSample(Surface, SampleRef,
FakeCell, FakeInvApproximatedNormalizationFactor, InvLightPickingPdf,
Options.NumDirectionalSamples, 0, 0,
RTXDI_SAMPLING_LIGHT, LightPickingPdf,
DirectionalReservoir, RandContext);
}
DirectionalReservoir.FinalizeResampling(1.0, 1.0);
DirectionalReservoir.M = 1;
return DirectionalReservoir;
}
template
const int FakeCell = -1;
const float FakeInvApproximatedNormalizationFactor = 1.f;
for (int i = 0; i < Options.NumSkyLightSamples; i++)
{
// Select a tile of random samples for the thread using the coherent sampler improve coherence of local threads
const uint Tile = uint(CoherentRandContext.GenerateSample1D() * RISSkylightBufferTiles) % RISSkylightBufferTiles;
// This is essentially RIS, for further detail please check [reference 1] at the end of this file.
const uint Sample = uint(RandContext.GenerateSample1D() * RISSkylightBufferTileSize) % RISSkylightBufferTileSize;
const uint2 SampleData = RISSkylightBuffer[Tile * RISSkylightBufferTileSize + Sample];
const uint SkyLightIdx = 0;
const float InvLightTilePickingPdf = asfloat(SampleData.y);
const float2 SampleLocation = float2(SampleData.x & 0xffff, SampleData.x >> 16) * 1.0 / float(0xffff);
const RTXDI_SDK_LightSampleRef SampleRef = RTXDI_SDK_LightSampleRef::Create(SkyLightIdx, SampleLocation);
const float LightPickingPdf = InvLightTilePickingPdf > 0.f ? (1.f / InvLightTilePickingPdf) : 0.f;
UpdateReservoirWithNewProposalSample(Surface, SampleRef,
FakeCell, FakeInvApproximatedNormalizationFactor, InvLightTilePickingPdf,
Options.NumSkyLightSamples, Options.NumBrdfSamples, 0,
RTXDI_SAMPLING_LIGHT, LightPickingPdf,
SkylightReservoir, RandContext);
}
SkylightReservoir.FinalizeResampling(1.0, 1.0);
SkylightReservoir.M = 1;
return SkylightReservoir;
}
RTXDI_SDK_Reservoir ProduceBrdfSample( FGBufferSurface Surface, InitialCandidateOptions Options, inout FRandomContext RandContext, inout FRandomContext CoherentRandContext) { RTXDI_SDK_Reservoir BrdfReservoir = RTXDI_SDK_Reservoir::Empty();
const int ReGIRCell = ReGIRGetCell(Surface.TranslatedWorldPosition);
const float ApproximatedNormalizationFactor = ReGIRNormalizationBuffer[ReGIRCell];
const int ReGIRCell = 0;
const float ApproximatedNormalizationFactor = 1.f;
const float InvApproximatedNormalizationFactor = ApproximatedNormalizationFactor > 0.f ? (1.f / ApproximatedNormalizationFactor) : 0.f;
for (int i = 0; i < Options.NumBrdfSamples; i++)
{
float2 RandSample = RandContext.GenerateSample2D();
const BrdfRaySample BrdfSample = DefaultLit_SampleBrdf(Surface.GetRTXDISurface(), RandSample);
if (BrdfSample.OutPdf <= 0.f)
continue;
FRayDesc Ray = (FRayDesc)0;
Ray.Origin = Surface.TranslatedWorldPosition;
Ray.Direction = BrdfSample.OutDirection;
Ray.TMin = 0.1f;
Ray.TMax = POSITIVE_INFINITY;
float HitDistance = -1.f;
int LightIdx = TraceRayAgainstLights(Ray, HitDistance);
/* int TraceRayAgainstLights(FRayDesc Ray, out float Distance) { FTraceRayInlineContext TraceRayInlineContext = CreateTraceRayInlineContext(); FTraceRayInlineResult TraceResult = TraceRayInline(LightTLAS, RAY_FLAG_FORCE_OPAQUE, 0xff, Ray.GetNativeDesc(), TraceRayInlineContext); if (TraceResult.IsHit()) { Distance = TraceResult.HitT; return TraceResult.InstanceID; }
return RTXDI_INVALID_LIGHT_ID;
} */ float2 SampleLocation = float2(0.f, 0.f);
uint NumHitLightSamples = 0;
uint NumReGIRSamples = 0;
if (LightIdx == RTXDI_INVALID_LIGHT_ID)
{
if(!EnableSkySampling)
continue;
// Convert the 3D world space coordinate to 2D texture space coordinate
SampleLocation = InverseEquiAreaSphericalMapping(BrdfSample.OutDirection.yzx);
NumHitLightSamples = Options.NumSkyLightSamples;
LightIdx = 0;
}
else
{
// Light sample position in world space.
const float3 LightSamplePosition = Ray.Origin + Ray.Direction * HitDistance;
FSampledLightData Light = GetSampledDeferredLightDataWithSkylight(LightIdx, float2(0.f, 0.f), SkylightTexture, SkylightTextureSampler);
SampleLocation = ConstructUVFromWorldPosition(Light.LightData,
Surface.TranslatedWorldPosition,
Surface.GetWorldNormal(),
Light.LightType,
LightSamplePosition);
NumHitLightSamples = Options.NumLocalSamples;
NumReGIRSamples = Options.NumLocalSamples;
}
const RTXDI_SDK_LightSampleRef SampleRef = RTXDI_SDK_LightSampleRef::Create(LightIdx, SampleLocation);
UpdateReservoirWithNewProposalSample(Surface, SampleRef,
ReGIRCell, InvApproximatedNormalizationFactor, 0.f, /* this is not read inside */
NumHitLightSamples, Options.NumBrdfSamples, NumReGIRSamples,
RTXDI_SAMPLING_BRDF, BrdfSample.OutPdf,
BrdfReservoir, RandContext);
}
BrdfReservoir.FinalizeResampling(1.0, 1.f);
BrdfReservoir.M = 1;
return BrdfReservoir;
}
template
for (uint i = 0; i < Options.NumLocalSamples; i++)
{
float3 RandSample = RandContext.GenerateSample3D();
// Uniformly pick a light source with each light having the same properbility of being picked.
const uint LocalLightIdx = uint(RandSample.x * SampledLightData.LocalLightCount) % SampledLightData.LocalLightCount + SampledLightData.LocalLightStart;
const float2 SampleLocation = RandSample.yz;
const RTXDI_SDK_LightSampleRef SampleRef = RTXDI_SDK_LightSampleRef::Create(LocalLightIdx, SampleLocation);
UpdateReservoirWithNewProposalSample(Surface, SampleRef,
Cell, InvApproximatedNormalizationFactor, InvLightPickingPdf,
Options.NumLocalSamples, Options.NumBrdfSamples, NumReGIRSample,
RTXDI_SAMPLING_LIGHT, LightPickingPdf,
LocalReservoir, RandContext);
}
}
template
// valid cell, produce a sample
for (uint i = 0; i < Options.NumLocalSamples; i++)
{
float3 RandSample = RandContext.GenerateSample3D();
const uint Sample = uint(RandSample.x * ReGIRTileSize) % ReGIRTileSize;
const uint4 SampleData = ReGIRBuffer[Cell * ReGIRTileSize + Sample];
float InvLightPickingPdf = 0.f;
if (SampleData.x != 0xffffffff)
{
const uint LocalLightIdx = SampleData.x + SampledLightData.LocalLightStart;
const float ApproximatedLightPickingPdf = asfloat(SampleData.w) * InvApproximatedNormalizationFactor;
// This is essentially RIS, for further detail please check [reference 1] at the end of this file.
const float InvLightPickingPdf = asfloat(SampleData.y);
const float2 SampleLocation = RandSample.yz;
const RTXDI_SDK_LightSampleRef SampleRef = RTXDI_SDK_LightSampleRef::Create(LocalLightIdx, SampleLocation);
UpdateReservoirWithNewProposalSample(Surface, SampleRef,
Cell, InvApproximatedNormalizationFactor, InvLightPickingPdf,
Options.NumLocalSamples, Options.NumBrdfSamples, Options.NumLocalSamples,
RTXDI_SAMPLING_REGIR, ApproximatedLightPickingPdf,
LocalReservoir, RandContext);
}
else
{
// feed a null sample to avoid bias
LocalReservoir.StreamSample(RTXDI_SDK_LightSampleRef::Invalid(), 1.f, 0.f, 0.f);
}
}
}
template
// This is fine since this will not be read
const int FakeCell = -1;
const float FakeInvApproximatedNormalizationFactor = 1.f;
for (uint i = 0; i < Options.NumLocalSamples; i++)
{
float3 RandSample = RandContext.GenerateSample3D();
// This is essentially RIS, for further detail please check [reference 1] at the end of this file.
const uint Sample = uint(RandSample.x * RISBufferTileSize) % RISBufferTileSize;
const uint2 SampleData = RISBuffer[Tile * RISBufferTileSize + Sample];
const uint LocalLightIdx = SampleData.x + SampledLightData.LocalLightStart;
const float InvLightPickingPdf = asfloat(SampleData.y);
const float LightPickingPdf = InvLightPickingPdf > 0.f ? (1.f / InvLightPickingPdf) : 0.f;
const float2 SampleLocation = RandSample.yz;
const RTXDI_SDK_LightSampleRef SampleRef = RTXDI_SDK_LightSampleRef::Create(LocalLightIdx, SampleLocation);
UpdateReservoirWithNewProposalSample(Surface, SampleRef,
FakeCell, FakeInvApproximatedNormalizationFactor, InvLightPickingPdf,
Options.NumLocalSamples, Options.NumBrdfSamples, 0,
RTXDI_SAMPLING_LIGHT, LightPickingPdf,
LocalReservoir, RandContext);
}
}
template
const float RisRand = RandContext.GenerateSample1D();
// Draw a light sample from given light sources
FSampledLightData Light;
FLightSampleLocation LightSample;
GetLightSampleData(Surface, SampleRef, LightSample, Light);
// Conditional PDF of picking a light sample when a specific light is picked.
const float LightSamplePdf = LightSample.Pdf;
// Whether the sample is valid.
const uint LightIdx = SampleRef.GetLightIndex();
bool ValidSample = Surface.GetLightingChannelMask() & GetSampledLightChannelMask(LightIdx);
if (InSamplingMethodPdf <= 0.f)
ValidSample = false;
if (!SamplingBrdf && LightSamplePdf <= 0.f)
ValidSample = false;
// Target PDF of RIS, this is the unshadowed light contribution with cosine term included.
const float TargetPdf = ValidSample ? GetApproximateLightSampleWeightWithLight(Surface, LightSample, Light) : 0.f;
// Multiple importance sampling weight for initial samples
const float MisWeight = GetLightMISWeight(Surface, Light, SampleRef, LightSample, LightIdx,
ReGIRCell, InvApproximatedNormalizationFactor, NumLightSamples, NumBrdfSamples, NumReGIRSamples, LightSamplePdf,
SamplingMethod, InSamplingMethodPdf, RandContext);
// Proposal PDF has two parts
// - The discrete PDF of picking a light source
// - The conditional (continous) PDF of picking a sample on the picked light source
const float InvProposalPdf = ValidSample ? (SamplingBrdf ? (1.f / InSamplingMethodPdf) : (RealInvLightPickingPdf / LightSamplePdf)) : 0.f;
// MIS weight is counted as part of the RIS weight here
const float RisWeight = MisWeight * TargetPdf * InvProposalPdf;
// WRS algorithm execution
Reservoir.StreamSample(SampleRef, RisRand, TargetPdf, RisWeight);
}
#### DirectLightSamplingCommon
- シェーダー(DirectLightSamplingCommon.ush)
```C++
// ReGIR specific data
float ReGIRCellSize;
int3 ReGIRCellCount;
int ReGIRTileSize;
float ReGIRLevels;
float ReGIRComputeLevel(float3 TranslatedWorldPosition)
{
const float3 GridCoord = (TranslatedWorldPosition - View.TranslatedWorldCameraOrigin);
const float3 CellCoord = GridCoord / ReGIRCellSize;
const float3 TileLevel = ceil(log2(max(1.0, abs(CellCoord))) - log2(ReGIRCellCount * 0.5));
const float Level = max(0.0, max(TileLevel.x, max(TileLevel.y, TileLevel.z)));
return min(Level, ReGIRLevels - 1.0);;
}
int ReGIRGetCell(float3 TranslatedWorldPosition)
{
const float3 GridCoord = (TranslatedWorldPosition - View.TranslatedWorldCameraOrigin);
const float Level = ReGIRComputeLevel(TranslatedWorldPosition);
const float3 TileCoord = GridCoord / (ReGIRCellSize * exp2(Level)) + ReGIRCellCount * 0.5f;
if (Level >= ReGIRLevels || any(TileCoord < 0.0) || any(TileCoord > ReGIRCellCount))
return -1;
const int3 IntTileCoord = TileCoord;
return Level * (ReGIRCellCount.x * ReGIRCellCount.y * ReGIRCellCount.z) + IntTileCoord.x + IntTileCoord.y * (ReGIRCellCount.x) + IntTileCoord.z * (ReGIRCellCount.x * ReGIRCellCount.y);
}
BrdfRaySample DefaultLit_SampleBrdf(in const FRTXDISurface Surface, in float2 RandSample)
{
const FGBufferData GBuffer = Surface.GBuffer;
const float3 N = GBuffer.WorldNormal;
const float3 V = -Surface.ViewDirection;
BrdfRaySample Sample = (BrdfRaySample)0;
float2 Alpha;
float3x3 Basis = GetGGXBasis(GBuffer.Roughness, GBuffer.Anisotropy, GBuffer.WorldNormal, GBuffer.WorldTangent, Alpha);
const float3 LV = mul(Basis, V);
const float NoV = saturate(LV.z);
const FBxDFEnergyTerms Spec = ComputeGGXSpecEnergyTerms(GBuffer.Roughness, NoV, GBuffer.SpecularColor);
// Probability of picking diffuse lobe vs. specular lobe
const float3 Diffuse = GBuffer.DiffuseColor * (1 - Spec.E);
const float3 Specular = Spec.E;
const float SumDiffuse = Diffuse.x + Diffuse.y + Diffuse.z;
const float SumSpecular = Specular.x + Specular.y + Specular.z;
const float DiffuseRatio = SumDiffuse / (SumDiffuse + SumSpecular);
#if SUPPORTS_ANISOTROPIC_MATERIALS
bool bHasAnisotropy = HasAnisotropy(GBuffer.SelectiveOutputMask);
#else
bool bHasAnisotropy = false;
#endif
// Randomly choose to sample diffuse or specular
float3 L = 0, H = 0;
if (RandSample.x < DiffuseRatio)
{
RandSample.x /= DiffuseRatio;
// Lambert
// TODO: evaluate CosineSampleHemisphereConcentric
float4 Result = CosineSampleHemisphere(RandSample.xy);
L = Result.xyz;
H = normalize(L + LV);
}
else
{
RandSample.x -= DiffuseRatio;
RandSample.x /= (1.0 - DiffuseRatio);
H = ImportanceSampleVisibleGGX(RandSample.xy, Alpha, LV).xyz;
L = reflect(-LV, H);
// invalid output direction, exit early
if (L.z <= 0)
return Sample;
}
L = normalize(mul(L, Basis));
Sample.OutDirection = L;
Sample.OutPdf = EvaluateBrdfPdfInternal(N, L, V, GBuffer.WorldTangent, Alpha, bHasAnisotropy, DiffuseRatio);
return Sample;
}
FSampledLightData GetSampledDeferredLightDataPrimitiveLight(int LightIndex)
{
FSampledLightData L = (FSampledLightData)0;
FPackedSampledLightingData RayTracingLightData = GetSampledLightData(LightIndex);
const uint LightType = RayTracingLightData.Type;
L.LightData.TranslatedWorldPosition = RayTracingLightData.LightPosition; // LWC ToDo
L.LightData.InvRadius = RayTracingLightData.InvRadius;
L.LightData.Color = RayTracingLightData.LightColor;
L.LightData.FalloffExponent = RayTracingLightData.FalloffExponent;
L.LightData.Direction = RayTracingLightData.Direction;
L.LightData.Tangent = RayTracingLightData.Tangent;
L.LightData.SpotAngles = RayTracingLightData.SpotAngles;
L.LightData.SourceRadius = RayTracingLightData.SourceRadius;
L.LightData.SourceLength = RayTracingLightData.SourceLength;
L.LightData.SoftSourceRadius = RayTracingLightData.SoftSourceRadius;
L.LightData.SpecularScale = RayTracingLightData.SpecularScale;
L.LightData.RectLightData.BarnCosAngle = RayTracingLightData.RectLightBarnCosAngle;
L.LightData.RectLightData.BarnLength = RayTracingLightData.RectLightBarnLength;
L.LightData.DistanceFadeMAD = RayTracingLightData.DistanceFadeMAD;
L.LightData.ShadowMapChannelMask = float4(0, 0, 0, 0);
L.LightData.ShadowedBits = 0; // Not lit dynamic shadows
L.LightData.ContactShadowLength = 0.0;
L.LightData.ContactShadowLengthInWS = false;
L.LightData.ContactShadowNonCastingIntensity = 0.0f;
L.LightData.bRadialLight = (LightType != LIGHT_TYPE_DIRECTIONAL);
L.LightData.bSpotLight = (LightType == LIGHT_TYPE_SPOT);
L.LightData.bRectLight = (LightType == LIGHT_TYPE_RECT);
L.LightData.HairTransmittance = InitHairTransmittanceData();
if (LightType == LIGHT_TYPE_DIRECTIONAL)
{
L.LightData.bInverseSquared = false;
}
else
{
L.LightData.bInverseSquared = L.LightData.FalloffExponent == 0;
}
L.IESAtlasIndex = RayTracingLightData.LightProfileIndex;
L.LightType = LightType;
L.AtmosphereCloudShadowIndexAndFlags = RayTracingLightData.AtmosphereCloudShadowIndexAndFlags;
L.LightData.RectLightData.AtlasData.AtlasMaxLevel = RayTracingLightData.RectLightAtlasMaxLevel;
L.LightData.RectLightData.AtlasData.AtlasUVOffset.x = f16tof32(RayTracingLightData.RectLightAtlasUVOffset & 0xffff);
L.LightData.RectLightData.AtlasData.AtlasUVOffset.y = f16tof32((RayTracingLightData.RectLightAtlasUVOffset >> 16) & 0xffff);
L.LightData.RectLightData.AtlasData.AtlasUVScale.x = f16tof32(RayTracingLightData.RectLightAtlasUVScale & 0xffff);
L.LightData.RectLightData.AtlasData.AtlasUVScale.y = f16tof32((RayTracingLightData.RectLightAtlasUVScale >> 16) & 0xffff);
L.LightFunctionIndex = RayTracingLightData.FlagsLightFunctionAndMask & 0xff;
L.LightID = RayTracingLightData.LightID;
L.ColorToRadianceScale = 1.0f;
if (LightType == LIGHT_TYPE_POINT || LightType == LIGHT_TYPE_SPOT)
{
float Area = 1.f;
if (L.LightData.SourceLength > 0.f)
{
float Radius = L.LightData.SourceRadius;
float Radius2 = Radius * Radius;
float SourceLength = L.LightData.SourceLength;
// the caps are two halves of a full sphere
// the body is a cylinder
// the common factor of 4*PI is accounted for at the end
float CapsArea = Radius2;
float BodyArea = 0.5 * Radius * SourceLength;
Area = PI * (CapsArea + BodyArea);
}
else
{
Area = (L.LightData.SourceRadius < POINT_LIGHT_RADIUS_LIMIT) ? 1.f : (PI * Pow2(L.LightData.SourceRadius));
}
L.ColorToRadianceScale = 1.f / Area;
}
L.IsSkyLight = false;
return L;
}
FSampledLightData GetSampledDeferredLightDataWithSkylight(int LightIndex, float2 LightUV, Texture2D<float4> SkylightTexture,SamplerState SkylightSampler)
{
if (LightIndex >= SampledLightData.DirectionalLightStart)
{
return GetSampledDeferredLightDataPrimitiveLight(LightIndex);
}
FSampledLightData L = (FSampledLightData)0;
L.LightData.TranslatedWorldPosition = 0.0f;
L.LightData.InvRadius = 0.0f;
L.LightData.Color = Texture2DSample(SkylightTexture, SkylightSampler, LightUV).xyz * SampledLightData.SkylightColor;
L.LightData.FalloffExponent = 0.0f;
L.LightData.Direction = normalize(EquiAreaSphericalMapping(LightUV).zxy);
L.LightData.Tangent = 0;
L.LightData.SpotAngles = 0;
L.LightData.SourceRadius = 0;
L.LightData.SourceLength = 0;
L.LightData.SoftSourceRadius =0;
L.LightData.SpecularScale = 0.0; // skylight contributions for specular are handled with reflections
L.LightData.RectLightData.BarnCosAngle = 0;
L.LightData.RectLightData.BarnLength = 0;
L.LightData.DistanceFadeMAD = 0;
L.LightData.ShadowMapChannelMask = float4(0, 0, 0, 0);
L.LightData.ShadowedBits = 0; // Not lit dynamic shadows
L.LightData.ContactShadowLength = 0.0;
L.LightData.ContactShadowLengthInWS = false;
L.LightData.ContactShadowNonCastingIntensity = 0.0f;
L.LightData.bRadialLight = false;
L.LightData.bSpotLight = false;
L.LightData.bRectLight = false;
L.LightData.bInverseSquared = false;
L.LightData.HairTransmittance = InitHairTransmittanceData();
L.IESAtlasIndex = -1.0;
L.LightData.RectLightData.AtlasData.AtlasMaxLevel = MAX_RECT_ATLAS_MIP;
L.LightData.RectLightData.AtlasData.AtlasUVOffset = float2(0, 0);
L.LightData.RectLightData.AtlasData.AtlasUVScale = float2(0, 0);
L.LightType = LIGHT_TYPE_DIRECTIONAL;
L.AtmosphereCloudShadowIndexAndFlags = 0;
L.LightFunctionIndex = 0;
L.LightID = 0;
L.ColorToRadianceScale = 1.0f;
L.IsSkyLight = true;
return L;
}
シェーダー(ReservoirManagement.ush)
// This structure represents a light sample without storing any actual light information.
// Therefore, it's called a light sample reference.
// It stores the global light index, a validity flag, and a unorm2 parameter that can be used
// to reconstruct the position on the light.
struct RTXDI_SDK_LightSampleRef
{
static const uint c_ValidBit = 0x80000000;
static const uint c_IndexMask = 0x7FFFFFFF;
// Internal compressed light sample data
uint lightData;
uint uvData;
:
static RTXDI_SDK_LightSampleRef Create(uint lightIndex, float2 uv)
{
RTXDI_SDK_LightSampleRef s;
s.lightData = lightIndex | c_ValidBit;
s.uvData = uint(saturate(uv.x) * 0xffff) | (uint(saturate(uv.y) * 0xffff) << 16);
return s;
}
};
// Code for managing reads/writes to reservoirs
RWStructuredBuffer
RWStructuredBuffer
uint ComputeReservoirAddress(int2 PixelCoord, int Slice, uint3 BufferDim) { static const uint TileSize = 4; const uint RowStride = (BufferDim.x / TileSize) TileSize TileSize;
int2 Tile = PixelCoord / TileSize;
int2 TileCoord = PixelCoord % TileSize;
uint Address = Slice * BufferDim.x * BufferDim.y;
Address += Tile.y * RowStride;
Address += Tile.x * TileSize * TileSize;
Address += TileCoord.y * TileSize + TileCoord.x;
return Address;
}
uint4 ReadReservoirData(int2 PixelCoord, int Slice) { uint Address = ComputeReservoirAddress(PixelCoord, Slice, ReservoirBufferDim);
return RWLightReservoirUAV[Address];
}
void WriteReservoirData(int2 PixelCoord, int Slice, uint4 Value) { uint Address = ComputeReservoirAddress(PixelCoord, Slice, ReservoirBufferDim);
RWLightReservoirUAV[Address] = Value;
}
uint4 ReadReservoirHistoryData(int2 PixelCoord, int Slice) {
uint Address = ComputeReservoirAddress(PixelCoord, Slice, ReservoirHistoryBufferDim);
return LightReservoirHistory[Address];
}
void WriteReservoirHistoryData(int2 PixelCoord, int Slice, uint4 Value) {
uint Address = ComputeReservoirAddress(PixelCoord, Slice, ReservoirHistoryBufferDim);
RWLightReservoirHistoryUAV[Address] = Value;
}
// This structure represents a single light reservoir that stores the weights, the sample ref, // and sample count (M). It can be stored into uint4 and loaded from uint4, except the weightSum field. struct RTXDI_SDK_Reservoir { static const uint MaxM = 0xffff;
RTXDI_SDK_LightSampleRef sampleRef;
float weightSum;
float targetPdf;
float M;
// Cannonical weight when using pairwise MIS (ignored except during pairwise MIS computations)
float canonicalWeight;
static RTXDI_SDK_Reservoir Empty()
{
RTXDI_SDK_Reservoir s;
s.sampleRef = RTXDI_SDK_LightSampleRef::Invalid();
s.targetPdf = 0;
s.weightSum = 0;
s.M = 0;
s.canonicalWeight = 0;
return s;
}
static RTXDI_SDK_Reservoir Load(RTXDI_SDK_PackedReservoir data)
{
RTXDI_SDK_Reservoir res;
res.sampleRef.lightData = data.x;
res.sampleRef.uvData = data.y;
res.weightSum = f16tof32(data.z);
res.targetPdf = f16tof32(data.z >> 16);
res.M = data.w;
const uint M = (data.w & 0xff) | ((data.z & 0xff) << 8);
res.M = M;
// unsigned 24 bit floats
uint weightSum = (data.z & 0xffffff00);
uint targetPdf = (data.w & 0xffffff00);
res.weightSum = asfloat(weightSum >> 1);
res.targetPdf = asfloat(targetPdf >> 1);
res.canonicalWeight = 0.0f;
// Discard reservoirs that have Inf/NaN
if (isinf(res.weightSum) || isnan(res.weightSum)) {
res = Empty();
}
return res;
}
RTXDI_SDK_PackedReservoir Store()
{
RTXDI_SDK_PackedReservoir data;
data.x = sampleRef.lightData;
data.y = sampleRef.uvData;
data.w = M;
data.z = f32tof16(weightSum) | (f32tof16(targetPdf) << 16);
const float RoundConst = 1.0f / float(1 << 18); // 1/(2^18) is the first bit unrepresented by the custom float)
float RoundedWeight = weightSum + weightSum * RoundConst;
float RoundedPdf = targetPdf + targetPdf * RoundConst;
//remove sign as values are guaranteed positive
uint uWeightSum = asuint(max(0.0, RoundedWeight)) << 1;
uint uTargetPdf = asuint(max(0.0, RoundedPdf)) << 1;
//chop to 24 bits
uWeightSum = uWeightSum & 0xffffff00;
uTargetPdf = uTargetPdf & 0xffffff00;
//clamp M
M = (uint)min((uint)M, MaxM);
// pack M to bottom bits
data.z = (uWeightSum) | (((uint)M >> 8) & 0xff);
data.w = (uTargetPdf) | ((uint)M & 0xff);
return data;
}
void FinalizeResampling(float normalizationNumerator, float normalizationDenominator)
{
float denominator = targetPdf * normalizationDenominator;
weightSum = (denominator == 0.0) ? 0.0 : (weightSum * normalizationNumerator) / denominator;
}
// Adds a new, non-reservoir light sample into this reservoir.
// Algorithm (3) from the ReSTIR paper, Streaming RIS using weighted reservoir sampling.
bool StreamSample(RTXDI_SDK_LightSampleRef NewSampleRef, float Random, float NewTargetPdf, float RisWeight)
{
// Add one sample to the counter
M += 1;
// Update the weight sum
weightSum += RisWeight;
// Decide if we will randomly pick this sample
bool selectSample = (Random * weightSum < RisWeight);
// If we did select this sample, update the relevant data.
// New samples don't have visibility or age information, we can skip that.
if (selectSample)
{
sampleRef = NewSampleRef;
targetPdf = NewTargetPdf;
}
return selectSample;
}
// Adds `newReservoir` into `reservoir`, returns true if the new reservoir's sample was selected.
// This is a very general form, allowing input parameters to specfiy normalization and targetPdf
// rather than computing them from `newReservoir`. Named "internal" since these parameters take
// different meanings (e.g., in RTXDI_CombineReservoirs() or RTXDI_StreamNeighborWithPairwiseMIS())
bool InternalSimpleResample(
const RTXDI_SDK_Reservoir newReservoir,
float random,
float newTargetPdf, // Usually closely related to the sample normalization,
float sampleNormalization, // typically off by some multiplicative factor
float sampleM // In its most basic form, should be newReservoir.M
)
{
// What's the current weight (times any prior-step RIS normalization factor)
float risWeight = newTargetPdf * sampleNormalization;
// Our *effective* candidate pool is the sum of our candidates plus those of our neighbors
M += sampleM;
// Update the weight sum
weightSum += risWeight;
// Decide if we will randomly pick this sample
bool selectSample = (random * weightSum < risWeight);
// If we did select this sample, update the relevant data
if (selectSample)
{
sampleRef = newReservoir.sampleRef;
targetPdf = newTargetPdf;
}
return selectSample;
}
// Adds a reservoir with one sample into this reservoir.
// Algorithm (4) from the ReSTIR paper, Combining the streams of multiple reservoirs.
// Normalization - Equation (6) - is postponed until all reservoirs are combined.
bool CombineReservoirs(RTXDI_SDK_Reservoir newReservoir, float random, float newTargetPdf)
{
return InternalSimpleResample(
newReservoir,
random,
newTargetPdf,
newReservoir.weightSum * newReservoir.M,
newReservoir.M
);
}
};
https://developer.nvidia.com/game-engines/unreal-engine/rtx-branch