diff --git a/03-UnrealEngine/卡通渲染相关资料/卡通渲染开发总览.md b/03-UnrealEngine/卡通渲染相关资料/卡通渲染开发总览.md index 6a3e9fa..35f81ca 100644 --- a/03-UnrealEngine/卡通渲染相关资料/卡通渲染开发总览.md +++ b/03-UnrealEngine/卡通渲染相关资料/卡通渲染开发总览.md @@ -132,6 +132,7 @@ rating: ⭐⭐⭐ 1. ![[星穹铁道中下巴阴影处理.png]] 2. [ ] ToonSDFShadow 1. [ ] TODO: SDF贴图工具? + 3. [ ] ShadowPass https://zhuanlan.zhihu.com/p/619718145 19. [ ] LookDev场景 1. [ ] https://zhuanlan.zhihu.com/p/394608910 20. [ ] 考虑往GBuffer中添加更多数据(考虑Velocity以及SingleLayerWater) @@ -144,12 +145,14 @@ rating: ⭐⭐⭐ 1. 【二次元人物眼睛如何变形?】 https://www.bilibili.com/video/BV14M4m1y71A/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e 1. 原视频 https://www.youtube.com/watch?v=euIyX9v8rvw 2. 眼睛建模 https://youtu.be/s2_7Q2IIvNY?si=fWiYjqcLFXzdeQ-B&t=126 -25. DX11问题修复 +25. 尝试实现Forward+ + 1. BasePass https://zhuanlan.zhihu.com/p/618698467 +26. DX11问题修复 1. [x] ToonOutline SceneColorTexture为空的问题。 **DX11限制,必须CopyTexture** -26. 卡通渲染针对TAA的优化思路 https://zhuanlan.zhihu.com/p/678876237 +27. 卡通渲染针对TAA的优化思路 https://zhuanlan.zhihu.com/p/678876237 1. https://www.bilibili.com/video/BV1BK411v7FY/?spm_id_from=333.788&vd_source=ea6df38502a795b7533aa33b78bf1159 2. https://zhuanlan.zhihu.com/p/20786650 -27. [ ] Unreal Engine 5.4 Scene Extension https://zhuanlan.zhihu.com/p/706268007 +28. [ ] Unreal Engine 5.4 Scene Extension https://zhuanlan.zhihu.com/p/706268007 1. [ ] 通过SceneExtension改进ToonObjectID,这样可以减少对应ToonBuffer的精度来存其他数据。 2. [ ] https://www.bilibili.com/video/BV1fM4m1U7Tp/ diff --git a/03-UnrealEngine/卡通渲染相关资料/渲染功能/ShaderModel/BasePass C++.md b/03-UnrealEngine/卡通渲染相关资料/渲染功能/ShaderModel/BasePass C++.md new file mode 100644 index 0000000..c1bdc95 --- /dev/null +++ b/03-UnrealEngine/卡通渲染相关资料/渲染功能/ShaderModel/BasePass C++.md @@ -0,0 +1,378 @@ +--- +title: Untitled +date: 2024-09-26 18:41:24 +excerpt: +tags: +rating: ⭐ +--- +# RenderBasePass() +传入RenderBasePass()的DepthStencil逻辑如下: +```c++ +const FExclusiveDepthStencil::Type BasePassDepthStencilAccess = + bAllowReadOnlyDepthBasePass + ? FExclusiveDepthStencil::DepthRead_StencilWrite + : FExclusiveDepthStencil::DepthWrite_StencilWrite; +``` + + +FDeferredShadingSceneRenderer::RenderBasePass() => +FDeferredShadingSceneRenderer::RenderBasePassInternal() => + + +FBasePassMeshProcessor::TryAddMeshBatch => + +## 大致流程 +1. 创建MRT并绑定、取得深度缓存。 +```c++ +const FExclusiveDepthStencil ExclusiveDepthStencil(BasePassDepthStencilAccess); + +TStaticArray BasePassTextures; +uint32 BasePassTextureCount = SceneTextures.GetGBufferRenderTargets(BasePassTextures); +Strata::AppendStrataMRTs(*this, BasePassTextureCount, BasePassTextures); +TArrayView BasePassTexturesView = MakeArrayView(BasePassTextures.GetData(), BasePassTextureCount); +FRDGTextureRef BasePassDepthTexture = SceneTextures.Depth.Target; +``` +2. GBuffer Clear +```c++ +GraphBuilder.AddPass(RDG_EVENT_NAME("GBufferClear"), PassParameters, ERDGPassFlags::Raster, + [PassParameters, ColorLoadAction, SceneColorClearValue](FRHICommandList& RHICmdList) + { + // If no fast-clear action was used, we need to do an MRT shader clear. + if (ColorLoadAction == ERenderTargetLoadAction::ENoAction) + { + const FRenderTargetBindingSlots& RenderTargets = PassParameters->RenderTargets; + FLinearColor ClearColors[MaxSimultaneousRenderTargets]; + FRHITexture* Textures[MaxSimultaneousRenderTargets]; + int32 TextureIndex = 0; + + RenderTargets.Enumerate([&](const FRenderTargetBinding& RenderTarget) + { + FRHITexture* TextureRHI = RenderTarget.GetTexture()->GetRHI(); + ClearColors[TextureIndex] = TextureIndex == 0 ? SceneColorClearValue : TextureRHI->GetClearColor(); + Textures[TextureIndex] = TextureRHI; + ++TextureIndex; + }); + + // Clear color only; depth-stencil is fast cleared. + DrawClearQuadMRT(RHICmdList, true, TextureIndex, ClearColors, false, 0, false, 0); + } + }); +``` +3. RenderTargetBindingSlots +```c++ +// Render targets bindings should remain constant at this point. +FRenderTargetBindingSlots BasePassRenderTargets = GetRenderTargetBindings(ERenderTargetLoadAction::ELoad, BasePassTexturesView); +BasePassRenderTargets.DepthStencil = FDepthStencilBinding(BasePassDepthTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, ExclusiveDepthStencil); +``` +4. RenderBasePassInternal() +5. RenderAnisotropyPass() + +# MeshDraw +## RenderBasePassInternal() +RenderNaniteBasePass()为一个Lambda,最终调用**Nanite::DrawBasePass()** 渲染Nanite物体的BasePass。其他相关渲染代码如下: +```c++ +SCOPE_CYCLE_COUNTER(STAT_BasePassDrawTime); + RDG_EVENT_SCOPE(GraphBuilder, "BasePass"); + RDG_GPU_STAT_SCOPE(GraphBuilder, Basepass); + + const bool bDrawSceneViewsInOneNanitePass = Views.Num() > 1 && Nanite::ShouldDrawSceneViewsInOneNanitePass(Views[0]); + if (bParallelBasePass)//并行渲染模式 + { + RDG_WAIT_FOR_TASKS_CONDITIONAL(GraphBuilder, IsBasePassWaitForTasksEnabled()); + + for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex) + { + FViewInfo& View = Views[ViewIndex]; + RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask); + RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex); + View.BeginRenderView(); + + const bool bLumenGIEnabled = GetViewPipelineState(View).DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen; + + FMeshPassProcessorRenderState DrawRenderState; + SetupBasePassState(BasePassDepthStencilAccess, ViewFamily.EngineShowFlags.ShaderComplexity, DrawRenderState); + + FOpaqueBasePassParameters* PassParameters = GraphBuilder.AllocParameters(); + PassParameters->View = View.GetShaderParameters(); + PassParameters->ReflectionCapture = View.ReflectionCaptureUniformBuffer; + PassParameters->BasePass = CreateOpaqueBasePassUniformBuffer(GraphBuilder, View, ViewIndex, ForwardBasePassTextures, DBufferTextures, bLumenGIEnabled); + PassParameters->RenderTargets = BasePassRenderTargets; + PassParameters->RenderTargets.ShadingRateTexture = GVRSImageManager.GetVariableRateShadingImage(GraphBuilder, View, FVariableRateShadingImageManager::EVRSPassType::BasePass); + + const bool bShouldRenderView = View.ShouldRenderView(); + if (bShouldRenderView) + { + View.ParallelMeshDrawCommandPasses[EMeshPass::BasePass].BuildRenderingCommands(GraphBuilder, Scene->GPUScene, PassParameters->InstanceCullingDrawParams); + + GraphBuilder.AddPass( + RDG_EVENT_NAME("BasePassParallel"), + PassParameters, + ERDGPassFlags::Raster | ERDGPassFlags::SkipRenderPass, + [this, &View, PassParameters](const FRDGPass* InPass, FRHICommandListImmediate& RHICmdList) + { + FRDGParallelCommandListSet ParallelCommandListSet(InPass, RHICmdList, GET_STATID(STAT_CLP_BasePass), View, FParallelCommandListBindings(PassParameters)); + View.ParallelMeshDrawCommandPasses[EMeshPass::BasePass].DispatchDraw(&ParallelCommandListSet, RHICmdList, &PassParameters->InstanceCullingDrawParams); + }); + } + + const bool bShouldRenderViewForNanite = bNaniteEnabled && (!bDrawSceneViewsInOneNanitePass || ViewIndex == 0); // when bDrawSceneViewsInOneNanitePass, the first view should cover all the other atlased ones + if (bShouldRenderViewForNanite) + { + // Should always have a full Z prepass with Nanite + check(ShouldRenderPrePass()); + //渲染Nanite物体BasePass + RenderNaniteBasePass(View, ViewIndex); + } + + //渲染编辑器相关的图元物体 + RenderEditorPrimitives(GraphBuilder, PassParameters, View, DrawRenderState, InstanceCullingManager); + + //渲染大气 + if (bShouldRenderView && View.Family->EngineShowFlags.Atmosphere) + { + FOpaqueBasePassParameters* SkyPassPassParameters = GraphBuilder.AllocParameters(); + SkyPassPassParameters->BasePass = PassParameters->BasePass; + SkyPassPassParameters->RenderTargets = BasePassRenderTargets; + SkyPassPassParameters->View = View.GetShaderParameters(); + SkyPassPassParameters->ReflectionCapture = View.ReflectionCaptureUniformBuffer; + + View.ParallelMeshDrawCommandPasses[EMeshPass::SkyPass].BuildRenderingCommands(GraphBuilder, Scene->GPUScene, SkyPassPassParameters->InstanceCullingDrawParams); + + GraphBuilder.AddPass( + RDG_EVENT_NAME("SkyPassParallel"), + SkyPassPassParameters, + ERDGPassFlags::Raster | ERDGPassFlags::SkipRenderPass, + [this, &View, SkyPassPassParameters](const FRDGPass* InPass, FRHICommandListImmediate& RHICmdList) + { + FRDGParallelCommandListSet ParallelCommandListSet(InPass, RHICmdList, GET_STATID(STAT_CLP_BasePass), View, FParallelCommandListBindings(SkyPassPassParameters)); + View.ParallelMeshDrawCommandPasses[EMeshPass::SkyPass].DispatchDraw(&ParallelCommandListSet, RHICmdList, &SkyPassPassParameters->InstanceCullingDrawParams); + }); + } + } + } + else + { + for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex) + { + FViewInfo& View = Views[ViewIndex]; + RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask); + RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex); + View.BeginRenderView(); + + const bool bLumenGIEnabled = GetViewPipelineState(View).DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen; + + FMeshPassProcessorRenderState DrawRenderState; + SetupBasePassState(BasePassDepthStencilAccess, ViewFamily.EngineShowFlags.ShaderComplexity, DrawRenderState); + + FOpaqueBasePassParameters* PassParameters = GraphBuilder.AllocParameters(); + PassParameters->View = View.GetShaderParameters(); + PassParameters->ReflectionCapture = View.ReflectionCaptureUniformBuffer; + PassParameters->BasePass = CreateOpaqueBasePassUniformBuffer(GraphBuilder, View, ViewIndex, ForwardBasePassTextures, DBufferTextures, bLumenGIEnabled); + PassParameters->RenderTargets = BasePassRenderTargets; + PassParameters->RenderTargets.ShadingRateTexture = GVRSImageManager.GetVariableRateShadingImage(GraphBuilder, View, FVariableRateShadingImageManager::EVRSPassType::BasePass); + + const bool bShouldRenderView = View.ShouldRenderView(); + if (bShouldRenderView) + { + View.ParallelMeshDrawCommandPasses[EMeshPass::BasePass].BuildRenderingCommands(GraphBuilder, Scene->GPUScene, PassParameters->InstanceCullingDrawParams); + + GraphBuilder.AddPass( + RDG_EVENT_NAME("BasePass"), + PassParameters, + ERDGPassFlags::Raster, + [this, &View, PassParameters](FRHICommandList& RHICmdList) + { + SetStereoViewport(RHICmdList, View, 1.0f); + View.ParallelMeshDrawCommandPasses[EMeshPass::BasePass].DispatchDraw(nullptr, RHICmdList, &PassParameters->InstanceCullingDrawParams); + } + ); + } + + const bool bShouldRenderViewForNanite = bNaniteEnabled && (!bDrawSceneViewsInOneNanitePass || ViewIndex == 0); // when bDrawSceneViewsInOneNanitePass, the first view should cover all the other atlased ones + if (bShouldRenderViewForNanite) + { + // Should always have a full Z prepass with Nanite + check(ShouldRenderPrePass()); + + RenderNaniteBasePass(View, ViewIndex); + } + + RenderEditorPrimitives(GraphBuilder, PassParameters, View, DrawRenderState, InstanceCullingManager); + + if (bShouldRenderView && View.Family->EngineShowFlags.Atmosphere) + { + FOpaqueBasePassParameters* SkyPassParameters = GraphBuilder.AllocParameters(); + SkyPassParameters->BasePass = PassParameters->BasePass; + SkyPassParameters->RenderTargets = BasePassRenderTargets; + SkyPassParameters->View = View.GetShaderParameters(); + SkyPassParameters->ReflectionCapture = View.ReflectionCaptureUniformBuffer; + + View.ParallelMeshDrawCommandPasses[EMeshPass::SkyPass].BuildRenderingCommands(GraphBuilder, Scene->GPUScene, SkyPassParameters->InstanceCullingDrawParams); + + GraphBuilder.AddPass( + RDG_EVENT_NAME("SkyPass"), + SkyPassParameters, + ERDGPassFlags::Raster, + [this, &View, SkyPassParameters](FRHICommandList& RHICmdList) + { + SetStereoViewport(RHICmdList, View, 1.0f); + View.ParallelMeshDrawCommandPasses[EMeshPass::SkyPass].DispatchDraw(nullptr, RHICmdList, &SkyPassParameters->InstanceCullingDrawParams); + } + ); + } + } + } +``` + +### SetDepthStencilStateForBasePass() +```c++ +void SetDepthStencilStateForBasePass( + FMeshPassProcessorRenderState& DrawRenderState, + ERHIFeatureLevel::Type FeatureLevel, + bool bDitheredLODTransition, + const FMaterial& MaterialResource, + bool bEnableReceiveDecalOutput, + bool bForceEnableStencilDitherState) +{ + const bool bMaskedInEarlyPass = (MaterialResource.IsMasked() || bDitheredLODTransition) && MaskedInEarlyPass(GShaderPlatformForFeatureLevel[FeatureLevel]); + if (bEnableReceiveDecalOutput) + { + if (bMaskedInEarlyPass) + { + SetDepthStencilStateForBasePass_Internal(DrawRenderState, FeatureLevel); + } + else if (DrawRenderState.GetDepthStencilAccess() & FExclusiveDepthStencil::DepthWrite) + { + SetDepthStencilStateForBasePass_Internal(DrawRenderState, FeatureLevel); + } + else + { + SetDepthStencilStateForBasePass_Internal(DrawRenderState, FeatureLevel); + } + } + else if (bMaskedInEarlyPass) + { + DrawRenderState.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); + } + + if (bForceEnableStencilDitherState) + { + SetDepthStencilStateForBasePass_Internal(DrawRenderState, FeatureLevel); + } +} +``` +## AnisotropyPass +Anisotropy的RT设置: +- RenderTarget:SceneTextures.GBufferF。 +- DepthStencil:SceneTextures.Depth.Target。**ERenderTargetLoadAction::ELoad**、**FExclusiveDepthStencil::DepthRead_StencilNop** + +### 管线状态 +在FAnisotropyMeshProcessor::CollectPSOInitializers()中: +```c++ +ETextureCreateFlags GBufferFCreateFlags; +EPixelFormat GBufferFPixelFormat = FSceneTextures::GetGBufferFFormatAndCreateFlags(GBufferFCreateFlags); +AddRenderTargetInfo(GBufferFPixelFormat, GBufferFCreateFlags, RenderTargetsInfo); +SetupDepthStencilInfo(PF_DepthStencil, SceneTexturesConfig.DepthCreateFlags, ERenderTargetLoadAction::ELoad, + ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthRead_StencilNop, RenderTargetsInfo); +``` + +```c++ +void SetupDepthStencilInfo( + EPixelFormat DepthStencilFormat, + ETextureCreateFlags DepthStencilCreateFlags, + ERenderTargetLoadAction DepthTargetLoadAction, + ERenderTargetLoadAction StencilTargetLoadAction, + FExclusiveDepthStencil DepthStencilAccess, + FGraphicsPipelineRenderTargetsInfo& RenderTargetsInfo) +{ + // Setup depth stencil state + RenderTargetsInfo.DepthStencilTargetFormat = DepthStencilFormat; + RenderTargetsInfo.DepthStencilTargetFlag = DepthStencilCreateFlags; + + RenderTargetsInfo.DepthTargetLoadAction = DepthTargetLoadAction; + RenderTargetsInfo.StencilTargetLoadAction = StencilTargetLoadAction; + RenderTargetsInfo.DepthStencilAccess = DepthStencilAccess; + + const ERenderTargetStoreAction StoreAction = EnumHasAnyFlags(RenderTargetsInfo.DepthStencilTargetFlag, TexCreate_Memoryless) ? ERenderTargetStoreAction::ENoAction : ERenderTargetStoreAction::EStore; + RenderTargetsInfo.DepthTargetStoreAction = RenderTargetsInfo.DepthStencilAccess.IsUsingDepth() ? StoreAction : ERenderTargetStoreAction::ENoAction; + RenderTargetsInfo.StencilTargetStoreAction = RenderTargetsInfo.DepthStencilAccess.IsUsingStencil() ? StoreAction : ERenderTargetStoreAction::ENoAction; +} +``` + +### ParallelRendering +AnisotropyPass支持并行渲染,并行渲染的判断逻辑为 +```c++ +const bool bEnableParallelBasePasses = GRHICommandList.UseParallelAlgorithms() && CVarParallelBasePass.GetValueOnRenderThread(); +``` +看得出判断条件是: +1. 显卡是否支持并行渲染。 +2. CVar(r.ParallelBasePass)是否开启并行渲染。 + +从AnisotropyPass可以看得出并行渲染与一般渲染的差别在于: +1. FRenderTargetBinding绑定时的ERenderTargetLoadAction不同,**并行为ELoad**;**普通渲染为EClear**。 +2. 调用AddPass添加了**ERDGPassFlags::SkipRenderPass**标记。 +3. 并行渲染会在AddPass中构建**FRDGParallelCommandListSet ParallelCommandListSet**,并作为传入**DispatchDraw**;普通渲染传递nullptr。 +4. 普通渲染会额外调用**SetStereoViewport(RHICmdList, View);**,本质是调用RHICmdList.SetViewport来设置View。 + +### Code +```c++ +RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, RenderAnisotropyPass); +SCOPED_NAMED_EVENT(FDeferredShadingSceneRenderer_RenderAnisotropyPass, FColor::Emerald); +SCOPE_CYCLE_COUNTER(STAT_AnisotropyPassDrawTime); +RDG_GPU_STAT_SCOPE(GraphBuilder, RenderAnisotropyPass); + +for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) +{ + FViewInfo& View = Views[ViewIndex]; + + if (View.ShouldRenderView()) + { + FParallelMeshDrawCommandPass& ParallelMeshPass = View.ParallelMeshDrawCommandPasses[EMeshPass::AnisotropyPass]; + + if (!ParallelMeshPass.HasAnyDraw()) + { + continue; + } + + View.BeginRenderView(); + + auto* PassParameters = GraphBuilder.AllocParameters(); + PassParameters->View = View.GetShaderParameters(); + PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(SceneTextures.Depth.Target, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthRead_StencilNop); + + ParallelMeshPass.BuildRenderingCommands(GraphBuilder, Scene->GPUScene, PassParameters->InstanceCullingDrawParams); + if (bDoParallelPass) + { + AddClearRenderTargetPass(GraphBuilder, SceneTextures.GBufferF); + + PassParameters->RenderTargets[0] = FRenderTargetBinding(SceneTextures.GBufferF, ERenderTargetLoadAction::ELoad); + + GraphBuilder.AddPass( + RDG_EVENT_NAME("AnisotropyPassParallel"), + PassParameters, + ERDGPassFlags::Raster | ERDGPassFlags::SkipRenderPass, + [this, &View, &ParallelMeshPass, PassParameters](const FRDGPass* InPass, FRHICommandListImmediate& RHICmdList) + { + FRDGParallelCommandListSet ParallelCommandListSet(InPass, RHICmdList, GET_STATID(STAT_CLP_AnisotropyPass), View, FParallelCommandListBindings(PassParameters)); + + ParallelMeshPass.DispatchDraw(&ParallelCommandListSet, RHICmdList, &PassParameters->InstanceCullingDrawParams); + }); + } + else + { + PassParameters->RenderTargets[0] = FRenderTargetBinding(SceneTextures.GBufferF, ERenderTargetLoadAction::EClear); + + GraphBuilder.AddPass( + RDG_EVENT_NAME("AnisotropyPass"), + PassParameters, + ERDGPassFlags::Raster, + [this, &View, &ParallelMeshPass, PassParameters](FRHICommandList& RHICmdList) + { + SetStereoViewport(RHICmdList, View); + + ParallelMeshPass.DispatchDraw(nullptr, RHICmdList, &PassParameters->InstanceCullingDrawParams); + }); + } + } +} +``` \ No newline at end of file diff --git a/03-UnrealEngine/卡通渲染相关资料/渲染功能/ShaderModel/GBuffer&Material&BasePass.md b/03-UnrealEngine/卡通渲染相关资料/渲染功能/ShaderModel/GBuffer&Material&BasePass.md index 61641e1..be5d9a8 100644 --- a/03-UnrealEngine/卡通渲染相关资料/渲染功能/ShaderModel/GBuffer&Material&BasePass.md +++ b/03-UnrealEngine/卡通渲染相关资料/渲染功能/ShaderModel/GBuffer&Material&BasePass.md @@ -32,6 +32,7 @@ OutGBufferD = GBuffer.CustomData OutGBufferE = GBuffer.PrecomputedShadowFactors (GBT_Unorm_8_8_8_8) TargetVelocity / OutGBufferF = velocity / tangent (默认不开启 带有深度<开启Lumen与距离场 或者 开启光线追踪> GBC_Raw_Float_16_16_16_16 不带深度 GBC_Raw_Float_16_16) TargetSeparatedMainDirLight = SingleLayerWater相关 (有SingleLayerWater才会开启 GBC_Raw_Float_11_11_10) +OutGBufferF = Anisotropy // 0..1, 2 bits, use CastContactShadow(GBuffer) or HasDynamicIndirectShadowCasterRepresentation(GBuffer) to extract half PerObjectGBufferData;