// Copyright Epic Games, Inc. All Rights Reserved. #include "HairStrandsComposition.h" #include "Shader.h" #include "GlobalShader.h" #include "ScenePrivate.h" #include "ShaderParameters.h" #include "ShaderParameterStruct.h" #include "ShaderParameterMacros.h" #include "RenderGraphUtils.h" #include "PostProcess/PostProcessing.h" #include "PostProcess/SceneRenderTargets.h" #include "HairStrandsRendering.h" #include "HairStrandsTile.h" #include "FogRendering.h" #include "PostProcess/TemporalAA.h" #include "HairStrandsForwardRaster.h" ///////////////////////////////////////////////////////////////////////////////////////// DECLARE_GPU_STAT(HairStrandsComposition); static int32 GHairFastResolveVelocityThreshold = 1; static FAutoConsoleVariableRef CVarHairFastResolveVelocityThreshold(TEXT("r.HairStrands.VelocityThreshold"), GHairFastResolveVelocityThreshold, TEXT("Threshold value (in pixel) above which a pixel is forced to be resolve with responsive AA (in order to avoid smearing). Default is 3.")); static int32 GHairWriteGBufferData = 1; static FAutoConsoleVariableRef CVarHairWriteGBufferData(TEXT("r.HairStrands.WriteGBufferData"), GHairWriteGBufferData, TEXT("Write hair hair material data into GBuffer before post processing run. 0: no write, 1: dummy write into GBuffer A/B (Normal/ShadingModel), 2: write into GBuffer A/B (Normal/ShadingModel). 2: Write entire GBuffer data. (default 1).")); static int32 GHairStrandsHoldoutMode = 1; static FAutoConsoleVariableRef CVarHairStrandsHoldoutMode(TEXT("r.HairStrands.HoldoutMode"), GHairStrandsHoldoutMode, TEXT("Change how sample are merged when rendering with holdout.")); ///////////////////////////////////////////////////////////////////////////////////////// float GetHairFastResolveVelocityThreshold(const FIntPoint& Resolution) { FVector2f PixelVelocity(1.f / (Resolution.X * 2), 1.f / (Resolution.Y * 2)); const float VelocityThreshold = FMath::Clamp(GHairFastResolveVelocityThreshold, 0, 512) * FMath::Min(PixelVelocity.X, PixelVelocity.Y); return VelocityThreshold; } enum EHairStrandsCommonPassType { Composition, DOF, TAAFastResolve, GBuffer, Blit, HoldoutScene, HoldoutHair, }; template void InternalCommonDrawPass( FRDGBuilder& GraphBuilder, FRDGEventName&& EventName, const FViewInfo& View, const FIntPoint Resolution, const EHairStrandsCommonPassType Type, const bool bWriteDepth, const bool bHasHoldout, const FHairStrandsTiles& TileData, TPixelShader& PixelShader, TPassParameter* PassParamters) { //ClearUnusedGraphResources(PixelShader, PassParamters); const FIntRect Viewport = View.ViewRect; TShaderMapRef TileVertexShader(View.ShaderMap); const FHairStrandsTiles::ETileType TileType = FHairStrandsTiles::ETileType::HairAll; check(TileData.IsValid()); PassParamters->TileData = GetHairStrandsTileParameters(View, TileData, TileType); GraphBuilder.AddPass( Forward(EventName), PassParamters, ERDGPassFlags::Raster, [PassParamters, TileVertexShader, PixelShader, Viewport, Resolution, Type, bWriteDepth, bHasHoldout, TileType](FRDGAsyncTask, FRHICommandList& RHICmdList) { FHairStrandsTilePassVS::FParameters ParametersVS = PassParamters->TileData; FGraphicsPipelineStateInitializer GraphicsPSOInit; RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); if (Type == EHairStrandsCommonPassType::Composition) { if (bHasHoldout) { // EHairStrandsCommonPassType::Composition and bHasHoldout, only color is composed. The alpha value is composed into a separate pass. // Two modes: // * 0: the background pixels are *not* weighted by the hair coverage // * 1: the background pixels are weighted by the hair coverage if (GHairStrandsHoldoutMode == 0) { GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); } else { GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); } } else { // Alpha usage/output is controlled with r.PostProcessing.PropagateAlpha. The value are: // 0: disabled(default); // 1: enabled // // When enabled, the alpha value means: // 0: valid pixel // 1: invalid pixel (background) GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); } } else if (Type == EHairStrandsCommonPassType::Blit) //used to write data into the temporal input buffer { GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); } else if (Type == EHairStrandsCommonPassType::HoldoutScene) { GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); } else if (Type == EHairStrandsCommonPassType::HoldoutHair) { GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); } else { GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); } GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI(); if (Type == EHairStrandsCommonPassType::Composition || Type == EHairStrandsCommonPassType::HoldoutScene || Type == EHairStrandsCommonPassType::HoldoutHair) { GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState::GetRHI(); } else if (Type == EHairStrandsCommonPassType::DOF) { GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState::GetRHI(); } else if (Type == EHairStrandsCommonPassType::TAAFastResolve) { GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState< false, CF_Always, true, CF_Always, SO_Keep, SO_Keep, SO_Replace, false, CF_Always, SO_Keep, SO_Keep, SO_Keep, STENCIL_TEMPORAL_RESPONSIVE_AA_MASK, STENCIL_TEMPORAL_RESPONSIVE_AA_MASK>::GetRHI(); } else if ((Type == EHairStrandsCommonPassType::GBuffer) || (Type == EHairStrandsCommonPassType::Blit)) { if (bWriteDepth) { GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState::GetRHI(); } else { GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState::GetRHI(); } } GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI; GraphicsPSOInit.BoundShaderState.VertexShaderRHI = TileVertexShader.GetVertexShader(); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader(); GraphicsPSOInit.PrimitiveType = PassParamters->TileData.bRectPrimitive > 0 ? PT_RectList : PT_TriangleList; const uint32 StencilRef = (Type == EHairStrandsCommonPassType::TAAFastResolve) ? STENCIL_TEMPORAL_RESPONSIVE_AA_MASK : 0; SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, StencilRef); SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *PassParamters); RHICmdList.SetViewport(Viewport.Min.X, Viewport.Min.Y, 0.0f, Viewport.Max.X, Viewport.Max.Y, 1.0f); SetShaderParameters(RHICmdList, TileVertexShader, TileVertexShader.GetVertexShader(), ParametersVS); RHICmdList.SetStreamSource(0, nullptr, 0); RHICmdList.DrawPrimitiveIndirect(PassParamters->TileData.TileIndirectBuffer->GetRHI(), FHairStrandsTiles::GetIndirectDrawArgOffset(TileType)); }); } ///////////////////////////////////////////////////////////////////////////////////////// class FHairHoldoutPS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FHairHoldoutPS); SHADER_USE_PARAMETER_STRUCT(FHairHoldoutPS, FGlobalShader); using FPermutationDomain = TShaderPermutationDomain<>; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_STRUCT_INCLUDE(FHairStrandsTilePassVS::FParameters, TileData) SHADER_PARAMETER(FIntPoint, OutputResolution) SHADER_PARAMETER(uint32, bComposeDofDepth) SHADER_PARAMETER(uint32, PassType) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FHairStrandsViewUniformParameters, HairStrands) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FFogUniformParameters, FogStruct) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, HairDOFDepthTexture) 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.SetDefine(TEXT("SHADER_HOLDOUT"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FHairHoldoutPS, "/Engine/Private/HairStrands/HairStrandsComposition.usf", "HoldoutPS", SF_Pixel); static void AddHairHoldoutPass( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FHairStrandsVisibilityData& VisibilityData, const FRDGTextureRef& HairDOFDepthTexture, FRDGTextureRef& OutColorTexture, FRDGTextureRef& OutDepthTexture) { const bool bDOFEnable = HairDOFDepthTexture != nullptr ? 1 : 0; TRDGUniformBufferRef FogBuffer = CreateFogUniformBuffer(GraphBuilder, View); // Attenuate original scene's pixel alpha value { FHairHoldoutPS::FParameters* Parameters = GraphBuilder.AllocParameters(); Parameters->bComposeDofDepth = bDOFEnable ? 1 : 0; Parameters->PassType = 0; Parameters->HairDOFDepthTexture = bDOFEnable ? HairDOFDepthTexture : GSystemTextures.GetBlackDummy(GraphBuilder); Parameters->OutputResolution = OutColorTexture->Desc.Extent; Parameters->ViewUniformBuffer = View.ViewUniformBuffer; Parameters->HairStrands = View.HairStrandsViewData.UniformBuffer; Parameters->FogStruct = FogBuffer; Parameters->RenderTargets[0] = FRenderTargetBinding(OutColorTexture, ERenderTargetLoadAction::ELoad); Parameters->RenderTargets.DepthStencil = FDepthStencilBinding(OutDepthTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthWrite_StencilRead); FHairHoldoutPS::FPermutationDomain PermutationVector; TShaderMapRef PixelShader(View.ShaderMap, PermutationVector); InternalCommonDrawPass( GraphBuilder, RDG_EVENT_NAME("HairStrands::Holdout(Scene)"), View, OutColorTexture->Desc.Extent, EHairStrandsCommonPassType::HoldoutScene, false /*bWriteDepth*/, true /*bHasHoldout*/, VisibilityData.TileData, PixelShader, Parameters); } // Add hair's fog contribution { FHairHoldoutPS::FParameters* Parameters = GraphBuilder.AllocParameters(); Parameters->bComposeDofDepth = bDOFEnable ? 1 : 0; Parameters->PassType = 1; Parameters->HairDOFDepthTexture = bDOFEnable ? HairDOFDepthTexture : GSystemTextures.GetBlackDummy(GraphBuilder); Parameters->OutputResolution = OutColorTexture->Desc.Extent; Parameters->ViewUniformBuffer = View.ViewUniformBuffer; Parameters->HairStrands = View.HairStrandsViewData.UniformBuffer; Parameters->FogStruct = FogBuffer; Parameters->RenderTargets[0] = FRenderTargetBinding(OutColorTexture, ERenderTargetLoadAction::ELoad); Parameters->RenderTargets.DepthStencil = FDepthStencilBinding(OutDepthTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthWrite_StencilRead); FHairHoldoutPS::FPermutationDomain PermutationVector; TShaderMapRef PixelShader(View.ShaderMap, PermutationVector); InternalCommonDrawPass( GraphBuilder, RDG_EVENT_NAME("HairStrands::Holdout(Hair)"), View, OutColorTexture->Desc.Extent, EHairStrandsCommonPassType::HoldoutHair, false /*bWriteDepth*/, true /*bHasHoldout*/, VisibilityData.TileData, PixelShader, Parameters); } } ///////////////////////////////////////////////////////////////////////////////////////// class FHairVisibilityComposeSamplePS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FHairVisibilityComposeSamplePS); SHADER_USE_PARAMETER_STRUCT(FHairVisibilityComposeSamplePS, FGlobalShader); class FDebug : SHADER_PERMUTATION_BOOL("PERMUTATION_DEBUG"); using FPermutationDomain = TShaderPermutationDomain; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_STRUCT_INCLUDE(FHairStrandsTilePassVS::FParameters, TileData) SHADER_PARAMETER(FIntPoint, OutputResolution) SHADER_PARAMETER(uint32, bComposeDofDepth) SHADER_PARAMETER(uint32, bHasHoldout) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FHairStrandsViewUniformParameters, HairStrands) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, HairLightingSampleBuffer) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, HairDOFDepthTexture) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutMetaTexture) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FFogUniformParameters, FogStruct) 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.SetDefine(TEXT("SHADER_COMPOSE_SAMPLE"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FHairVisibilityComposeSamplePS, "/Engine/Private/HairStrands/HairStrandsComposition.usf", "ComposeSamplePS", SF_Pixel); static void AddHairVisibilityComposeSamplePass( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FHairStrandsVisibilityData& VisibilityData, const bool bHasHoldout, const FRDGTextureRef& HairDOFDepthTexture, const FRDGTextureRef& HairTemporalAccumulationTexture, FRDGTextureRef& OutColorTexture, FRDGTextureRef& OutDepthTexture) { check(VisibilityData.SampleLightingTexture); const bool bDOFEnable = HairDOFDepthTexture != nullptr ? 1 : 0; TRDGUniformBufferRef FogBuffer = CreateFogUniformBuffer(GraphBuilder, View); FHairVisibilityComposeSamplePS::FParameters* Parameters = GraphBuilder.AllocParameters(); Parameters->bComposeDofDepth = bDOFEnable ? 1 : 0; Parameters->HairLightingSampleBuffer = VisibilityData.SampleLightingTexture; Parameters->HairDOFDepthTexture = bDOFEnable ? HairDOFDepthTexture : GSystemTextures.GetBlackDummy(GraphBuilder); Parameters->OutputResolution = OutColorTexture->Desc.Extent; Parameters->ViewUniformBuffer = View.ViewUniformBuffer; Parameters->HairStrands = View.HairStrandsViewData.UniformBuffer; Parameters->FogStruct = FogBuffer; Parameters->bHasHoldout = bHasHoldout ? 1u : 0u; Parameters->RenderTargets[0] = FRenderTargetBinding(OutColorTexture, ERenderTargetLoadAction::ELoad); Parameters->RenderTargets.DepthStencil = FDepthStencilBinding(OutDepthTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthWrite_StencilRead); const bool bDebugComposition = View.Family->EngineShowFlags.LODColoration; FHairVisibilityComposeSamplePS::FPermutationDomain PermutationVector; PermutationVector.Set(bDebugComposition); TShaderMapRef PixelShader(View.ShaderMap, PermutationVector); InternalCommonDrawPass( GraphBuilder, RDG_EVENT_NAME("HairStrands::ComposeSample"), View, OutColorTexture->Desc.Extent, EHairStrandsCommonPassType::Composition, false /*bWriteDepth*/, bHasHoldout, VisibilityData.TileData, PixelShader, Parameters); } ///////////////////////////////////////////////////////////////////////////////////////// class FHairDOFDepthPS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FHairDOFDepthPS); SHADER_USE_PARAMETER_STRUCT(FHairDOFDepthPS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_STRUCT_INCLUDE(FHairStrandsTilePassVS::FParameters, TileData) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, HairLightingSampleBuffer) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneColorTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneDepthTexture) 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.SetDefine(TEXT("SHADER_DOFDEPTH"), 1); OutEnvironment.SetRenderTargetOutputFormat(0, PF_R32_FLOAT); } }; IMPLEMENT_GLOBAL_SHADER(FHairDOFDepthPS, "/Engine/Private/HairStrands/HairStrandsComposition.usf", "DOFDepthPS", SF_Pixel); static FRDGTextureRef AddHairDOFDepthPass( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FHairStrandsVisibilityData& VisibilityData, const FRDGTextureRef& CategorizationTexture, const FRDGTextureRef& InColorTexture, const FRDGTextureRef& InDepthTexture) { check(VisibilityData.SampleLightingTexture); FIntPoint OutputResolution = InColorTexture->Desc.Extent; FRDGTextureRef OutDOFDepthTexture = nullptr; { FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(OutputResolution, PF_R32_FLOAT, FClearValueBinding::Black, TexCreate_RenderTargetable | TexCreate_ShaderResource, 1); OutDOFDepthTexture = GraphBuilder.CreateTexture(Desc, TEXT("Hair.DOFDepth")); } FHairDOFDepthPS::FParameters* Parameters = GraphBuilder.AllocParameters(); Parameters->HairStrands = View.HairStrandsViewData.UniformBuffer; Parameters->HairLightingSampleBuffer = VisibilityData.SampleLightingTexture; Parameters->SceneColorTexture = InColorTexture; Parameters->SceneDepthTexture = InDepthTexture; Parameters->ViewUniformBuffer = View.ViewUniformBuffer; Parameters->RenderTargets[0] = FRenderTargetBinding(OutDOFDepthTexture, ERenderTargetLoadAction::ENoAction); TShaderMapRef PixelShader(View.ShaderMap); InternalCommonDrawPass( GraphBuilder, RDG_EVENT_NAME("HairStrands::DOFDepth"), View, OutputResolution, EHairStrandsCommonPassType::DOF, false /*bWriteDepth*/, false /*bHasHoldout*/, VisibilityData.TileData, PixelShader, Parameters); return OutDOFDepthTexture; } /////////////////////////////////////////////////////////////////////////////////////////////////// class FHairVisibilityFastResolveMaskPS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FHairVisibilityFastResolveMaskPS); SHADER_USE_PARAMETER_STRUCT(FHairVisibilityFastResolveMaskPS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FHairStrandsTilePassVS::FParameters, TileData) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ResolveMaskTexture) 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.SetDefine(TEXT("SHADER_FASTRESOLVE_MASK"), 1); OutEnvironment.SetRenderTargetOutputFormat(0, PF_R8G8B8A8); } }; IMPLEMENT_GLOBAL_SHADER(FHairVisibilityFastResolveMaskPS, "/Engine/Private/HairStrands/HairStrandsComposition.usf", "FastResolvePS", SF_Pixel); static void AddHairVisibilityFastResolveMaskPass( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FRDGTextureRef& HairResolveMaskTexture, const FHairStrandsTiles& TileData, FRDGTextureRef& OutDepthTexture) { const FIntPoint Resolution = OutDepthTexture->Desc.Extent; FRDGTextureRef DummyTexture; { FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(Resolution, PF_R8G8B8A8, FClearValueBinding::Black, TexCreate_RenderTargetable); Desc.NumSamples = OutDepthTexture->Desc.NumSamples; DummyTexture = GraphBuilder.CreateTexture(Desc, TEXT("Hair.DummyTexture")); } FHairVisibilityFastResolveMaskPS::FParameters* Parameters = GraphBuilder.AllocParameters(); Parameters->ResolveMaskTexture = HairResolveMaskTexture; Parameters->RenderTargets[0] = FRenderTargetBinding(DummyTexture, ERenderTargetLoadAction::ENoAction); Parameters->RenderTargets.DepthStencil = FDepthStencilBinding( OutDepthTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthRead_StencilWrite); TShaderMapRef PixelShader(View.ShaderMap); InternalCommonDrawPass( GraphBuilder, RDG_EVENT_NAME("HairStrands::MarkTAAFastResolve"), View, Resolution, EHairStrandsCommonPassType::TAAFastResolve, false /*bWriteDepth*/, false /*bHasHoldout*/, TileData, PixelShader, Parameters); } /////////////////////////////////////////////////////////////////////////////////////////////////// class FHairVisibilityGBufferWritePS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FHairVisibilityGBufferWritePS); SHADER_USE_PARAMETER_STRUCT(FHairVisibilityGBufferWritePS, FGlobalShader); class FOutputType : SHADER_PERMUTATION_INT("PERMUTATION_OUTPUT_TYPE", 2); using FPermutationDomain = TShaderPermutationDomain; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_STRUCT_INCLUDE(FHairStrandsTilePassVS::FParameters, TileData) SHADER_PARAMETER(uint32, bWriteDummyData) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FHairStrandsViewUniformParameters, HairStrands) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() public: static FPermutationDomain RemapPermutation(FPermutationDomain PermutationVector) { return PermutationVector; } 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.SetRenderTargetOutputFormat(1, PF_FloatRGBA); OutEnvironment.SetDefine(TEXT("SHADER_WRITE_GBUFFER"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FHairVisibilityGBufferWritePS, "/Engine/Private/HairStrands/HairStrandsComposition.usf", "MainPS", SF_Pixel); static void AddHairVisibilityGBufferWritePass( FRDGBuilder& GraphBuilder, const FViewInfo& View, const bool bWriteDummyData, const FHairStrandsTiles& TileData, FRDGTextureRef OutGBufferATexture, FRDGTextureRef OutGBufferBTexture, FRDGTextureRef OutGBufferCTexture, FRDGTextureRef OutGBufferDTexture, FRDGTextureRef OutGBufferETexture, FRDGTextureRef OutDepthTexture) { const bool bWriteFullGBuffer = OutGBufferCTexture != nullptr; const bool bWriteDepth = OutDepthTexture != nullptr; if (!OutGBufferATexture || !OutGBufferBTexture) { return; } if (bWriteFullGBuffer && (!OutGBufferCTexture || !OutDepthTexture)) { return; } FHairVisibilityGBufferWritePS::FParameters* Parameters = GraphBuilder.AllocParameters(); Parameters->ViewUniformBuffer = View.ViewUniformBuffer; Parameters->bWriteDummyData = bWriteDummyData ? 1 : 0; Parameters->HairStrands = View.HairStrandsViewData.UniformBuffer; Parameters->RenderTargets[0] = FRenderTargetBinding(OutGBufferATexture, ERenderTargetLoadAction::ELoad); Parameters->RenderTargets[1] = FRenderTargetBinding(OutGBufferBTexture, ERenderTargetLoadAction::ELoad); if (bWriteFullGBuffer) { Parameters->RenderTargets[2] = FRenderTargetBinding(OutGBufferCTexture, ERenderTargetLoadAction::ELoad); if (OutGBufferDTexture) { Parameters->RenderTargets[3] = FRenderTargetBinding(OutGBufferDTexture, ERenderTargetLoadAction::ELoad); } if (OutGBufferETexture) { Parameters->RenderTargets[4] = FRenderTargetBinding(OutGBufferETexture, ERenderTargetLoadAction::ELoad); } } if (bWriteDepth) { Parameters->RenderTargets.DepthStencil = FDepthStencilBinding( OutDepthTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthWrite_StencilNop); } FHairVisibilityGBufferWritePS::FPermutationDomain PermutationVector; PermutationVector.Set(bWriteFullGBuffer ? 1 : 0); TShaderMapRef PixelShader(View.ShaderMap, PermutationVector); InternalCommonDrawPass( GraphBuilder, RDG_EVENT_NAME("HairStrands::GBufferOverride"), View, OutGBufferATexture->Desc.Extent, EHairStrandsCommonPassType::GBuffer, bWriteDepth, false /*bHasHoldout*/, TileData, PixelShader, Parameters); } static void InternalRenderHairComposition( FRDGBuilder& GraphBuilder, const FViewInfo& View, FRDGTextureRef SceneColorTexture, FRDGTextureRef SceneDepthTexture, FRDGTextureRef SceneVelocityTexture) { RDG_EVENT_SCOPE_STAT(GraphBuilder, HairStrandsComposition, "HairStrandsComposition"); RDG_GPU_STAT_SCOPE(GraphBuilder, HairStrandsComposition); SCOPED_NAMED_EVENT(HairStrandsComposition, FColor::Emerald); if (View.HairStrandsViewData.VisibilityData.RasterizedInstanceCount > 0) { AddHairStrandsForwardRasterPass( GraphBuilder, View, SceneDepthTexture->Desc.Extent, View.HairStrandsViewData.VisibilityData, SceneDepthTexture, SceneColorTexture, SceneVelocityTexture); } else { const FHairStrandsVisibilityData& VisibilityData = View.HairStrandsViewData.VisibilityData; if (!VisibilityData.CoverageTexture) { return; // Automatically skip for any view not rendering hair } FRDGTextureRef DOFDepth = nullptr; if (View.FinalPostProcessSettings.DepthOfFieldUseHairDepth) { DOFDepth = AddHairDOFDepthPass( GraphBuilder, View, VisibilityData, VisibilityData.CoverageTexture, SceneColorTexture, SceneDepthTexture); } FRDGTextureRef AccumulatedColor = nullptr; const bool bHasHoldout = HasHairFlags(View.HairStrandsViewData.Flags, HAIR_FLAGS_HOLDOUT) && View.Family->EngineShowFlags.AllowPrimitiveAlphaHoldout; const bool bTemporalLayeringEnabled = false; // HAIR_TODO: remove AddHairVisibilityComposeSamplePass( GraphBuilder, View, VisibilityData, bHasHoldout, DOFDepth, AccumulatedColor, SceneColorTexture, SceneDepthTexture); if (bHasHoldout) { AddHairHoldoutPass( GraphBuilder, View, VisibilityData, DOFDepth, SceneColorTexture, SceneDepthTexture); } if (VisibilityData.ResolveMaskTexture) { AddHairVisibilityFastResolveMaskPass( GraphBuilder, View, VisibilityData.ResolveMaskTexture, VisibilityData.TileData, SceneDepthTexture); } const bool bWriteDummyData = View.Family->ViewMode != VMI_VisualizeBuffer && GHairWriteGBufferData == 1; const bool bWritePartialGBuffer = View.Family->ViewMode != VMI_VisualizeBuffer && (GHairWriteGBufferData == 1 || GHairWriteGBufferData == 2); const bool bWriteFullGBuffer = View.Family->ViewMode == VMI_VisualizeBuffer || (GHairWriteGBufferData == 3); if (bWriteFullGBuffer || bWritePartialGBuffer) { const FSceneTextures& SceneTextures = View.GetSceneTextures(); const FRDGTextureRef GBufferATexture = SceneTextures.GBufferA; const FRDGTextureRef GBufferBTexture = SceneTextures.GBufferB; const FRDGTextureRef GBufferCTexture = SceneTextures.GBufferC; const FRDGTextureRef GBufferDTexture = SceneTextures.GBufferD; const FRDGTextureRef GBufferETexture = SceneTextures.GBufferE; if (bWritePartialGBuffer && GBufferATexture && GBufferBTexture) { AddHairVisibilityGBufferWritePass( GraphBuilder, View, bWriteDummyData, VisibilityData.TileData, GBufferATexture, GBufferBTexture, nullptr, nullptr, nullptr, nullptr); } else if (bWriteFullGBuffer && GBufferATexture && GBufferBTexture && GBufferCTexture && SceneDepthTexture) { AddHairVisibilityGBufferWritePass( GraphBuilder, View, bWriteDummyData, VisibilityData.TileData, GBufferATexture, GBufferBTexture, GBufferCTexture, GBufferDTexture, GBufferETexture, SceneDepthTexture); } } } } void RenderHairComposition( FRDGBuilder& GraphBuilder, const TArray& Views, FRDGTextureRef SceneColorTexture, FRDGTextureRef SceneDepthTexture, FRDGTextureRef SceneVelocityTexture, FTranslucencyPassResourcesMap& TranslucencyResourceMap) { uint32 ViewIndex = 0; for (const FViewInfo& View : Views) { if (View.Family && HairStrands::HasViewHairStrandsData(View)) { FTranslucencyPassResources& TranslucencyPassResources = TranslucencyResourceMap.Get(ViewIndex, ETranslucencyPass::TPT_TranslucencyStandard); FRDGTextureRef SceneColorTarget = TranslucencyPassResources.ColorTexture.IsValid() ? TranslucencyPassResources.ColorTexture.Target : SceneColorTexture; InternalRenderHairComposition( GraphBuilder, View, SceneColorTarget, SceneDepthTexture, SceneVelocityTexture); } ViewIndex++; } } void RenderHairComposition( FRDGBuilder& GraphBuilder, const FViewInfo& View, FRDGTextureRef SceneColorTexture, FRDGTextureRef SceneDepthTexture, FRDGTextureRef SceneVelocityTexture) { if (View.Family && HairStrands::HasViewHairStrandsData(View)) { InternalRenderHairComposition( GraphBuilder, View, SceneColorTexture, SceneDepthTexture, SceneVelocityTexture); } }