// Copyright Epic Games, Inc. All Rights Reserved. #include "MegaLights.h" #include "MegaLightsInternal.h" #include "RendererPrivate.h" #include "Quantization.h" static TAutoConsoleVariable CVarMegaLightsTemporal( TEXT("r.MegaLights.Temporal"), true, TEXT("Whether to use temporal accumulation for shadow mask."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarMegaLightsTemporalMinFramesAccumulatedForHistoryMiss( TEXT("r.MegaLights.Temporal.MinFramesAccumulatedForHistoryMiss"), 4, TEXT("Minimal amount of history length when reducing history length due to a history miss. Higher values than 1 soften and slowdown transitions."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarMegaLightsTemporalMinFramesAccumulatedForHighConfidence( TEXT("r.MegaLights.Temporal.MinFramesAccumulatedForHighConfidence"), 2, TEXT("Minimal amount of history length when reducing history length due to a high confidence. Higher values than 1 soften image, but reduce noise in high confidence areas."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarMegaLightsTemporalMaxFramesAccumulated( TEXT("r.MegaLights.Temporal.MaxFramesAccumulated"), 12, TEXT("Max history length when accumulating frames. Lower values have less ghosting, but more noise."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarMegaLightsTemporalNeighborhoodClampScale( TEXT("r.MegaLights.Temporal.NeighborhoodClampScale"), 1.0f, TEXT("Scales how permissive is neighborhood clamp. Higher values increase ghosting, but reduce noise and instability."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarMegaLightsSpatial( TEXT("r.MegaLights.Spatial"), true, TEXT("Whether denoiser should run spatial filter."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarMegaLightsSpatialDepthWeightScale( TEXT("r.MegaLights.Spatial.DepthWeightScale"), 10000.0f, TEXT("Scales the depth weight of the spatial filter. Smaller values allow for more sample reuse, but also introduce more bluriness between unrelated surfaces."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarMegaLightsSpatialKernelRadius( TEXT("r.MegaLights.Spatial.KernelRadius"), 8.0f, TEXT("Spatial filter kernel radius in pixels"), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarMegaLightsSpatialNumSamples( TEXT("r.MegaLights.Spatial.NumSamples"), 4, TEXT("Number of spatial filter samples."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarMegaLightsSpatialMaxDisocclusionFrames( TEXT("r.MegaLights.Spatial.MaxDisocclusionFrames"), 3, TEXT("Number of of history frames to boost spatial filtering in order to minimize noise after disocclusion."), ECVF_Scalability | ECVF_RenderThreadSafe ); namespace MegaLights { bool UseSpatialFilter() { return CVarMegaLightsSpatial.GetValueOnRenderThread(); } bool UseTemporalFilter() { return CVarMegaLightsTemporal.GetValueOnRenderThread(); } float GetTemporalMaxFramesAccumulated() { return FMath::Max(CVarMegaLightsTemporalMaxFramesAccumulated.GetValueOnRenderThread(), 1.0f); } float GetSpatialFilterMaxDisocclusionFrames() { return FMath::Max(FMath::Min(CVarMegaLightsSpatialMaxDisocclusionFrames.GetValueOnRenderThread(), GetTemporalMaxFramesAccumulated() - 1.0f), 0.0f); } } class FDenoiserTemporalCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FDenoiserTemporalCS) SHADER_USE_PARAMETER_STRUCT(FDenoiserTemporalCS, FGlobalShader) BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FMegaLightsParameters, MegaLightsParameters) SHADER_PARAMETER_STRUCT_INCLUDE(StochasticLighting::FHistoryScreenParameters, HistoryScreenParameters) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ResolvedDiffuseLighting) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ResolvedSpecularLighting) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ShadingConfidenceTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, DiffuseLightingHistoryTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SpecularLightingHistoryTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, LightingMomentsHistoryTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, NumFramesAccumulatedHistoryTexture) SHADER_PARAMETER(FVector3f, TargetFormatQuantizationError) SHADER_PARAMETER(float, PrevSceneColorPreExposureCorrection) SHADER_PARAMETER(float, TemporalMaxFramesAccumulated) SHADER_PARAMETER(float, TemporalNeighborhoodClampScale) SHADER_PARAMETER(float, MinFramesAccumulatedForHistoryMiss) SHADER_PARAMETER(float, MinFramesAccumulatedForHighConfidence) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWDiffuseLighting) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWSpecularLighting) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWLightingMoments) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWNumFramesAccumulated) END_SHADER_PARAMETER_STRUCT() class FDownsampleFactorX : SHADER_PERMUTATION_RANGE_INT("DOWNSAMPLE_FACTOR_X", 1, 2); class FDownsampleFactorY : SHADER_PERMUTATION_RANGE_INT("DOWNSAMPLE_FACTOR_Y", 1, 2); class FValidHistory : SHADER_PERMUTATION_BOOL("VALID_HISTORY"); class FDebugMode : SHADER_PERMUTATION_BOOL("DEBUG_MODE"); using FPermutationDomain = TShaderPermutationDomain; static int32 GetGroupSize() { return 8; } static FPermutationDomain RemapPermutation(FPermutationDomain PermutationVector) { if (PermutationVector.Get() == 2) { PermutationVector.Set(2); } return PermutationVector; } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { FPermutationDomain PermutationVector(Parameters.PermutationId); if (RemapPermutation(PermutationVector) != PermutationVector) { return false; } return MegaLights::ShouldCompileShaders(Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize()); if (FDataDrivenShaderPlatformInfo::GetSupportsRealTypes(Parameters.Platform) == ERHIFeatureSupport::RuntimeGuaranteed) { OutEnvironment.CompilerFlags.Add(CFLAG_AllowRealTypes); } } static EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FGlobalShaderPermutationParameters& Parameters) { FPermutationDomain PermutationVector(Parameters.PermutationId); if (PermutationVector.Get()) { return EShaderPermutationPrecacheRequest::NotPrecached; } return FGlobalShader::ShouldPrecachePermutation(Parameters); } }; IMPLEMENT_GLOBAL_SHADER(FDenoiserTemporalCS, "/Engine/Private/MegaLights/MegaLightsDenoiserTemporal.usf", "DenoiserTemporalCS", SF_Compute); class FDenoiserSpatialCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FDenoiserSpatialCS) SHADER_USE_PARAMETER_STRUCT(FDenoiserSpatialCS, FGlobalShader) BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FMegaLightsParameters, MegaLightsParameters) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWSceneColor) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, DiffuseLightingTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SpecularLightingTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, LightingMomentsTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ShadingConfidenceTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, NumFramesAccumulatedTexture) SHADER_PARAMETER(float, TemporalMaxFramesAccumulated) SHADER_PARAMETER(float, SpatialFilterDepthWeightScale) SHADER_PARAMETER(float, SpatialFilterKernelRadius) SHADER_PARAMETER(uint32, SpatialFilterNumSamples) SHADER_PARAMETER(float, SpatialFilterMaxDisocclusionFrames) SHADER_PARAMETER(uint32, bSubPixelShading) END_SHADER_PARAMETER_STRUCT() class FSpatialFilter : SHADER_PERMUTATION_BOOL("SPATIAL_FILTER"); class FInputType : SHADER_PERMUTATION_INT("INPUT_TYPE", int32(EMegaLightsInput::Count)); class FDebugMode : SHADER_PERMUTATION_BOOL("DEBUG_MODE"); using FPermutationDomain = TShaderPermutationDomain; static int32 GetGroupSize() { return 8; } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { FPermutationDomain PermutationVector(Parameters.PermutationId); EMegaLightsInput InputType = EMegaLightsInput(PermutationVector.Get()); if (PermutationVector.Get() && !MegaLights::SupportsSpatialFilter(InputType)) { return false; } return MegaLights::ShouldCompileShaders(Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize()); } static EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FGlobalShaderPermutationParameters& Parameters) { FPermutationDomain PermutationVector(Parameters.PermutationId); if (PermutationVector.Get()) { return EShaderPermutationPrecacheRequest::NotPrecached; } return FGlobalShader::ShouldPrecachePermutation(Parameters); } }; IMPLEMENT_GLOBAL_SHADER(FDenoiserSpatialCS, "/Engine/Private/MegaLights/MegaLightsDenoiserSpatial.usf", "DenoiserSpatialCS", SF_Compute); class FMegaLightsDebugCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FMegaLightsDebugCS) SHADER_USE_PARAMETER_STRUCT(FMegaLightsDebugCS, FGlobalShader) BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) RDG_BUFFER_ACCESS(IndirectArgs, ERHIAccess::IndirectArgs) SHADER_PARAMETER_STRUCT_INCLUDE(FMegaLightsParameters, MegaLightsParameters) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, TileAllocator) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, TileData) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, DownsampledTileAllocator) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, DownsampledTileData) SHADER_PARAMETER(uint32, DebugTileClassificationMode) END_SHADER_PARAMETER_STRUCT() static int32 GetGroupSize() { return 64; } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return MegaLights::ShouldCompileShaders(Parameters.Platform); } static EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FGlobalShaderPermutationParameters& Parameters) { return EShaderPermutationPrecacheRequest::NotPrecached; } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); MegaLights::ModifyCompilationEnvironment(Parameters.Platform, OutEnvironment); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize()); } }; IMPLEMENT_GLOBAL_SHADER(FMegaLightsDebugCS, "/Engine/Private/MegaLights/MegaLightsDebug.usf", "MegaLightsDebugCS", SF_Compute); void FMegaLightsViewContext::DenoiseLighting(FRDGTextureRef OutputColorTarget) { // Demodulated lighting components with second luminance moments stored in alpha channel for temporal variance tracking // This will be passed to the next frame const EPixelFormat LightingDataFormat = MegaLights::GetLightingDataFormat(); FRDGTextureRef DiffuseLighting = nullptr; FRDGTextureRef SpecularLighting = nullptr; FRDGTextureRef LightingMoments = nullptr; DiffuseLighting = GraphBuilder.CreateTexture( FRDGTextureDesc::Create2D(View.GetSceneTexturesConfig().Extent, LightingDataFormat, FClearValueBinding::Black, TexCreate_ShaderResource | TexCreate_UAV), TEXT("MegaLights.DiffuseLighting")); SpecularLighting = GraphBuilder.CreateTexture( FRDGTextureDesc::Create2D(View.GetSceneTexturesConfig().Extent, LightingDataFormat, FClearValueBinding::Black, TexCreate_ShaderResource | TexCreate_UAV), TEXT("MegaLights.SpecularLighting")); LightingMoments = GraphBuilder.CreateTexture( FRDGTextureDesc::Create2D(View.GetSceneTexturesConfig().Extent, PF_FloatRGBA, FClearValueBinding::Black, TexCreate_ShaderResource | TexCreate_UAV), TEXT("MegaLights.LightingMoments")); FRDGTextureRef NumFramesAccumulated = GraphBuilder.CreateTexture( FRDGTextureDesc::Create2D(View.GetSceneTexturesConfig().Extent, PF_G8, FClearValueBinding::Black, TexCreate_ShaderResource | TexCreate_UAV), TEXT("MegaLights.NumFramesAccumulated")); // Temporal accumulation { const bool bValidHistory = DiffuseLightingHistory && SceneDepthHistory && SceneNormalAndShadingHistory && bTemporal; FDenoiserTemporalCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->MegaLightsParameters = MegaLightsParameters; PassParameters->HistoryScreenParameters = HistoryScreenParameters; PassParameters->ResolvedDiffuseLighting = ResolvedDiffuseLighting; PassParameters->ResolvedSpecularLighting = ResolvedSpecularLighting; PassParameters->ShadingConfidenceTexture = ShadingConfidence; PassParameters->DiffuseLightingHistoryTexture = DiffuseLightingHistory; PassParameters->SpecularLightingHistoryTexture = SpecularLightingHistory; PassParameters->LightingMomentsHistoryTexture = LightingMomentsHistory; PassParameters->NumFramesAccumulatedHistoryTexture = NumFramesAccumulatedHistory; PassParameters->TargetFormatQuantizationError = ComputePixelFormatQuantizationError(LightingDataFormat); PassParameters->PrevSceneColorPreExposureCorrection = View.PreExposure / View.PrevViewInfo.SceneColorPreExposure; PassParameters->TemporalMaxFramesAccumulated = MegaLights::GetTemporalMaxFramesAccumulated(); PassParameters->TemporalNeighborhoodClampScale = CVarMegaLightsTemporalNeighborhoodClampScale.GetValueOnRenderThread(); PassParameters->MinFramesAccumulatedForHistoryMiss = FMath::Clamp(CVarMegaLightsTemporalMinFramesAccumulatedForHistoryMiss.GetValueOnRenderThread(), 1.0f, MegaLights::GetTemporalMaxFramesAccumulated()); PassParameters->MinFramesAccumulatedForHighConfidence = FMath::Clamp(CVarMegaLightsTemporalMinFramesAccumulatedForHighConfidence.GetValueOnRenderThread(), 1.0f, MegaLights::GetTemporalMaxFramesAccumulated()); PassParameters->RWDiffuseLighting = GraphBuilder.CreateUAV(DiffuseLighting); PassParameters->RWSpecularLighting = GraphBuilder.CreateUAV(SpecularLighting); PassParameters->RWLightingMoments = GraphBuilder.CreateUAV(LightingMoments); PassParameters->RWNumFramesAccumulated = GraphBuilder.CreateUAV(NumFramesAccumulated); FDenoiserTemporalCS::FPermutationDomain PermutationVector; PermutationVector.Set(DownsampleFactor.X); PermutationVector.Set(DownsampleFactor.Y); PermutationVector.Set(bValidHistory); PermutationVector.Set(bDebug); PermutationVector = FDenoiserTemporalCS::RemapPermutation(PermutationVector); auto ComputeShader = View.ShaderMap->GetShader(PermutationVector); const FIntVector GroupCount = FComputeShaderUtils::GetGroupCount(View.ViewRect.Size(), FDenoiserTemporalCS::GetGroupSize()); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("TemporalAccumulation"), ComputeShader, PassParameters, GroupCount); } // Spatial filter { FDenoiserSpatialCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->MegaLightsParameters = MegaLightsParameters; PassParameters->RWSceneColor = GraphBuilder.CreateUAV(OutputColorTarget); PassParameters->DiffuseLightingTexture = DiffuseLighting; PassParameters->SpecularLightingTexture = SpecularLighting; PassParameters->LightingMomentsTexture = LightingMoments; PassParameters->ShadingConfidenceTexture = ShadingConfidence; PassParameters->NumFramesAccumulatedTexture = NumFramesAccumulated; PassParameters->TemporalMaxFramesAccumulated = MegaLights::GetTemporalMaxFramesAccumulated(); PassParameters->SpatialFilterDepthWeightScale = CVarMegaLightsSpatialDepthWeightScale.GetValueOnRenderThread(); PassParameters->SpatialFilterKernelRadius = CVarMegaLightsSpatialKernelRadius.GetValueOnRenderThread(); PassParameters->SpatialFilterNumSamples = FMath::Clamp(CVarMegaLightsSpatialNumSamples.GetValueOnRenderThread(), 0, 1024); PassParameters->SpatialFilterMaxDisocclusionFrames = MegaLights::GetSpatialFilterMaxDisocclusionFrames(); PassParameters->bSubPixelShading = bSubPixelShading ? 1u : 0u; FDenoiserSpatialCS::FPermutationDomain PermutationVector; PermutationVector.Set(bSpatial); PermutationVector.Set(uint32(InputType)); PermutationVector.Set(bDebug); auto ComputeShader = View.ShaderMap->GetShader(PermutationVector); const FIntVector GroupCount = FComputeShaderUtils::GetGroupCount(View.ViewRect.Size(), FDenoiserSpatialCS::GetGroupSize()); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("Spatial"), ComputeShader, PassParameters, GroupCount); } // Debug pass if (DebugTileClassificationMode != 0 && ((DebugTileClassificationMode - 1) / 2) == (uint32)InputType) { FMegaLightsDebugCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->IndirectArgs = DownsampledTileIndirectArgs; PassParameters->MegaLightsParameters = MegaLightsParameters; PassParameters->TileAllocator = GraphBuilder.CreateSRV(TileAllocator); PassParameters->TileData = GraphBuilder.CreateSRV(TileData); PassParameters->DownsampledTileAllocator = GraphBuilder.CreateSRV(DownsampledTileAllocator); PassParameters->DownsampledTileData = GraphBuilder.CreateSRV(DownsampledTileData); PassParameters->DebugTileClassificationMode = DebugTileClassificationMode; auto ComputeShader = View.ShaderMap->GetShader(); const FIntVector GroupCount = FComputeShaderUtils::GetGroupCount(ViewSizeInTiles.X * ViewSizeInTiles.Y, FMegaLightsDebugCS::GetGroupSize()); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("Debug"), ComputeShader, PassParameters, GroupCount); } if (View.ViewState && !View.bStatePrevViewInfoIsReadOnly) { FMegaLightsViewState::FResources& MegaLightsViewState = InputType == EMegaLightsInput::HairStrands ? View.ViewState->MegaLights.HairStrands : View.ViewState->MegaLights.GBuffer; MegaLightsViewState.HistoryVisibleLightHashViewMinInTiles = VisibleLightHashViewMinInTiles; MegaLightsViewState.HistoryVisibleLightHashViewSizeInTiles = VisibleLightHashViewSizeInTiles; MegaLightsViewState.HistoryVolumeVisibleLightHashViewSizeInTiles = VolumeVisibleLightHashViewSizeInTiles; MegaLightsViewState.HistoryTranslucencyVolumeVisibleLightHashSizeInTiles = TranslucencyVolumeVisibleLightHashSizeInTiles; if (DiffuseLighting && SpecularLighting && LightingMoments && NumFramesAccumulated && bTemporal) { GraphBuilder.QueueTextureExtraction(DiffuseLighting, &MegaLightsViewState.DiffuseLightingHistory); GraphBuilder.QueueTextureExtraction(SpecularLighting, &MegaLightsViewState.SpecularLightingHistory); GraphBuilder.QueueTextureExtraction(LightingMoments, &MegaLightsViewState.LightingMomentsHistory); GraphBuilder.QueueTextureExtraction(NumFramesAccumulated, &MegaLightsViewState.NumFramesAccumulatedHistory); } else { MegaLightsViewState.DiffuseLightingHistory = nullptr; MegaLightsViewState.SpecularLightingHistory = nullptr; MegaLightsViewState.LightingMomentsHistory = nullptr; MegaLightsViewState.NumFramesAccumulatedHistory = nullptr; } if (bGuideByHistory) { GraphBuilder.QueueBufferExtraction(VisibleLightHash, &MegaLightsViewState.VisibleLightHashHistory); GraphBuilder.QueueBufferExtraction(VisibleLightMaskHash, &MegaLightsViewState.VisibleLightMaskHashHistory); } else { MegaLightsViewState.VisibleLightHashHistory = nullptr; MegaLightsViewState.VisibleLightMaskHashHistory = nullptr; } if (bVolumeGuideByHistory && VolumeVisibleLightHash != nullptr) { GraphBuilder.QueueBufferExtraction(VolumeVisibleLightHash, &MegaLightsViewState.VolumeVisibleLightHashHistory); } else { MegaLightsViewState.VolumeVisibleLightHashHistory = nullptr; } if (bTranslucencyVolumeGuideByHistory && TranslucencyVolumeVisibleLightHash[0] != nullptr && TranslucencyVolumeVisibleLightHash[1] != nullptr) { GraphBuilder.QueueBufferExtraction(TranslucencyVolumeVisibleLightHash[0], &MegaLightsViewState.TranslucencyVolume0VisibleLightHashHistory); GraphBuilder.QueueBufferExtraction(TranslucencyVolumeVisibleLightHash[1], &MegaLightsViewState.TranslucencyVolume1VisibleLightHashHistory); } else { MegaLightsViewState.TranslucencyVolume0VisibleLightHashHistory = nullptr; MegaLightsViewState.TranslucencyVolume1VisibleLightHashHistory = nullptr; } // Scene Depth/Normal history if (InputType == EMegaLightsInput::HairStrands) { if (SceneDepth) { GraphBuilder.QueueTextureExtraction(SceneDepth, &MegaLightsViewState.SceneDepthHistory); } else { MegaLightsViewState.SceneDepthHistory = nullptr; } if (SceneWorldNormal) { GraphBuilder.QueueTextureExtraction(SceneWorldNormal, &MegaLightsViewState.SceneNormalHistory); } else { MegaLightsViewState.SceneNormalHistory = nullptr; } } } }