diff --git a/.obsidian/plugins/various-complements/data.json b/.obsidian/plugins/various-complements/data.json index 0f72d01..acd5c1a 100644 --- a/.obsidian/plugins/various-complements/data.json +++ b/.obsidian/plugins/various-complements/data.json @@ -142,6 +142,22 @@ "lastUpdated": 1707302668655 } } + }, + "BasePassDepthStencilAccess": { + "BasePassDepthStencilAccess": { + "currentFile": { + "count": 1, + "lastUpdated": 1707384942861 + } + } + }, + "ush:此模块定义了一些BasePass通用的结构体、宏。": { + "ush:此模块定义了一些BasePass通用的结构体、宏。": { + "currentFile": { + "count": 1, + "lastUpdated": 1707386581416 + } + } } } } \ No newline at end of file diff --git a/03-UnrealEngine/Rendering/RenderingPipeline/向往渲染系列文章阅读笔记/剖析虚幻渲染体系(04)- 延迟渲染管线.md b/03-UnrealEngine/Rendering/RenderingPipeline/向往渲染系列文章阅读笔记/剖析虚幻渲染体系(04)- 延迟渲染管线.md index d0e191b..e1908ec 100644 --- a/03-UnrealEngine/Rendering/RenderingPipeline/向往渲染系列文章阅读笔记/剖析虚幻渲染体系(04)- 延迟渲染管线.md +++ b/03-UnrealEngine/Rendering/RenderingPipeline/向往渲染系列文章阅读笔记/剖析虚幻渲染体系(04)- 延迟渲染管线.md @@ -1406,6 +1406,547 @@ void FSceneViewState::OnStartRender(FViewInfo& View, FSceneViewFamily& ViewFamil ``` ### PrePass -PrePass又被称为提前深度Pass、Depth Only Pass、Early-Z Pass,用来渲染不透明物体的深度。此Pass只会写入深度而不会写入颜色,写入深度时有disabled、occlusion only、complete depths三种模式,视不同的平台和Feature Level决定。 +PrePass又被称为**提前深度Pass**、**Depth Only Pass**、**Early-Z Pass**,**用来渲染不透明物体的深度**。此Pass只会写入深度而不会写入颜色,写入深度时有disabled、occlusion only、complete depths三种模式,视不同的平台和Feature Level决定。 -PrePass可以由DBuffer发起,也可由Forward Shading触发,通常用来建立Hierarchical-Z,以便能够开启硬件的Early-Z技术,还可被用于遮挡剔除,提升Base Pass的渲染效率。 \ No newline at end of file +**PrePass可以由DBuffer发起,也可由Forward Shading触发,通常用来建立Hierarchical-Z,以便能够开启硬件的Early-Z技术,还可被用于遮挡剔除,提升Base Pass的渲染效率。** PrePass在`FDeferredShadingSceneRenderer::Render`: +```c++ +void FDeferredShadingSceneRenderer::Render(FRHICommandListImmediate& RHICmdList) +{ + Scene->UpdateAllPrimitiveSceneInfos(RHICmdList, true); + (......) + InitViews(...); + (......) + UpdateGPUScene(RHICmdList, *Scene); + (......) + + // 判断是否需要PrePass. + const bool bNeedsPrePass = NeedsPrePass(this); + + // The Z-prepass + (......) + + if (bNeedsPrePass) + { + // 绘制场景深度, 构建深度缓冲和层级Z缓冲(HiZ). + bDepthWasCleared = RenderPrePass(RHICmdList, AfterTasksAreStarted); + } + + (......) + // Z-Prepass End +} +``` +开启PrePass需要满足以下两个条件: +- 非硬件Tiled的GPU。现代移动端GPU通常自带Tiled,且是TBDR架构,已经在GPU层做了Early-Z,无需再显式绘制。 +- 指定了有效的EarlyZPassMode或者渲染器的bEarlyZPassMovable不为0。 +```c++ +// Engine\Source\Runtime\Renderer\Private\DepthRendering.cpp + +bool FDeferredShadingSceneRenderer::RenderPrePass(FRHICommandListImmediate& RHICmdList, TFunctionRef AfterTasksAreStarted) +{ + bool bDepthWasCleared = false; + + (......) + + bool bDidPrePre = false; + FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList); + + bool bParallel = GRHICommandList.UseParallelAlgorithms() && CVarParallelPrePass.GetValueOnRenderThread(); + + // 非并行模式. + if (!bParallel) + { + AfterTasksAreStarted(); + bDepthWasCleared = PreRenderPrePass(RHICmdList); + bDidPrePre = true; + SceneContext.BeginRenderingPrePass(RHICmdList, false); + } + else // 并行模式 + { + // 分配深度缓冲. + SceneContext.GetSceneDepthSurface(); + } + + // Draw a depth pass to avoid overdraw in the other passes. + if(EarlyZPassMode != DDM_None) + { + const bool bWaitForTasks = bParallel && (CVarRHICmdFlushRenderThreadTasksPrePass.GetValueOnRenderThread() > 0 || CVarRHICmdFlushRenderThreadTasks.GetValueOnRenderThread() > 0); + FScopedCommandListWaitForTasks Flusher(bWaitForTasks, RHICmdList); + + // 每个view都绘制一遍深度缓冲. + for(int32 ViewIndex = 0;ViewIndex < Views.Num();ViewIndex++) + { + const FViewInfo& View = Views[ViewIndex]; + + // 创建和设置Uniform Buffer. + TUniformBufferRef PassUniformBuffer; + CreateDepthPassUniformBuffer(RHICmdList, View, PassUniformBuffer); + + // 处理渲染状态. + FMeshPassProcessorRenderState DrawRenderState(View, PassUniformBuffer); + + SetupDepthPassState(DrawRenderState); + + if (View.ShouldRenderView()) + { + Scene->UniformBuffers.UpdateViewUniformBuffer(View); + + // 并行模式 + if (bParallel) + { + check(RHICmdList.IsOutsideRenderPass()); + bDepthWasCleared = RenderPrePassViewParallel(View, RHICmdList, DrawRenderState, AfterTasksAreStarted, !bDidPrePre) || bDepthWasCleared; + bDidPrePre = true; + } + else + { + RenderPrePassView(RHICmdList, View, DrawRenderState); + } + } + + // Parallel rendering has self contained renderpasses so we need a new one for editor primitives. + if (bParallel) + { + SceneContext.BeginRenderingPrePass(RHICmdList, false); + } + RenderPrePassEditorPrimitives(RHICmdList, View, DrawRenderState, EarlyZPassMode, true); + if (bParallel) + { + RHICmdList.EndRenderPass(); + } + } + } + + (......) + + if (bParallel) + { + // In parallel mode there will be no renderpass here. Need to restart. + SceneContext.BeginRenderingPrePass(RHICmdList, false); + } + + (......) + + SceneContext.FinishRenderingPrePass(RHICmdList); + + return bDepthWasCleared; +} +``` +PrePass的绘制流程跟上一篇解析的FMeshProcessor和Pass绘制类似,此处不再重复解析。不过这里可以重点看看PrePass的渲染状态: +```c++ +void SetupDepthPassState(FMeshPassProcessorRenderState& DrawRenderState) +{ + // 禁止写入颜色, 开启深度测试和写入, 深度比较函数是更近或相等. + DrawRenderState.SetBlendState(TStaticBlendState::GetRHI()); + DrawRenderState.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); +} +``` +另外,绘制深度时,由于不需要写入颜色,那么渲染物体时使用的材质肯定不应该是物体本身的材质,而是某种简单的材质。为了验证猜想,进入`FDepthPassMeshProcessor`一探Depth Pass使用的材质: +```c++ +// Engine\Source\Runtime\Renderer\Private\DepthRendering.cpp + +void FDepthPassMeshProcessor::AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId) +{ + (......) + + if (bDraw) + { + (......) + + // 获取Surface材质域的默认材质, 作为深度Pass的渲染材质. + const FMaterialRenderProxy& DefaultProxy = *UMaterial::GetDefaultMaterial(MD_Surface)->GetRenderProxy(); + const FMaterial& DefaultMaterial = *DefaultProxy.GetMaterial(FeatureLevel); + Process(MeshBatch, BatchElementMask, StaticMeshId, BlendMode, PrimitiveSceneProxy, DefaultProxy, DefaultMaterial, MeshFillMode, MeshCullMode); + + (......) + } +} +``` + +```c++ +// Engine\Source\Runtime\Engine\Private\Materials\Material.cpp + +UMaterial* UMaterial::GetDefaultMaterial(EMaterialDomain Domain) +{ + InitDefaultMaterials(); + + UMaterial* Default = GDefaultMaterials[Domain]; + return Default; +} + +void UMaterialInterface::InitDefaultMaterials() +{ + static bool bInitialized = false; + if (!bInitialized) + { + (......) + + for (int32 Domain = 0; Domain < MD_MAX; ++Domain) + { + if (GDefaultMaterials[Domain] == nullptr) + { + FString ResolvedPath = ResolveIniObjectsReference(GDefaultMaterialNames[Domain]); + + GDefaultMaterials[Domain] = FindObject(nullptr, *ResolvedPath); + if (GDefaultMaterials[Domain] == nullptr) + { + GDefaultMaterials[Domain] = LoadObject(nullptr, *ResolvedPath, nullptr, LOAD_DisableDependencyPreloading, nullptr); + } + if (GDefaultMaterials[Domain]) + { + GDefaultMaterials[Domain]->AddToRoot(); + } + } + } + + (......) + } +} +``` +由上面可知,材质系统的默认材质由`GDefaultMaterialNames`指明,转到其声明: +```c++ +static const TCHAR* GDefaultMaterialNames[MD_MAX] = +{ + // Surface + TEXT("engine-ini:/Script/Engine.Engine.DefaultMaterialName"), + // Deferred Decal + TEXT("engine-ini:/Script/Engine.Engine.DefaultDeferredDecalMaterialName"), + // Light Function + TEXT("engine-ini:/Script/Engine.Engine.DefaultLightFunctionMaterialName"), + // Volume + //@todo - get a real MD_Volume default material + TEXT("engine-ini:/Script/Engine.Engine.DefaultMaterialName"), + // Post Process + TEXT("engine-ini:/Script/Engine.Engine.DefaultPostProcessMaterialName"), + // User Interface + TEXT("engine-ini:/Script/Engine.Engine.DefaultMaterialName"), + // Virtual Texture + TEXT("engine-ini:/Script/Engine.Engine.DefaultMaterialName"), +}; +``` +最终发现默认材质为`ResolvedPath = L"/Engine/EngineMaterials/WorldGridMaterial.WorldGridMaterial"` + +>**非常值得一提的是:WorldGridMaterial使用的Shading Model是Default Lit,材质中也存在冗余的节点。如果想要做极致的优化,建议在配置文件中更改此材质,删除冗余的材质节点,改成Unlit模式更佳,以最大化地缩减shader指令,提升渲染效率。** + +还值得一提的是,绘制深度Pass时可以指定深度绘制模式: +```c++ +// Engine\Source\Runtime\Renderer\Private\DepthRendering.h +enum EDepthDrawingMode +{ + // 不绘制深度 + DDM_None = 0, + // 只绘制Opaque材质(不包含Masked材质) + DDM_NonMaskedOnly = 1, + // Opaque和Masked材质, 但不包含关闭了bUseAsOccluder的物体. + DDM_AllOccluders = 2, + // 全部不透明物体模式, 所有物体需绘制, 且每个像素都要匹配Base Pass的深度. + DDM_AllOpaque = 3, + // 仅Masked模式. + DDM_MaskedOnly = 4, +}; +``` +具体是如何决定深度绘制模式的,由下面的接口决定: +```c++ +// Engine\Source\Runtime\Renderer\Private\RendererScene.cpp + +void FScene::UpdateEarlyZPassMode() +{ + DefaultBasePassDepthStencilAccess = FExclusiveDepthStencil::DepthWrite_StencilWrite; + EarlyZPassMode = DDM_NonMaskedOnly; // 默认只绘制Opaque材质. + bEarlyZPassMovable = false; + + // 延迟渲染管线下的深度策略 + if (GetShadingPath(GetFeatureLevel()) == EShadingPath::Deferred) + { + // 由命令行重写, 也可由工程设置中指定. + { + const int32 CVarValue = CVarEarlyZPass.GetValueOnAnyThread(); + + switch (CVarValue) + { + case 0: EarlyZPassMode = DDM_None; break; + case 1: EarlyZPassMode = DDM_NonMaskedOnly; break; + case 2: EarlyZPassMode = DDM_AllOccluders; break; + case 3: break; // Note: 3 indicates "default behavior" and does not specify an override + } + } + + const EShaderPlatform ShaderPlatform = GetFeatureLevelShaderPlatform(FeatureLevel); + if (ShouldForceFullDepthPass(ShaderPlatform)) + { + // DBuffer贴图和模板LOD抖动强制全部模式. + EarlyZPassMode = DDM_AllOpaque; + bEarlyZPassMovable = true; + } + + if (EarlyZPassMode == DDM_AllOpaque + && CVarBasePassWriteDepthEvenWithFullPrepass.GetValueOnAnyThread() == 0) + { + DefaultBasePassDepthStencilAccess = FExclusiveDepthStencil::DepthRead_StencilWrite; + } + } + + (......) +} +``` + +### BasePass +UE的BasePass就是延迟渲染里的几何通道,用来渲染不透明物体的几何信息,包含法线、深度、颜色、AO、粗糙度、金属度等等,这些几何信息会写入若干张GBuffer中。 + +```c++ +bool FDeferredShadingSceneRenderer::RenderBasePass(FRHICommandListImmediate& RHICmdList, FExclusiveDepthStencil::Type BasePassDepthStencilAccess, IPooledRenderTarget* ForwardScreenSpaceShadowMask, bool bParallelBasePass, bool bRenderLightmapDensity) +{ + (......) + + { + FExclusiveDepthStencil::Type BasePassDepthStencilAccess_NoDepthWrite = FExclusiveDepthStencil::Type(BasePassDepthStencilAccess & ~FExclusiveDepthStencil::DepthWrite); + // 并行模式 + if (bParallelBasePass) + { + FScopedCommandListWaitForTasks Flusher(CVarRHICmdFlushRenderThreadTasksBasePass.GetValueOnRenderThread() > 0 || CVarRHICmdFlushRenderThreadTasks.GetValueOnRenderThread() > 0, RHICmdList); + // 遍历所有view渲染Base Pass + for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) + { + FViewInfo& View = Views[ViewIndex]; + + // Uniform Buffer + TUniformBufferRef BasePassUniformBuffer; + CreateOpaqueBasePassUniformBuffer(RHICmdList, View, ForwardScreenSpaceShadowMask, nullptr, nullptr, nullptr, BasePassUniformBuffer); + + // Render State + FMeshPassProcessorRenderState DrawRenderState(View, BasePassUniformBuffer); + + SetupBasePassState(BasePassDepthStencilAccess, ViewFamily.EngineShowFlags.ShaderComplexity, DrawRenderState); + + const bool bShouldRenderView = View.ShouldRenderView(); + if (bShouldRenderView) + { + Scene->UniformBuffers.UpdateViewUniformBuffer(View); + + // 执行渲染. + RenderBasePassViewParallel(View, RHICmdList, BasePassDepthStencilAccess, DrawRenderState); + } + + FSceneRenderTargets::Get(RHICmdList).BeginRenderingGBuffer(RHICmdList, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, BasePassDepthStencilAccess, this->ViewFamily.EngineShowFlags.ShaderComplexity); + RenderEditorPrimitives(RHICmdList, View, BasePassDepthStencilAccess, DrawRenderState, bDirty); + RHICmdList.EndRenderPass(); + + (......) + } + + bDirty = true; // assume dirty since we are not going to wait + } + else // 非并行模式 + { + (......) + } + } + + (......) +} +``` +Base Pass的渲染逻辑和Pre Pass的逻辑是很类似的,故而不再细究。接下来重点查看渲染Base Pass时使用的渲染状态和材质,下面是渲染状态: +```c++ +void SetupBasePassState(FExclusiveDepthStencil::Type BasePassDepthStencilAccess, const bool bShaderComplexity, FMeshPassProcessorRenderState& DrawRenderState) +{ + DrawRenderState.SetDepthStencilAccess(BasePassDepthStencilAccess); + + (......) + + { + // 所有GBuffer都开启了混合. + static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.BasePassOutputsVelocityDebug")); + if (CVar && CVar->GetValueOnRenderThread() == 2) + { + DrawRenderState.SetBlendState(TStaticBlendStateWriteMask::GetRHI()); + } + else + { + DrawRenderState.SetBlendState(TStaticBlendStateWriteMask::GetRHI()); + } + + // 开启了深度写入和测试, 比较函数为NearOrEqual. + if (DrawRenderState.GetDepthStencilAccess() & FExclusiveDepthStencil::DepthWrite) + { + DrawRenderState.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); + } + else + { + DrawRenderState.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); + } + } +} +``` + +### LightingPass + +### Translucency +半透明阶段会渲染半透明的颜色、扰动纹理(用于折射等效果)、速度缓冲(用于TAA抗锯齿、后处理效果),其中最主要的渲染半透明的逻辑在`RenderTranslucency`: +```c++ +// Source\Runtime\Renderer\Private\TranslucentRendering.cpp +void FDeferredShadingSceneRenderer::RenderTranslucency(FRHICommandListImmediate& RHICmdList, bool bDrawUnderwaterViews) +{ + TRefCountPtr SceneColorCopy; + if (!bDrawUnderwaterViews) + { + ConditionalResolveSceneColorForTranslucentMaterials(RHICmdList, SceneColorCopy); + } + + // Disable UAV cache flushing so we have optimal VT feedback performance. + RHICmdList.BeginUAVOverlap(); + + // 在景深之后渲染半透明物体。 + if (ViewFamily.AllowTranslucencyAfterDOF()) + { + // 第一个Pass渲染标准的半透明物体。 + RenderTranslucencyInner(RHICmdList, ETranslucencyPass::TPT_StandardTranslucency, SceneColorCopy, bDrawUnderwaterViews); + // 第二个Pass渲染DOF之后的半透明物体, 会存储在单独的一张半透明RT中, 以便稍后使用. + RenderTranslucencyInner(RHICmdList, ETranslucencyPass::TPT_TranslucencyAfterDOF, SceneColorCopy, bDrawUnderwaterViews); + // 第三个Pass将半透明的RT和场景颜色缓冲在DOF pass之后混合起来. + RenderTranslucencyInner(RHICmdList, ETranslucencyPass::TPT_TranslucencyAfterDOFModulate, SceneColorCopy, bDrawUnderwaterViews); + } + else // 普通模式, 单个Pass即渲染完所有的半透明物体. + { + RenderTranslucencyInner(RHICmdList, ETranslucencyPass::TPT_AllTranslucency, SceneColorCopy, bDrawUnderwaterViews); + } + + RHICmdList.EndUAVOverlap(); +} +``` +`RenderTranslucencyInner`在内部真正地渲染半透明物体,它的代码如下: +```c++ +// Source\Runtime\Renderer\Private\TranslucentRendering.cpp + +void FDeferredShadingSceneRenderer::RenderTranslucencyInner(FRHICommandListImmediate& RHICmdList, ETranslucencyPass::Type TranslucencyPass, IPooledRenderTarget* SceneColorCopy, bool bDrawUnderwaterViews) +{ + if (!ShouldRenderTranslucency(TranslucencyPass)) + { + return; // Early exit if nothing needs to be done. + } + + FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList); + + // 并行渲染支持. + const bool bUseParallel = GRHICommandList.UseParallelAlgorithms() && CVarParallelTranslucency.GetValueOnRenderThread(); + if (bUseParallel) + { + SceneContext.AllocLightAttenuation(RHICmdList); // materials will attempt to get this texture before the deferred command to set it up executes + } + FScopedCommandListWaitForTasks Flusher(bUseParallel && (CVarRHICmdFlushRenderThreadTasksTranslucentPass.GetValueOnRenderThread() > 0 || CVarRHICmdFlushRenderThreadTasks.GetValueOnRenderThread() > 0), RHICmdList); + + // 遍历所有view. + for (int32 ViewIndex = 0, NumProcessedViews = 0; ViewIndex < Views.Num(); ViewIndex++) + { + FViewInfo& View = Views[ViewIndex]; + if (!View.ShouldRenderView() || (Views[ViewIndex].IsUnderwater() != bDrawUnderwaterViews)) + { + continue; + } + + // 更新场景的Uinform Buffer. + Scene->UniformBuffers.UpdateViewUniformBuffer(View); + + TUniformBufferRef BasePassUniformBuffer; + // 更新半透明Pass的Uinform Buffer. + CreateTranslucentBasePassUniformBuffer(RHICmdList, View, SceneColorCopy, ESceneTextureSetupMode::All, BasePassUniformBuffer, ViewIndex); + // 渲染状态. + FMeshPassProcessorRenderState DrawRenderState(View, BasePassUniformBuffer); + + // 渲染saparate列队. + if (!bDrawUnderwaterViews && RenderInSeparateTranslucency(SceneContext, TranslucencyPass, View.TranslucentPrimCount.DisableOffscreenRendering(TranslucencyPass))) + { + FIntPoint ScaledSize; + float DownsamplingScale = 1.f; + SceneContext.GetSeparateTranslucencyDimensions(ScaledSize, DownsamplingScale); + + if (DownsamplingScale < 1.f) + { + FViewUniformShaderParameters DownsampledTranslucencyViewParameters; + SetupDownsampledTranslucencyViewParameters(RHICmdList, View, DownsampledTranslucencyViewParameters); + Scene->UniformBuffers.UpdateViewUniformBufferImmediate(DownsampledTranslucencyViewParameters); + DrawRenderState.SetViewUniformBuffer(Scene->UniformBuffers.ViewUniformBuffer); + + (......) + } + + // 渲染前的准备阶段. + if (TranslucencyPass == ETranslucencyPass::TPT_TranslucencyAfterDOF) + { + BeginTimingSeparateTranslucencyPass(RHICmdList, View); + SceneContext.BeginRenderingSeparateTranslucency(RHICmdList, View, *this, NumProcessedViews == 0 || View.Family->bMultiGPUForkAndJoin); + } + // 混合队列. + else if (TranslucencyPass == ETranslucencyPass::TPT_TranslucencyAfterDOFModulate) + { + BeginTimingSeparateTranslucencyModulatePass(RHICmdList, View); + SceneContext.BeginRenderingSeparateTranslucencyModulate(RHICmdList, View, *this, NumProcessedViews == 0 || View.Family->bMultiGPUForkAndJoin); + } + // 标准队列. + else + { + SceneContext.BeginRenderingSeparateTranslucency(RHICmdList, View, *this, NumProcessedViews == 0 || View.Family->bMultiGPUForkAndJoin); + } + + // Draw only translucent prims that are in the SeparateTranslucency pass + DrawRenderState.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); + + // 真正地绘制半透明物体. + if (bUseParallel) + { + RHICmdList.EndRenderPass(); + RenderViewTranslucencyParallel(RHICmdList, View, DrawRenderState, TranslucencyPass); + } + else + { + RenderViewTranslucency(RHICmdList, View, DrawRenderState, TranslucencyPass); + RHICmdList.EndRenderPass(); + } + + // 渲染后的结束阶段. + if (TranslucencyPass == ETranslucencyPass::TPT_TranslucencyAfterDOF) + { + SceneContext.ResolveSeparateTranslucency(RHICmdList, View); + EndTimingSeparateTranslucencyPass(RHICmdList, View); + } + else if (TranslucencyPass == ETranslucencyPass::TPT_TranslucencyAfterDOFModulate) + { + SceneContext.ResolveSeparateTranslucencyModulate(RHICmdList, View); + EndTimingSeparateTranslucencyModulatePass(RHICmdList, View); + } + else + { + SceneContext.ResolveSeparateTranslucency(RHICmdList, View); + } + + // 上采样(放大)半透明物体的RT. + if (TranslucencyPass != ETranslucencyPass::TPT_TranslucencyAfterDOF && TranslucencyPass != ETranslucencyPass::TPT_TranslucencyAfterDOFModulate) + { + UpsampleTranslucency(RHICmdList, View, false); + } + } + else // 标准队列. + { + SceneContext.BeginRenderingTranslucency(RHICmdList, View, *this, NumProcessedViews == 0 || View.Family->bMultiGPUForkAndJoin); + DrawRenderState.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); + + if (bUseParallel && !ViewFamily.UseDebugViewPS()) + { + RHICmdList.EndRenderPass(); + RenderViewTranslucencyParallel(RHICmdList, View, DrawRenderState, TranslucencyPass); + } + else + { + RenderViewTranslucency(RHICmdList, View, DrawRenderState, TranslucencyPass); + RHICmdList.EndRenderPass(); + } + + SceneContext.FinishRenderingTranslucency(RHICmdList); + } + + // Keep track of number of views not skipped + NumProcessedViews++; + } +} +``` +半透明渲染的C++逻辑和shader逻辑跟Base Pass比较相似,不同的是半透明只处理半透明物体,Uniform Buffer部分不一样,Render State也有所不同,光照算法也有区别计算。但它们的主干逻辑大致雷同,此处不再展开剖析了。 + +此外,UE4 的半透明渲染队列主要有两个:一个是标准(Standard)队列,另一个是分离(Separate)队列。 分离队列需要在工程Render设置中开启(默认开启)。如果想要将某个透明物体加入分离(Separate)队列,只需要将其使用的材质开启**Render After DOF**即可(默认已开启): \ No newline at end of file diff --git a/03-UnrealEngine/Rendering/RenderingPipeline/向往渲染系列文章阅读笔记/剖析虚幻渲染体系(05)- 光源和阴影.md b/03-UnrealEngine/Rendering/RenderingPipeline/向往渲染系列文章阅读笔记/剖析虚幻渲染体系(05)- 光源和阴影.md new file mode 100644 index 0000000..d0ecbb4 --- /dev/null +++ b/03-UnrealEngine/Rendering/RenderingPipeline/向往渲染系列文章阅读笔记/剖析虚幻渲染体系(05)- 光源和阴影.md @@ -0,0 +1,137 @@ +--- +title: 剖析虚幻渲染体系(05)- 光源和阴影 +date: 2024-02-08 17:48:39 +excerpt: +tags: +rating: ⭐ +--- +# 前言 +https://www.cnblogs.com/timlly/p/14817455.html + +# Shader体系 +## Shader模块层级 +**第一梯队**的shader模块是**最底层最基础的模块**,这些模块不会引用其它模块,但会被其它很多模块引用。这些模块主要有: +- BasePassCommon.ush +- BRDF.ush +- CapsuleLight.ush +- Common.ush +- CommonViewUniformBuffer.ush +- Definitions.usf +- FP16Math.ush +- Platform.ush +- ShaderVersion.ush +- LightGridCommon.ush +- LocalVertexFactoryCommon.ush +- ShadingCommon.ush +- ShadowDepthCommon.ush +- ShadowProjectionCommon.ush +- SHCommon.ush +- (......) + +**第二梯队**的**重要或基础模块**会引用第一梯队的基础模块,但也会被其它梯队或模块引用: +- BasePassVertexCommon.ush +- CapsuleLightIntegrate.ush +- DeferredLightingCommon.ush +- DeferredShadingCommon.ush +- LocalVertexFactory.ush +- MaterialTemplate.ush +- RectLight.ush +- RectLightIntegrate.ush +- ShadingModels.ush +- ShadingModelsMaterial.ush +- VertexFactoryCommon.ush +- (......) + +最后是**第三梯队**的模块,重要模块的实现,会引用第一、第二梯队的模块: +- BasePassPixelShader.usf +- BasePassVertexShader.usf +- CapsuleShadowShaders.usf +- DeferredLightPixelShaders.usf +- DeferredLightVertexShaders.usf +- ShadowProjectionPixelShader.usf +- ShadowProjectionVertexShader.usf +- (......) + +## Shader基础模块 +- Platform.ush:主要定义了跟图形API(DirectX、OpenGL、Vulkan、Metal)和FEATURE_LEVEL相关的宏、变量及工具类接口。 +- **Common.ush**:此模块主要包含了图形API或Feature Level相关的宏、类型、局部变量、静态变量、基础工具接口、常用Shader函数等。 +- **Definitions.ush**:此模块主要是预先定义了一些常见的宏,防止其它模块引用时出现语法错误。 +- **ShadingCommon.ush**:此模块主要是定义了材质所有ShaderModel,并提供了少量相关的工具类接口。 +- **ShadingModels.ush**:此模块主要是着色模型以及光照计算相关的类型和辅助接口。 +- **BasePassCommon.ush**:此模块定义了BasePass的一些变量、宏定义、插值结构体和工具类接口。 +- **ShadingModelsMaterial.ush**:提供了根据材质和指定参数设置GBuffe的接口。 +- BRDF.ush:双向反射分布函数模块,提供了很多基础光照算法及相关辅助接口。 +- VertexFactoryCommon.ush:此模块主要定义了顶点变换相关的辅助接口。 +- LocalVertexFactoryCommon.ush:局部顶点工厂通用模块,定义了顶点工厂的数据插值结构体及部分辅助接口。 +- LocalVertexFactory.ush:局部顶点工厂模块,定义了骨骼蒙皮、顶点着色器等相关的数据类型和接口。 +- BasePassVertexCommon.ush:此模块定义了一些BasePass通用的结构体、宏。 + - 需要注意的是核心结构体`FGBufferData`,**它是通用的结构体,可用于BasePass和LightingPass的VS和PS之间,也可用于前向和延迟渲染中**。是最大数据集合体,在某些情况下,部分属性才有效,具体见**ShadingModelsMaterial.ush**的`SetGBufferForShadingModel`。 +- **DeferredLightingCommon.ush**:此模块定义了延迟光照相关的通用的接口、宏、变量、类型等。 + +# BasePass +下面是`RenderBasePass`和`RenderBasePassViewParallel`并行渲染的逻辑: +```c++ +bool FDeferredShadingSceneRenderer::RenderBasePass(FRHICommandListImmediate& RHICmdList, FExclusiveDepthStencil::Type BasePassDepthStencilAccess, IPooledRenderTarget* ForwardScreenSpaceShadowMask, bool bParallelBasePass, bool bRenderLightmapDensity) +{ + (......) + + FExclusiveDepthStencil::Type BasePassDepthStencilAccess_NoDepthWrite = FExclusiveDepthStencil::Type(BasePassDepthStencilAccess & ~FExclusiveDepthStencil::DepthWrite); + // 并行模式 + if (bParallelBasePass) + { + // 绘制任务等待. + FScopedCommandListWaitForTasks Flusher(CVarRHICmdFlushRenderThreadTasksBasePass.GetValueOnRenderThread() > 0 || CVarRHICmdFlushRenderThreadTasks.GetValueOnRenderThread() > 0, RHICmdList); + + // 遍历所有view, 每个view渲染一次Base Pass. + for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) + { + FViewInfo& View = Views[ViewIndex]; + + // Uniform Buffer + TUniformBufferRef BasePassUniformBuffer; + CreateOpaqueBasePassUniformBuffer(RHICmdList, View, ForwardScreenSpaceShadowMask, nullptr, nullptr, nullptr, BasePassUniformBuffer); + + // Render State + FMeshPassProcessorRenderState DrawRenderState(View, BasePassUniformBuffer); + SetupBasePassState(BasePassDepthStencilAccess, ViewFamily.EngineShowFlags.ShaderComplexity, DrawRenderState); + + const bool bShouldRenderView = View.ShouldRenderView(); + if (bShouldRenderView) + { + Scene->UniformBuffers.UpdateViewUniformBuffer(View); + + // 执行并行渲染. + RenderBasePassViewParallel(View, RHICmdList, BasePassDepthStencilAccess, DrawRenderState); + } + + FSceneRenderTargets::Get(RHICmdList).BeginRenderingGBuffer(RHICmdList, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, BasePassDepthStencilAccess, this->ViewFamily.EngineShowFlags.ShaderComplexity); + RHICmdList.EndRenderPass(); + + (......) + } + } + + (......) +} + +void FDeferredShadingSceneRenderer::RenderBasePassViewParallel(FViewInfo& View, FRHICommandListImmediate& ParentCmdList, FExclusiveDepthStencil::Type BasePassDepthStencilAccess, const FMeshPassProcessorRenderState& InDrawRenderState) +{ + // 并行绘制的数据: 命令队列, 上下文, 渲染状态等. + FBasePassParallelCommandListSet ParallelSet(View, ParentCmdList, + CVarRHICmdBasePassDeferredContexts.GetValueOnRenderThread() > 0, + CVarRHICmdFlushRenderThreadTasksBasePass.GetValueOnRenderThread() == 0 && CVarRHICmdFlushRenderThreadTasks.GetValueOnRenderThread() == 0, + this, + BasePassDepthStencilAccess, + InDrawRenderState); + + // 触发并行绘制指令. + View.ParallelMeshDrawCommandPasses[EMeshPass::BasePass].DispatchDraw(&ParallelSet, ParentCmdList); +} +``` +`RenderBasePass`是依靠`FScopedCommandListWaitForTasks`等待绘制指令完成。 + +## BasePass渲染状态 +我们知道BasePass是通过`FBasePassMeshProcessor`来收集很多shader绑定和绘制参数的,从Processor的过程中可以很容易知道,BasePass绘制时使用的VS和PS分别是`TBasePassVS`和`TBasePassPS`。 +可知`TBasePassVS`提供了获取Shader绑定、更改编译环境和只编译指定的排列组合shader等接口,此外还拥有反射球、光照图类型等属性。下面是它的获取shader绑定的代码分析: +```c++ +``` \ No newline at end of file