vault backup: 2024-02-08 19:03:46

This commit is contained in:
BlueRose 2024-02-08 19:03:46 +08:00
parent dad07796ee
commit 77690398af
3 changed files with 696 additions and 2 deletions

View File

@ -142,6 +142,22 @@
"lastUpdated": 1707302668655
}
}
},
"BasePassDepthStencilAccess": {
"BasePassDepthStencilAccess": {
"currentFile": {
"count": 1,
"lastUpdated": 1707384942861
}
}
},
"ush此模块定义了一些BasePass通用的结构体、宏。": {
"ush此模块定义了一些BasePass通用的结构体、宏。": {
"currentFile": {
"count": 1,
"lastUpdated": 1707386581416
}
}
}
}
}

View File

@ -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的渲染效率。
**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<void()> 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<FSceneTexturesUniformParameters> 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<CW_NONE>::GetRHI());
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<true, CF_DepthNearOrEqual>::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<true>(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<UMaterial>(nullptr, *ResolvedPath);
if (GDefaultMaterials[Domain] == nullptr)
{
GDefaultMaterials[Domain] = LoadObject<UMaterial>(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<FOpaqueBasePassUniformParameters> 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<CW_RGBA, CW_RGBA, CW_RGBA, CW_RGBA, CW_RGBA, CW_RGBA, CW_NONE>::GetRHI());
}
else
{
DrawRenderState.SetBlendState(TStaticBlendStateWriteMask<CW_RGBA, CW_RGBA, CW_RGBA, CW_RGBA>::GetRHI());
}
// 开启了深度写入和测试, 比较函数为NearOrEqual.
if (DrawRenderState.GetDepthStencilAccess() & FExclusiveDepthStencil::DepthWrite)
{
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<true, CF_DepthNearOrEqual>::GetRHI());
}
else
{
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<false, CF_DepthNearOrEqual>::GetRHI());
}
}
}
```
### LightingPass
### Translucency
半透明阶段会渲染半透明的颜色、扰动纹理用于折射等效果、速度缓冲用于TAA抗锯齿、后处理效果其中最主要的渲染半透明的逻辑在`RenderTranslucency`
```c++
// Source\Runtime\Renderer\Private\TranslucentRendering.cpp
void FDeferredShadingSceneRenderer::RenderTranslucency(FRHICommandListImmediate& RHICmdList, bool bDrawUnderwaterViews)
{
TRefCountPtr<IPooledRenderTarget> 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<FTranslucentBasePassUniformParameters> 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<false, CF_DepthNearOrEqual>::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<false, CF_DepthNearOrEqual>::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**即可(默认已开启):

View File

@ -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主要定义了跟图形APIDirectX、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<FOpaqueBasePassUniformParameters> 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++
```