// Copyright Epic Games, Inc. All Rights Reserved. #include "HairStrandsTransmittance.h" #include "HairStrandsLUT.h" #include "HairStrandsDeepShadow.h" #include "HairStrandsVoxelization.h" #include "HairStrandsRendering.h" #include "BasePassRendering.h" #include "Shader.h" #include "GlobalShader.h" #include "ShaderParameters.h" #include "ShaderParameterStruct.h" #include "SceneTextureParameters.h" #include "RenderGraphUtils.h" #include "PostProcess/PostProcessing.h" #include "PostProcess/SceneFilterRendering.h" #include "ShaderPrintParameters.h" #include "LightSceneInfo.h" #include "ShaderPrint.h" /////////////////////////////////////////////////////////////////////////////////////////////////// static int32 GDeepShadowDebugMode = 0; static FAutoConsoleVariableRef CVarDeepShadowDebugMode(TEXT("r.HairStrands.DeepShadow.DebugMode"), GDeepShadowDebugMode, TEXT("Color debug mode for deep shadow")); static uint32 GetDeepShadowDebugMode() { return uint32(FMath::Max(0, GDeepShadowDebugMode)); } static int32 GDeepShadowKernelType = 2; // 0:linear, 1:PCF_2x2, 2: PCF_6x4, 3:PCSS static float GDeepShadowKernelAperture = 1; static FAutoConsoleVariableRef CVarDeepShadowKernelType(TEXT("r.HairStrands.DeepShadow.KernelType"), GDeepShadowKernelType, TEXT("Set the type of kernel used for evaluating hair transmittance, 0:linear, 1:PCF_2x2, 2: PCF_6x4, 3:PCSS, 4:PCF_6x6_Accurate")); static FAutoConsoleVariableRef CVarDeepShadowKernelAperture(TEXT("r.HairStrands.DeepShadow.KernelAperture"), GDeepShadowKernelAperture, TEXT("Set the aperture angle, in degree, used by the kernel for evaluating the hair transmittance when using PCSS kernel")); static uint32 GetDeepShadowKernelType() { return uint32(FMath::Max(0, GDeepShadowKernelType)); } static float GetDeepShadowKernelAperture() { return GDeepShadowKernelAperture; } static int32 GStrandHairShadowMaskKernelType = 4; static FAutoConsoleVariableRef GVarDeepShadowShadowMaskKernelType(TEXT("r.HairStrands.DeepShadow.ShadowMaskKernelType"), GStrandHairShadowMaskKernelType, TEXT("Set the kernel type for filtering shadow cast by hair on opaque geometry (0:2x2, 1:4x4, 2:Gaussian8, 3:Gaussian16, 4:Gaussian8 with transmittance. Default is 4")); static float GDeepShadowDensityScale = 2; // Default is arbitrary, based on Mike asset static float GDeepShadowDepthBiasScale = 0.05; static FAutoConsoleVariableRef CVarDeepShadowDensityScale(TEXT("r.HairStrands.DeepShadow.DensityScale"), GDeepShadowDensityScale, TEXT("Set density scale for compensating the lack of hair fiber in an asset")); static FAutoConsoleVariableRef CVarDeepShadowDepthBiasScale(TEXT("r.HairStrands.DeepShadow.DepthBiasScale"), GDeepShadowDepthBiasScale, TEXT("Set depth bias scale for transmittance computation")); static int32 GHairStrandsTransmittanceSuperSampling = 0; static FAutoConsoleVariableRef CVarHairStrandsTransmittanceSuperSampling(TEXT("r.HairStrands.DeepShadow.SuperSampling"), GHairStrandsTransmittanceSuperSampling, TEXT("Evaluate transmittance with supersampling. This is expensive and intended to be used only in cine mode."), ECVF_Scalability | ECVF_RenderThreadSafe); static int32 GHairStrandsTransmittanceMaskUseMipTraversal = 1; static FAutoConsoleVariableRef CVarHairStrandsTransmittanceMaskUseMipTraversal(TEXT("r.HairStrands.DeepShadow.MipTraversal"), GHairStrandsTransmittanceMaskUseMipTraversal, TEXT("Evaluate transmittance using mip-map traversal (faster)."), ECVF_Scalability | ECVF_RenderThreadSafe); static int32 GHairStrandsShadowRandomTraversalType = 2; static FAutoConsoleVariableRef CVarHairStrandsShadowRandomTraversalType(TEXT("r.HairStrands.DeepShadow.RandomType"), GHairStrandsShadowRandomTraversalType, TEXT("Change how traversal jittering is initialized. Valid value are 0, 1, and 2. Each type makes different type of tradeoff."), ECVF_Scalability | ECVF_RenderThreadSafe); static int32 GHairStrandsShadow_ShadowMaskPassType = 1; static FAutoConsoleVariableRef CVarHairStrandsShadow_ShadowMaskPassType(TEXT("r.HairStrands.DeepShadow.ShadowMaskPassType"), GHairStrandsShadow_ShadowMaskPassType, TEXT("Change how shadow mask from hair onto opaque geometry is generated. 0: one pass per hair group, 1: one pass for all groups."), ECVF_Scalability | ECVF_RenderThreadSafe); static int32 GHairStrandsShadow_UseScreenTrace = 1; static FAutoConsoleVariableRef CVarHairStrandsShadow_HZBType(TEXT("r.HairStrands.DeepShadow.UseScreenTrace"), GHairStrandsShadow_UseScreenTrace, TEXT("Use screen trace for adding fine occlusion to hair transmission."), ECVF_Scalability | ECVF_RenderThreadSafe); static float GHairStrandsShadow_ScreenTraceLength = 10.f; static FAutoConsoleVariableRef CVarHairStrandsShadow_ScreenTraceLength(TEXT("r.HairStrands.DeepShadow.ScreenTraceLength"), GHairStrandsShadow_ScreenTraceLength, TEXT("Screen trace length for hair transmission."), ECVF_Scalability | ECVF_RenderThreadSafe); static float GetDeepShadowDensityScale() { return FMath::Max(0.0f, GDeepShadowDensityScale); } static float GetDeepShadowDepthBiasScale() { return FMath::Max(0.0f, GDeepShadowDepthBiasScale); } /////////////////////////////////////////////////////////////////////////////////////////////////// enum class EHairTransmittancePassType : uint8 { PerLight, OnePass }; static bool HasDeepShadowData(const FLightSceneInfo* LightSceneInfo, const FHairStrandsMacroGroupDatas& InDatas) { for (const FHairStrandsMacroGroupData& MacroGroupData : InDatas) { for (const FHairStrandsDeepShadowData& DomData : MacroGroupData.DeepShadowDatas) { if (DomData.LightId == LightSceneInfo->Id) return true; } } return false; } FVector4f ComputeDeepShadowLayerDepths(float LayerDistribution) { // LayerDistribution in [0..1] // Exponent in [1 .. 6.2] // Default LayerDistribution is 0.5, which is mapped onto exponent=3.1, making the last layer at depth 0.5f in clip space // Within this range the last layer's depth goes from 1 to 0.25 in clip space (prior to inverse Z) const float Exponent = FMath::Clamp(LayerDistribution, 0.f, 1.f) * 5.2f + 1; FVector4f Depths; Depths.X = FMath::Pow(0.2f, Exponent); Depths.Y = FMath::Pow(0.4f, Exponent); Depths.Z = FMath::Pow(0.6f, Exponent); Depths.W = FMath::Pow(0.8f, Exponent); return Depths; } static FVector4f GetLightTranslatedWorldPositionAndDirection(const FViewInfo& View, const FLightSceneInfo* LightSceneInfo) { const FVector& TranslatedWorldOffset = View.ViewMatrices.GetPreViewTranslation(); if (LightSceneInfo->Proxy->GetLightType() == ELightComponentType::LightType_Directional) { return FVector4f((FVector3f)LightSceneInfo->Proxy->GetDirection(), 0.f); } else { return FVector4f(FVector4f(LightSceneInfo->Proxy->GetPosition() + TranslatedWorldOffset), 1.0f); // LWC_TODO: precision loss } } struct FHairStrandsTransmittanceLightParams { FVector4f TranslatedLightPosition_LightDirection = FVector4f(0, 0, 0, 0); uint32 LightChannelMask = 0; uint32 ShadowChannelMask = 0; float LightRadius = 0; }; /////////////////////////////////////////////////////////////////////////////////////////////////// // Clear transmittance Mask class FHairStrandsClearTransmittanceMaskCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FHairStrandsClearTransmittanceMaskCS); SHADER_USE_PARAMETER_STRUCT(FHairStrandsClearTransmittanceMaskCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER(uint32, ElementCount) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, OutputMask) END_SHADER_PARAMETER_STRUCT() public: static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsHairStrandsSupported(EHairStrandsShaderType::Strands, Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("SHADER_CLEAR"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FHairStrandsClearTransmittanceMaskCS, "/Engine/Private/HairStrands/HairStrandsDeepTransmittanceMask.usf", "MainCS", SF_Compute); static void AddHairStrandsClearTransmittanceMaskPass( FRDGBuilder& GraphBuilder, FGlobalShaderMap* ShaderMap, FRDGBufferRef OutTransmittanceMask) { FHairStrandsClearTransmittanceMaskCS::FParameters* Parameters = GraphBuilder.AllocParameters(); Parameters->ElementCount = OutTransmittanceMask->Desc.NumElements; Parameters->OutputMask = GraphBuilder.CreateUAV(OutTransmittanceMask, FHairStrandsTransmittanceMaskData::Format); FHairStrandsClearTransmittanceMaskCS::FPermutationDomain PermutationVector; TShaderMapRef ComputeShader(ShaderMap, PermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("HairStrands::ClearTransmittanceMask"), ComputeShader, Parameters, FComputeShaderUtils::GetGroupCount(Parameters->ElementCount, 64)); } /////////////////////////////////////////////////////////////////////////////////////////////////// // Transmittance buffer static FRDGBufferRef CreateHairStrandsTransmittanceMaskBuffer(FRDGBuilder& GraphBuilder, uint32 NumElements) { return GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc( sizeof(uint32), NumElements), TEXT("Hair.TransmittanceNodeData")); } FHairStrandsTransmittanceMaskData CreateDummyHairStrandsTransmittanceMaskData(FRDGBuilder& GraphBuilder, FGlobalShaderMap* ShaderMap) { FHairStrandsTransmittanceMaskData Out; Out.TransmittanceMask = CreateHairStrandsTransmittanceMaskBuffer(GraphBuilder, 1); AddHairStrandsClearTransmittanceMaskPass(GraphBuilder, ShaderMap, Out.TransmittanceMask); return Out; } /////////////////////////////////////////////////////////////////////////////////////////////////// // Transmittance Mask from voxel class FHairStrandsVoxelTransmittanceMaskCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FHairStrandsVoxelTransmittanceMaskCS); SHADER_USE_PARAMETER_STRUCT(FHairStrandsVoxelTransmittanceMaskCS, FGlobalShader); class FTransmittanceGroupSize : SHADER_PERMUTATION_SPARSE_INT("PERMUTATION_GROUP_SIZE", 32, 64); class FSuperSampling : SHADER_PERMUTATION_INT("PERMUTATION_SUPERSAMPLING", 2); class FTraversal : SHADER_PERMUTATION_INT("PERMUTATION_TRAVERSAL", 2); class FOnePass : SHADER_PERMUTATION_BOOL("PERMUTATION_ONE_PASS"); using FPermutationDomain = TShaderPermutationDomain; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureParameters, SceneTextures) SHADER_PARAMETER_STRUCT_INCLUDE(ShaderPrint::FShaderParameters, ShaderPrintParameters) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FHairStrandsViewUniformParameters, HairStrands) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualVoxelParameters, VirtualVoxel) SHADER_PARAMETER_STRUCT_INCLUDE(FVirtualShadowMapSamplingParameters, VirtualShadowMap) SHADER_PARAMETER_STRUCT_REF(FBlueNoise, BlueNoise) SHADER_PARAMETER_STRUCT_INCLUDE(FHZBParameters, HZBParameters) SHADER_PARAMETER(float, ScreenTraceNoFallbackThicknessScale) SHADER_PARAMETER(uint32, bUseScreenTrace) SHADER_PARAMETER(float, ScreenTraceLength) SHADER_PARAMETER(FVector4f, TranslatedLightPosition_LightDirection) SHADER_PARAMETER(FVector4f, ShadowChannelMask) SHADER_PARAMETER(uint32, LightChannelMask) SHADER_PARAMETER(float, LightRadius) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ShadowMaskBitsTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, RayMarchMaskTexture) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, OutTransmittanceMask) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FForwardLightUniformParameters, ForwardLightStruct) RDG_BUFFER_ACCESS(IndirectArgsBuffer, ERHIAccess::IndirectArgs) END_SHADER_PARAMETER_STRUCT() public: static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsHairStrandsSupported(EHairStrandsShaderType::Strands, Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); FForwardLightingParameters::ModifyCompilationEnvironment(Parameters.Platform, OutEnvironment); OutEnvironment.SetDefine(TEXT("SHADER_TRANSMITTANCE_VOXEL"), 1); OutEnvironment.CompilerFlags.Add(CFLAG_Wave32); } }; IMPLEMENT_GLOBAL_SHADER(FHairStrandsVoxelTransmittanceMaskCS, "/Engine/Private/HairStrands/HairStrandsDeepTransmittanceMask.usf", "MainCS", SF_Compute); // Transmittance mask using voxel volume static FRDGBufferRef AddHairStrandsVoxelTransmittanceMaskPass( FRDGBuilder& GraphBuilder, const FSceneTextureParameters& SceneTextures, const FViewInfo& View, int32 ViewIndex, const EHairTransmittancePassType PassType, const FHairStrandsTransmittanceLightParams& Params, const uint32 NodeGroupSize, FRDGBufferRef IndirectArgsBuffer, FRDGTextureRef ShadowMaskTexture, FVirtualShadowMapArray* VirtualShadowMapArray = nullptr) { check(HairStrands::HasViewHairStrandsVoxelData(View)); check(NodeGroupSize == 64 || NodeGroupSize == 32); const uint32 MaxLightPerPass = 10u; // HAIR_TODO: Need to match the virtual shadow mask bits encoding const uint32 AverageLightPerPixel = PassType == EHairTransmittancePassType::OnePass ? MaxLightPerPass : 1u; FRDGBufferRef OutBuffer = CreateHairStrandsTransmittanceMaskBuffer(GraphBuilder, View.HairStrandsViewData.VisibilityData.MaxNodeCount * AverageLightPerPixel); FHairStrandsVoxelTransmittanceMaskCS::FParameters* Parameters = GraphBuilder.AllocParameters(); Parameters->ViewUniformBuffer = View.ViewUniformBuffer; Parameters->SceneTextures = SceneTextures; Parameters->OutTransmittanceMask = GraphBuilder.CreateUAV(OutBuffer, FHairStrandsTransmittanceMaskData::Format); if (PassType == EHairTransmittancePassType::OnePass) { check(VirtualShadowMapArray != nullptr); Parameters->VirtualShadowMap = VirtualShadowMapArray->GetSamplingParameters(GraphBuilder, ViewIndex); Parameters->ForwardLightStruct = View.ForwardLightingResources.ForwardLightUniformBuffer; Parameters->RayMarchMaskTexture = nullptr; Parameters->ShadowMaskBitsTexture = ShadowMaskTexture ? ShadowMaskTexture : GSystemTextures.GetZeroUIntDummy(GraphBuilder); } else { Parameters->TranslatedLightPosition_LightDirection = Params.TranslatedLightPosition_LightDirection; Parameters->LightRadius = Params.LightRadius; Parameters->RayMarchMaskTexture = ShadowMaskTexture ? ShadowMaskTexture : GSystemTextures.GetWhiteDummy(GraphBuilder); Parameters->ShadowMaskBitsTexture = nullptr; } Parameters->ShadowChannelMask = FVector4f(0, 0, 0, 0); Parameters->ShadowChannelMask[FMath::Clamp(Params.ShadowChannelMask, 0, 3)] = 1.0f; Parameters->LightChannelMask = Params.LightChannelMask; Parameters->IndirectArgsBuffer = IndirectArgsBuffer; Parameters->HairStrands = HairStrands::BindHairStrandsViewUniformParameters(View); Parameters->VirtualVoxel = HairStrands::BindHairStrandsVoxelUniformParameters(View); FBlueNoise BlueNoise = GetBlueNoiseGlobalParameters(); Parameters->BlueNoise = CreateUniformBufferImmediate(BlueNoise, EUniformBufferUsage::UniformBuffer_SingleDraw); ShaderPrint::SetParameters(GraphBuilder, View.ShaderPrintData, Parameters->ShaderPrintParameters); const bool bIsSuperSampled = GHairStrandsTransmittanceSuperSampling > 0; const bool bIsMipTraversal = GHairStrandsTransmittanceMaskUseMipTraversal > 0; // Screen trace const float ScreenTraceNoFallbackThicknessScale = View.ViewMatrices.GetPerProjectionDepthThicknessScale(); Parameters->HZBParameters = GetHZBParameters(GraphBuilder, View, EHZBType::ClosestHZB, View.HairStrandsViewData.VisibilityData.HairOnlyDepthFurthestHZBTexture, View.HairStrandsViewData.VisibilityData.HairOnlyDepthClosestHZBTexture); Parameters->ScreenTraceNoFallbackThicknessScale = ScreenTraceNoFallbackThicknessScale; Parameters->bUseScreenTrace = GHairStrandsShadow_UseScreenTrace > 0? 1u : 0u; Parameters->ScreenTraceLength = FMath::Max(GHairStrandsShadow_ScreenTraceLength, 0.f); FHairStrandsVoxelTransmittanceMaskCS::FPermutationDomain PermutationVector; PermutationVector.Set(NodeGroupSize); PermutationVector.Set(bIsSuperSampled ? 1 : 0); PermutationVector.Set(bIsMipTraversal ? 1 : 0); PermutationVector.Set(PassType == EHairTransmittancePassType::OnePass); TShaderMapRef ComputeShader(View.ShaderMap, PermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("HairStrands::TransmittanceMask(Voxel,%s)", PassType == EHairTransmittancePassType::OnePass ? TEXT("OnePass") : TEXT("PerLight")), ComputeShader, Parameters, IndirectArgsBuffer, 0); return OutBuffer; } /////////////////////////////////////////////////////////////////////////////////////////////////// // Transmittance Mask from deep shadow class FHairStrandsDeepShadowTransmittanceMaskCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FHairStrandsDeepShadowTransmittanceMaskCS); SHADER_USE_PARAMETER_STRUCT(FHairStrandsDeepShadowTransmittanceMaskCS, FGlobalShader); class FTransmittanceGroupSize : SHADER_PERMUTATION_SPARSE_INT("PERMUTATION_GROUP_SIZE", 32, 64); using FPermutationDomain = TShaderPermutationDomain; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureParameters, SceneTextures) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FHairStrandsViewUniformParameters, HairStrands) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER(FIntPoint, DeepShadow_AtlasResolution) SHADER_PARAMETER(float, LightRadius) SHADER_PARAMETER(FVector4f, TranslatedLightPosition_LightDirection) SHADER_PARAMETER(uint32, LightChannelMask) SHADER_PARAMETER(FVector4f, ShadowChannelMask) SHADER_PARAMETER(FVector4f, DeepShadow_LayerDepths) SHADER_PARAMETER(float, DeepShadow_DepthBiasScale) SHADER_PARAMETER(float, DeepShadow_DensityScale) SHADER_PARAMETER(float, DeepShadow_KernelAperture) SHADER_PARAMETER(uint32, DeepShadow_KernelType) SHADER_PARAMETER(uint32, DeepShadow_DebugMode) SHADER_PARAMETER(FMatrix44f, DeepShadow_ShadowToWorld) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, RayMarchMaskTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, DeepShadow_FrontDepthTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, DeepShadow_DomTexture) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, DeepShadow_ViewInfoBuffer) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, DeepShadow_AtlasSlotIndexBuffer) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, OutTransmittanceMask) RDG_BUFFER_ACCESS(IndirectArgsBuffer, ERHIAccess::IndirectArgs) END_SHADER_PARAMETER_STRUCT() public: static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsHairStrandsSupported(EHairStrandsShaderType::Strands, Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("SHADER_TRANSMITTANCE_DEEPSHADOW"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FHairStrandsDeepShadowTransmittanceMaskCS, "/Engine/Private/HairStrands/HairStrandsDeepTransmittanceMask.usf", "MainCS", SF_Compute); struct FHairStrandsDeepShadowTransmittanceLightParams : FHairStrandsTransmittanceLightParams { //FIntVector4 DeepShadow_AtlasSlotOffsets_AtlasSlotIndex[FHairStrandsDeepShadowData::MaxMacroGroupCount]; FRDGBufferSRVRef DeepShadow_ViewInfoBuffer = nullptr; FRDGBufferSRVRef DeepShadow_AtlasSlotIndexBuffer = nullptr; FIntPoint DeepShadow_AtlasResolution = FIntPoint(0, 0); FVector4f DeepShadow_LayerDepths = FVector4f(0, 0, 0, 0); float DeepShadow_DepthBiasScale = 0; float DeepShadow_DensityScale = 0; FMatrix DeepShadow_ShadowToWorld = FMatrix::Identity; uint32 OutputChannel = ~0; uint32 ShadowChannelMask = 0; FRDGTextureRef DeepShadow_FrontDepthTexture = nullptr; FRDGTextureRef DeepShadow_DomTexture = nullptr; }; // Transmittance mask using deep shadow static FRDGBufferRef AddHairStrandsDeepShadowTransmittanceMaskPass( FRDGBuilder& GraphBuilder, const FSceneTextureParameters& SceneTextures, const FViewInfo& View, const FHairStrandsDeepShadowTransmittanceLightParams& Params, const uint32 NodeGroupSize, FRDGBufferRef IndirectArgsBuffer, FRDGTextureRef ScreenShadowMaskSubPixelTexture) { FRDGBufferRef OutBuffer = CreateHairStrandsTransmittanceMaskBuffer(GraphBuilder, View.HairStrandsViewData.VisibilityData.MaxNodeCount); FHairStrandsDeepShadowTransmittanceMaskCS::FParameters* Parameters = GraphBuilder.AllocParameters(); Parameters->ViewUniformBuffer = View.ViewUniformBuffer; Parameters->SceneTextures = SceneTextures; Parameters->DeepShadow_FrontDepthTexture = Params.DeepShadow_FrontDepthTexture; Parameters->DeepShadow_DomTexture = Params.DeepShadow_DomTexture; Parameters->OutTransmittanceMask = GraphBuilder.CreateUAV(OutBuffer, FHairStrandsTransmittanceMaskData::Format); Parameters->LightChannelMask = Params.LightChannelMask; Parameters->DeepShadow_AtlasResolution = Params.DeepShadow_AtlasResolution; Parameters->TranslatedLightPosition_LightDirection = Params.TranslatedLightPosition_LightDirection; Parameters->LightRadius = Params.LightRadius; Parameters->DeepShadow_DepthBiasScale = Params.DeepShadow_DepthBiasScale; Parameters->DeepShadow_DensityScale = Params.DeepShadow_DensityScale; Parameters->DeepShadow_KernelAperture = GetDeepShadowKernelAperture(); Parameters->DeepShadow_KernelType = GetDeepShadowKernelType(); Parameters->DeepShadow_DebugMode = GetDeepShadowDebugMode(); Parameters->DeepShadow_LayerDepths = Params.DeepShadow_LayerDepths; Parameters->DeepShadow_ShadowToWorld = FMatrix44f(Params.DeepShadow_ShadowToWorld); // LWC_TODO: Precision loss Parameters->IndirectArgsBuffer = IndirectArgsBuffer; Parameters->HairStrands = HairStrands::BindHairStrandsViewUniformParameters(View); Parameters->DeepShadow_ViewInfoBuffer = Params.DeepShadow_ViewInfoBuffer; Parameters->DeepShadow_AtlasSlotIndexBuffer = Params.DeepShadow_AtlasSlotIndexBuffer; Parameters->RayMarchMaskTexture = ScreenShadowMaskSubPixelTexture ? ScreenShadowMaskSubPixelTexture : GraphBuilder.RegisterExternalTexture(GSystemTextures.WhiteDummy); Parameters->ShadowChannelMask = FVector4f(0, 0, 0, 0); Parameters->ShadowChannelMask[FMath::Clamp(Params.ShadowChannelMask, 0, 3)] = 1.0f; check(NodeGroupSize == 64 || NodeGroupSize == 32); FHairStrandsDeepShadowTransmittanceMaskCS::FPermutationDomain PermutationVector; PermutationVector.Set(NodeGroupSize); TShaderMapRef ComputeShader(View.ShaderMap, PermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("HairStrands::TransmittanceMask(DeepShadow)"), ComputeShader, Parameters, IndirectArgsBuffer, 0); return OutBuffer; } /////////////////////////////////////////////////////////////////////////////////////////////////// // Opaque Mask from voxel volume class FHairStrandsVoxelShadowMaskPS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FHairStrandsVoxelShadowMaskPS); SHADER_USE_PARAMETER_STRUCT(FHairStrandsVoxelShadowMaskPS, FGlobalShader); class FOnePass : SHADER_PERMUTATION_BOOL("PERMUTATION_USE_ONEPASS"); using FPermutationDomain = TShaderPermutationDomain; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(ShaderPrint::FShaderParameters, ShaderPrintParameters) SHADER_PARAMETER(FVector4f, Voxel_TranslatedLightPosition_LightDirection) SHADER_PARAMETER(uint32, Voxel_MacroGroupCount) SHADER_PARAMETER(uint32, Voxel_MacroGroupId) SHADER_PARAMETER(uint32, Voxel_RandomType) SHADER_PARAMETER(uint32, EncodingType) SHADER_PARAMETER(float, FadeAlpha) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, RayMarchMaskTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneDepthTexture) SHADER_PARAMETER_SAMPLER(SamplerState, LinearSampler) SHADER_PARAMETER_SAMPLER(SamplerState, ShadowSampler) SHADER_PARAMETER_STRUCT_REF(FBlueNoise, BlueNoise) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualVoxelParameters, VirtualVoxel) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FHairStrandsViewUniformParameters, HairStrands) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() public: static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsHairStrandsSupported(EHairStrandsShaderType::Strands, Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetRenderTargetOutputFormat(0, PF_B8G8R8A8); OutEnvironment.SetDefine(TEXT("SHADER_SHADOWMASK_VOXEL"), 1); OutEnvironment.CompilerFlags.Add(CFLAG_Wave32); } }; IMPLEMENT_GLOBAL_SHADER(FHairStrandsVoxelShadowMaskPS, "/Engine/Private/HairStrands/HairStrandsDeepShadowMask.usf", "MainPS", SF_Pixel); // Opaque mask from voxels static void AddHairStrandsVoxelShadowMaskPass( FRDGBuilder& GraphBuilder, FRDGTextureRef SceneDepthTexture, const FViewInfo& View, const FLightSceneInfo* LightSceneInfo, const float FadeAlpha, const uint32 EncodingType, const bool bProjectingForForwardShading, FRDGTextureRef& OutShadowMask) { check(LightSceneInfo); check(OutShadowMask); check(HairStrands::HasViewHairStrandsVoxelData(View)); // Copy the shadow mask texture to read its content, and early out voxel traversal FRDGTextureRef RayMarchMask = nullptr; { FRDGTextureDesc Desc = OutShadowMask->Desc; Desc.Flags |= TexCreate_ShaderResource; RayMarchMask = GraphBuilder.CreateTexture(Desc, TEXT("Hair.RayMarchMask")); FRHICopyTextureInfo CopyInfo; CopyInfo.Size = OutShadowMask->Desc.GetSize(); AddCopyTexturePass(GraphBuilder, OutShadowMask, RayMarchMask, CopyInfo); } const bool bOnePass = GHairStrandsShadow_ShadowMaskPassType > 0 && View.HairStrandsViewData.MacroGroupDatas.Num() > 1; const FIntPoint OutputResolution = SceneDepthTexture->Desc.Extent; FHairStrandsVoxelShadowMaskPS::FPermutationDomain PermutationVector; PermutationVector.Set(bOnePass); TShaderMapRef VertexShader(View.ShaderMap); TShaderMapRef PixelShader(View.ShaderMap, PermutationVector); const FGlobalShaderMap* GlobalShaderMap = View.ShaderMap; const FIntRect Viewport = View.ViewRect; const uint32 OutputChannel = bProjectingForForwardShading ? LightSceneInfo->GetDynamicShadowMapChannel() : ~0; const FIntPoint Resolution = OutShadowMask->Desc.Extent; FBlueNoise BlueNoise = GetBlueNoiseGlobalParameters(); for (int32 GroupIt=0, GroupCount=View.HairStrandsViewData.MacroGroupDatas.Num(); GroupIt < GroupCount; ++GroupIt) { if (bOnePass && GroupIt > 0) { return; } const FHairStrandsMacroGroupData& MacroGroupData = View.HairStrandsViewData.MacroGroupDatas[GroupIt]; FHairStrandsVoxelShadowMaskPS::FParameters* Parameters = GraphBuilder.AllocParameters(); Parameters->FadeAlpha = FadeAlpha; Parameters->EncodingType = EncodingType; Parameters->ViewUniformBuffer = View.ViewUniformBuffer; Parameters->SceneDepthTexture = SceneDepthTexture; Parameters->HairStrands = HairStrands::BindHairStrandsViewUniformParameters(View); Parameters->VirtualVoxel = HairStrands::BindHairStrandsVoxelUniformParameters(View); Parameters->LinearSampler = TStaticSamplerState::GetRHI(); Parameters->ShadowSampler = TStaticSamplerState::GetRHI(); Parameters->BlueNoise = CreateUniformBufferImmediate(BlueNoise, EUniformBufferUsage::UniformBuffer_SingleDraw); Parameters->Voxel_TranslatedLightPosition_LightDirection = GetLightTranslatedWorldPositionAndDirection(View, LightSceneInfo); Parameters->Voxel_MacroGroupId = MacroGroupData.MacroGroupId; Parameters->Voxel_MacroGroupCount = GroupCount; Parameters->Voxel_RandomType = FMath::Clamp(GHairStrandsShadowRandomTraversalType, 0, 2); Parameters->RenderTargets[0] = FRenderTargetBinding(OutShadowMask, ERenderTargetLoadAction::ELoad); ShaderPrint::SetParameters(GraphBuilder, View.ShaderPrintData, Parameters->ShaderPrintParameters); Parameters->RayMarchMaskTexture = RayMarchMask; ClearUnusedGraphResources(PixelShader, Parameters); GraphBuilder.AddPass( RDG_EVENT_NAME("HairStrands::ShadowMask(Voxel,%s)", bOnePass ? TEXT("OnePass") : TEXT("PerGroup")), Parameters, ERDGPassFlags::Raster, [Parameters, VertexShader, PixelShader, Viewport, Resolution, OutputChannel](FRDGAsyncTask, FRHICommandList& RHICmdList) { FGraphicsPipelineStateInitializer GraphicsPSOInit; RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); switch (OutputChannel) { case 0: GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); break; // Min Operator case 1: GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); break; // Min Operator case 2: GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); break; // Min Operator case 3: GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); break; // Min Operator default: GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); break; // Min Operator } GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI(); GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState::GetRHI(); GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI; GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader(); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader(); GraphicsPSOInit.PrimitiveType = PT_TriangleList; SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0); RHICmdList.SetViewport(Viewport.Min.X, Viewport.Min.Y, 0.0f, Viewport.Max.X, Viewport.Max.Y, 1.0f); SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *Parameters); DrawRectangle( RHICmdList, 0, 0, Viewport.Width(), Viewport.Height(), Viewport.Min.X, Viewport.Min.Y, Viewport.Width(), Viewport.Height(), Viewport.Size(), Resolution, VertexShader, EDRF_UseTriangleOptimization); }); } } /////////////////////////////////////////////////////////////////////////////////////////////////// // Opaque Mask from deep shadow class FHairStrandsDeepShadowMaskPS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FHairStrandsDeepShadowMaskPS); SHADER_USE_PARAMETER_STRUCT(FHairStrandsDeepShadowMaskPS, FGlobalShader); class FKernelType : SHADER_PERMUTATION_INT("PERMUTATION_KERNEL_TYPE", 5); using FPermutationDomain = TShaderPermutationDomain; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(ShaderPrint::FShaderParameters, ShaderPrintParameters) SHADER_PARAMETER(FIntPoint, DeepShadow_AtlasResolution) SHADER_PARAMETER(float, DeepShadow_DepthBiasScale) SHADER_PARAMETER(float, DeepShadow_DensityScale) SHADER_PARAMETER(FVector4f, DeepShadow_LayerDepths) SHADER_PARAMETER(float, FadeAlpha) SHADER_PARAMETER(uint32, EncodingType) SHADER_PARAMETER(uint32, EffectiveAtlasSlotCount) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, RayMarchMaskTexture) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, DeepShadow_ViewInfoBuffer) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, DeepShadow_AtlasSlotIndexBuffer) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, DeepShadow_FrontDepthTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, DeepShadow_DomTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneDepthTexture) SHADER_PARAMETER_SAMPLER(SamplerState, LinearSampler) SHADER_PARAMETER_SAMPLER(SamplerState, ShadowSampler) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FHairStrandsViewUniformParameters, HairStrands) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() public: static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsHairStrandsSupported(EHairStrandsShaderType::Strands, Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetRenderTargetOutputFormat(0, PF_B8G8R8A8); OutEnvironment.SetDefine(TEXT("SHADER_SHADOWMASK_DEEPSHADOW"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FHairStrandsDeepShadowMaskPS, "/Engine/Private/HairStrands/HairStrandsDeepShadowMask.usf", "MainPS", SF_Pixel); struct FHairStrandsDeepShadowParams { uint32 OutputChannel = ~0; FRDGBufferSRVRef DeepShadow_ViewInfoBuffer = nullptr; FRDGBufferSRVRef DeepShadow_AtlasSlotIndexBuffer = nullptr; FIntPoint DeepShadow_AtlasResolution; FRDGTextureRef DeepShadow_FrontDepthTexture = nullptr; FRDGTextureRef DeepShadow_LayerTexture = nullptr; float DeepShadow_DepthBiasScale = 1; float DeepShadow_DensityScale = 1; FVector4f DeepShadow_LayerDepths = FVector4f(0, 0, 0, 0); }; // Opaque mask with deep shadow static void AddHairStrandsDeepShadowMaskPass( FRDGBuilder& GraphBuilder, FRDGTextureRef SceneDepthTexture, const FViewInfo& View, const FHairStrandsDeepShadowParams& Params, const float FadeAlpha, const uint32 EncodingType, const uint32 EffectiveAtlasSlotCount, FRDGTextureRef& OutShadowMask) { check(OutShadowMask); FHairStrandsDeepShadowMaskPS::FParameters* Parameters = GraphBuilder.AllocParameters(); Parameters->FadeAlpha = FadeAlpha; Parameters->EncodingType = EncodingType; Parameters->ViewUniformBuffer = View.ViewUniformBuffer; Parameters->SceneDepthTexture = SceneDepthTexture; Parameters->DeepShadow_FrontDepthTexture = Params.DeepShadow_FrontDepthTexture; Parameters->DeepShadow_DomTexture = Params.DeepShadow_LayerTexture; Parameters->LinearSampler = TStaticSamplerState::GetRHI(); Parameters->ShadowSampler = TStaticSamplerState::GetRHI(); Parameters->DeepShadow_DepthBiasScale = Params.DeepShadow_DepthBiasScale; Parameters->DeepShadow_DensityScale = Params.DeepShadow_DensityScale; Parameters->DeepShadow_LayerDepths = Params.DeepShadow_LayerDepths; Parameters->RenderTargets[0] = FRenderTargetBinding(OutShadowMask, ERenderTargetLoadAction::ELoad); Parameters->DeepShadow_AtlasResolution = Params.DeepShadow_AtlasResolution; Parameters->EffectiveAtlasSlotCount = EffectiveAtlasSlotCount; Parameters->DeepShadow_AtlasSlotIndexBuffer = Params.DeepShadow_AtlasSlotIndexBuffer; Parameters->DeepShadow_ViewInfoBuffer = Params.DeepShadow_ViewInfoBuffer; Parameters->HairStrands = HairStrands::BindHairStrandsViewUniformParameters(View); if (ShaderPrint::IsValid(View.ShaderPrintData)) { ShaderPrint::SetParameters(GraphBuilder, View.ShaderPrintData, Parameters->ShaderPrintParameters); } FRDGTextureRef RayMarchMask = nullptr; { FRDGTextureDesc Desc = OutShadowMask->Desc; Desc.Flags |= TexCreate_ShaderResource; RayMarchMask = GraphBuilder.CreateTexture(Desc, TEXT("Hair.RayMarchMask")); FRHICopyTextureInfo CopyInfo; CopyInfo.Size = OutShadowMask->Desc.GetSize(); AddCopyTexturePass(GraphBuilder, OutShadowMask, RayMarchMask, CopyInfo); } Parameters->RayMarchMaskTexture = RayMarchMask; FHairStrandsDeepShadowMaskPS::FPermutationDomain PermutationVector; PermutationVector.Set(FMath::Clamp(GStrandHairShadowMaskKernelType, 0, 4)); const FIntPoint OutputResolution = SceneDepthTexture->Desc.Extent; TShaderMapRef VertexShader(View.ShaderMap); TShaderMapRef PixelShader(View.ShaderMap, PermutationVector); const FGlobalShaderMap* GlobalShaderMap = View.ShaderMap; const FIntRect Viewport = View.ViewRect; const uint32 OutputChannel = Params.OutputChannel; ClearUnusedGraphResources(PixelShader, Parameters); FIntPoint Resolution = OutShadowMask->Desc.Extent; GraphBuilder.AddPass( RDG_EVENT_NAME("HairStrands::ShadowMask"), Parameters, ERDGPassFlags::Raster, [Parameters, VertexShader, PixelShader, Viewport, Resolution, OutputChannel](FRDGAsyncTask, FRHICommandList& RHICmdList) { FGraphicsPipelineStateInitializer GraphicsPSOInit; RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); switch (OutputChannel) { case 0: GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); break; // Min Operator case 1: GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); break; // Min Operator case 2: GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); break; // Min Operator case 3: GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); break; // Min Operator default: GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); break; // Min Operator } GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI(); GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState::GetRHI(); GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI; GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader(); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader(); GraphicsPSOInit.PrimitiveType = PT_TriangleList; SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0); RHICmdList.SetViewport(Viewport.Min.X, Viewport.Min.Y, 0.0f, Viewport.Max.X, Viewport.Max.Y, 1.0f); SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *Parameters); DrawRectangle( RHICmdList, 0, 0, Viewport.Width(), Viewport.Height(), Viewport.Min.X, Viewport.Min.Y, Viewport.Width(), Viewport.Height(), Viewport.Size(), Resolution, VertexShader, EDRF_UseTriangleOptimization); }); } /////////////////////////////////////////////////////////////////////////////////////////////////// static FHairStrandsTransmittanceMaskData InternalRenderHairStrandsTransmittanceMask( FRDGBuilder& GraphBuilder, const FViewInfo& View, int32 ViewIndex, const FLightSceneInfo* LightSceneInfo, const FHairStrandsVisibilityData& VisibilityData, const FHairStrandsMacroGroupDatas& MacroGroupDatas, const FHairStrandsDeepShadowResources& DeepShadowResources, const FHairStrandsVoxelResources& VoxelResources, const bool bProjectingForForwardShading, FRDGTextureRef ScreenShadowMaskSubPixelTexture) { FHairStrandsTransmittanceMaskData Out; if (MacroGroupDatas.Num() == 0) return Out; if (!HasDeepShadowData(LightSceneInfo, MacroGroupDatas) && !IsHairStrandsVoxelizationEnable(View.GetShaderPlatform())) return Out; DECLARE_GPU_STAT(HairStrandsTransmittanceMask); RDG_EVENT_SCOPE_STAT(GraphBuilder, HairStrandsTransmittanceMask, "HairStrands::TransmittanceMask"); RDG_GPU_STAT_SCOPE(GraphBuilder, HairStrandsTransmittanceMask); // Note: GbufferB.a store the shading model on the 4 lower bits (MATERIAL_SHADINGMODEL_HAIR) FSceneTextureParameters SceneTextures = GetSceneTextureParameters(GraphBuilder, View); bool bHasFoundLight = false; if (!IsHairStrandsForVoxelTransmittanceAndShadowEnable(View.GetShaderPlatform())) { FHairStrandsDeepShadowTransmittanceLightParams Params; Params.DeepShadow_DensityScale = GetDeepShadowDensityScale(); Params.DeepShadow_DepthBiasScale = GetDeepShadowDepthBiasScale(); TArray AtlasSlotIndexBuffer; AtlasSlotIndexBuffer.Reserve(MacroGroupDatas.Num()); for (const FHairStrandsMacroGroupData& MacroGroupData : MacroGroupDatas) { uint32 AtlasSlotIndex = 0; for (const FHairStrandsDeepShadowData& DeepShadowData : MacroGroupData.DeepShadowDatas) { if (DeepShadowData.LightId == LightSceneInfo->Id) { bHasFoundLight = true; Params.TranslatedLightPosition_LightDirection = GetLightTranslatedWorldPositionAndDirection(View, LightSceneInfo); Params.DeepShadow_FrontDepthTexture = DeepShadowResources.DepthAtlasTexture; Params.DeepShadow_DomTexture = DeepShadowResources.LayersAtlasTexture; Params.DeepShadow_AtlasResolution = DeepShadowData.AtlasResolution; Params.LightRadius = 0; Params.LightChannelMask = LightSceneInfo->Proxy->GetLightingChannelMask(); Params.ShadowChannelMask = bProjectingForForwardShading ? LightSceneInfo->GetDynamicShadowMapChannel() : 0; Params.DeepShadow_LayerDepths = ComputeDeepShadowLayerDepths(DeepShadowData.LayerDistribution); AtlasSlotIndex = DeepShadowData.AtlasSlotIndex; } } AtlasSlotIndexBuffer.Add(AtlasSlotIndex); } if (bHasFoundLight) { check(Params.DeepShadow_FrontDepthTexture); check(Params.DeepShadow_DomTexture); FRDGBufferRef DeepShadowAtlasSlotIndexBuffer = CreateStructuredBuffer(GraphBuilder, TEXT("Hair.DeepShadow.AtlasSlotIndexBuffer"), 4u, AtlasSlotIndexBuffer.Num(), AtlasSlotIndexBuffer.GetData(), 4u * AtlasSlotIndexBuffer.Num()); Params.DeepShadow_ViewInfoBuffer = GraphBuilder.CreateSRV(DeepShadowResources.DeepShadowViewInfoBuffer); Params.DeepShadow_AtlasSlotIndexBuffer = GraphBuilder.CreateSRV(DeepShadowAtlasSlotIndexBuffer); Out.TransmittanceMask = AddHairStrandsDeepShadowTransmittanceMaskPass( GraphBuilder, SceneTextures, View, Params, VisibilityData.NodeGroupSize, VisibilityData.NodeIndirectArg, ScreenShadowMaskSubPixelTexture); } } if (!bHasFoundLight && VoxelResources.IsValid()) { FLightRenderParameters LightParameters; LightSceneInfo->Proxy->GetLightShaderParameters(LightParameters); FHairStrandsTransmittanceLightParams Params; Params.TranslatedLightPosition_LightDirection = GetLightTranslatedWorldPositionAndDirection(View, LightSceneInfo); Params.LightChannelMask = LightSceneInfo->Proxy->GetLightingChannelMask(); Params.ShadowChannelMask = bProjectingForForwardShading ? LightSceneInfo->GetDynamicShadowMapChannel() : 0; Params.LightRadius = FMath::Max(LightParameters.SourceLength, LightParameters.SourceRadius); Out.TransmittanceMask = AddHairStrandsVoxelTransmittanceMaskPass( GraphBuilder, SceneTextures, View, ViewIndex, EHairTransmittancePassType::PerLight, Params, VisibilityData.NodeGroupSize, VisibilityData.NodeIndirectArg, ScreenShadowMaskSubPixelTexture); } return Out; } FHairStrandsTransmittanceMaskData RenderHairStrandsOnePassTransmittanceMask( FRDGBuilder& GraphBuilder, const FViewInfo& View, int32 ViewIndex, FRDGTextureRef ShadowMaskBits, FVirtualShadowMapArray& VirtualShadowMapArray) { FHairStrandsTransmittanceMaskData Out; if (HairStrands::HasViewHairStrandsData(View) && View.HairStrandsViewData.MacroGroupDatas.Num() > 0) { DECLARE_GPU_STAT(HairStrandsOnePassTransmittanceMask); RDG_EVENT_SCOPE_STAT(GraphBuilder, HairStrandsOnePassTransmittanceMask, "HairStrands::TransmittanceMask(OnePass)"); RDG_GPU_STAT_SCOPE(GraphBuilder, HairStrandsOnePassTransmittanceMask); if (HairStrands::HasViewHairStrandsVoxelData(View)) { // Note: GbufferB.a store the shading model on the 4 lower bits (MATERIAL_SHADINGMODEL_HAIR) FSceneTextureParameters SceneTextures = GetSceneTextureParameters(GraphBuilder, View); FHairStrandsTransmittanceLightParams DummyParams; Out.TransmittanceMask = AddHairStrandsVoxelTransmittanceMaskPass( GraphBuilder, SceneTextures, View, ViewIndex, EHairTransmittancePassType::OnePass, DummyParams, View.HairStrandsViewData.VisibilityData.NodeGroupSize, View.HairStrandsViewData.VisibilityData.NodeIndirectArg, ShadowMaskBits, &VirtualShadowMapArray); } } return Out; } FHairStrandsTransmittanceMaskData RenderHairStrandsTransmittanceMask( FRDGBuilder& GraphBuilder, const FViewInfo& View, int32 ViewIndex, const FLightSceneInfo* LightSceneInfo, const bool bProjectingForForwardShading, FRDGTextureRef ScreenShadowMaskSubPixelTexture) { FHairStrandsTransmittanceMaskData TransmittanceMaskData; if (HairStrands::HasViewHairStrandsData(View)) { TransmittanceMaskData = InternalRenderHairStrandsTransmittanceMask( GraphBuilder, View, ViewIndex, LightSceneInfo, View.HairStrandsViewData.VisibilityData, View.HairStrandsViewData.MacroGroupDatas, View.HairStrandsViewData.DeepShadowResources, View.HairStrandsViewData.VirtualVoxelResources, bProjectingForForwardShading, ScreenShadowMaskSubPixelTexture); } return TransmittanceMaskData; } /////////////////////////////////////////////////////////////////////////////////////////////////// static void InternalRenderHairStrandsShadowMask( FRDGBuilder& GraphBuilder, const FViewInfo& View, const uint32 ViewIndex, const FLightSceneInfo* LightSceneInfo, const TArrayView& VisibleLightInfos, const FHairStrandsVisibilityData& InVisibilityData, const FHairStrandsMacroGroupDatas& InMacroGroupDatas, const FHairStrandsDeepShadowResources& DeepShadowResources, const FHairStrandsVoxelResources& VoxelResources, const bool bProjectingForForwardShading, FRDGTextureRef OutShadowMask) { if (InMacroGroupDatas.Num() == 0) return; if (!HasDeepShadowData(LightSceneInfo, InMacroGroupDatas) && !IsHairStrandsVoxelizationEnable(View.GetShaderPlatform())) return; DECLARE_GPU_STAT(HairStrandsOpaqueMask); RDG_EVENT_SCOPE_STAT(GraphBuilder, HairStrandsOpaqueMask, "HairStrands::OpaqueShadowMask"); RDG_GPU_STAT_SCOPE(GraphBuilder, HairStrandsOpaqueMask); const FMinimalSceneTextures& SceneTextures = View.GetSceneTextures(); const TArray& ShadowInfos = VisibleLightInfos[LightSceneInfo->Id].AllProjectedShadows; float FadeAlpha = 1.0f; bool bIsWholeSceneDirectionalLightShadow = false; bool bIsWholeSceneLocalLightShadow = false; if (ShadowInfos.Num() > 0) { FadeAlpha = ShadowInfos[0]->FadeAlphas[ViewIndex]; bIsWholeSceneDirectionalLightShadow = ShadowInfos[0]->IsWholeSceneDirectionalShadow(); bIsWholeSceneLocalLightShadow = ShadowInfos[0]->IsWholeScenePointLightShadow(); } uint32 EncodingType = 0; if (bIsWholeSceneDirectionalLightShadow) { EncodingType = 1; } else if (bIsWholeSceneLocalLightShadow) { EncodingType = 2; } // 1. Build list of all the macrogroup affected by this light bool bHasDeepShadow = false; TArray AtlasSlotIndexData; AtlasSlotIndexData.Reserve(InMacroGroupDatas.Num()); float LayerDistribution = 0; FIntPoint AtlasResolution = FIntPoint::ZeroValue; if (!IsHairStrandsForVoxelTransmittanceAndShadowEnable(View.GetShaderPlatform())) { for (const FHairStrandsMacroGroupData& MacroGroupData : InMacroGroupDatas) { uint32 AtlasSlotIndex = 0; for (const FHairStrandsDeepShadowData& DomData : MacroGroupData.DeepShadowDatas) { if (DomData.LightId == LightSceneInfo->Id) { bHasDeepShadow = true; LayerDistribution = DomData.LayerDistribution; AtlasSlotIndexData.Add(DomData.AtlasSlotIndex); AtlasResolution = DomData.AtlasResolution; } } } } // 2. Render deep shadow mask if any if (bHasDeepShadow) { FRDGBufferRef AtlasSlotIndexBuffer = CreateStructuredBuffer(GraphBuilder, TEXT("Hair.DeepShadow.AtlasSlotIndexBuffer"), 4u, AtlasSlotIndexData.Num(), AtlasSlotIndexData.GetData(), 4u * AtlasSlotIndexData.Num()); FHairStrandsDeepShadowParams Params; Params.DeepShadow_AtlasSlotIndexBuffer = GraphBuilder.CreateSRV(AtlasSlotIndexBuffer); Params.DeepShadow_ViewInfoBuffer = GraphBuilder.CreateSRV(DeepShadowResources.DeepShadowViewInfoBuffer); Params.DeepShadow_AtlasResolution = AtlasResolution; Params.DeepShadow_FrontDepthTexture = DeepShadowResources.DepthAtlasTexture; Params.DeepShadow_LayerTexture = DeepShadowResources.LayersAtlasTexture; Params.DeepShadow_DepthBiasScale = GetDeepShadowDepthBiasScale(); Params.DeepShadow_DensityScale = GetDeepShadowDensityScale(); Params.DeepShadow_LayerDepths = ComputeDeepShadowLayerDepths(LayerDistribution); Params.OutputChannel = bProjectingForForwardShading ? LightSceneInfo->GetDynamicShadowMapChannel() : ~0; AddHairStrandsDeepShadowMaskPass( GraphBuilder, SceneTextures.Depth.Resolve, View, Params, FadeAlpha, EncodingType, AtlasSlotIndexData.Num() /* EffectiveAtlasSlotCount */, OutShadowMask); } // 3. If there is no deep shadow for this light, fallback on the voxel representation if (!bHasDeepShadow && HairStrands::HasViewHairStrandsVoxelData(View)) { AddHairStrandsVoxelShadowMaskPass( GraphBuilder, SceneTextures.Depth.Resolve, View, LightSceneInfo, FadeAlpha, EncodingType, bProjectingForForwardShading, OutShadowMask); } } void RenderHairStrandsShadowMask( FRDGBuilder& GraphBuilder, const TArray& Views, const FLightSceneInfo* LightSceneInfo, const TArrayView& VisibleLightInfos, const bool bProjectingForForwardShading, FRDGTextureRef OutShadowMask) { if (Views.Num() == 0 || OutShadowMask == nullptr) { return; } uint32 ViewIndex = 0; for (const FViewInfo& View : Views) { if (HairStrands::HasViewHairStrandsData(View)) { check(View.HairStrandsViewData.VisibilityData.CoverageTexture); InternalRenderHairStrandsShadowMask( GraphBuilder, View, ViewIndex++, LightSceneInfo, VisibleLightInfos, View.HairStrandsViewData.VisibilityData, View.HairStrandsViewData.MacroGroupDatas, View.HairStrandsViewData.DeepShadowResources, View.HairStrandsViewData.VirtualVoxelResources, bProjectingForForwardShading, OutShadowMask); } } } void RenderHairStrandsDeepShadowMask( FRDGBuilder& GraphBuilder, const TArray& Views, const FLightSceneInfo* LightSceneInfo, const TArrayView& VisibleLightInfos, FRDGTextureRef OutShadowMask) { if (Views.Num() == 0 || OutShadowMask == nullptr) { return; } // Render only light with deep shadow if (!LightSceneInfo || !LightSceneInfo->Proxy->CastsHairStrandsDeepShadow()) { return; } uint32 ViewIndex = 0; for (const FViewInfo& View : Views) { if (HairStrands::HasViewHairStrandsData(View)) { check(View.HairStrandsViewData.VisibilityData.CoverageTexture); InternalRenderHairStrandsShadowMask( GraphBuilder, View, ViewIndex++, LightSceneInfo, VisibleLightInfos, View.HairStrandsViewData.VisibilityData, View.HairStrandsViewData.MacroGroupDatas, View.HairStrandsViewData.DeepShadowResources, View.HairStrandsViewData.VirtualVoxelResources, false, OutShadowMask); } } }