vault backup: 2024-02-08 19:03:46
This commit is contained in:
parent
dad07796ee
commit
77690398af
16
.obsidian/plugins/various-complements/data.json
vendored
16
.obsidian/plugins/various-complements/data.json
vendored
@ -142,6 +142,22 @@
|
|||||||
"lastUpdated": 1707302668655
|
"lastUpdated": 1707302668655
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"BasePassDepthStencilAccess": {
|
||||||
|
"BasePassDepthStencilAccess": {
|
||||||
|
"currentFile": {
|
||||||
|
"count": 1,
|
||||||
|
"lastUpdated": 1707384942861
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ush:此模块定义了一些BasePass通用的结构体、宏。": {
|
||||||
|
"ush:此模块定义了一些BasePass通用的结构体、宏。": {
|
||||||
|
"currentFile": {
|
||||||
|
"count": 1,
|
||||||
|
"lastUpdated": 1707386581416
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1406,6 +1406,547 @@ void FSceneViewState::OnStartRender(FViewInfo& View, FSceneViewFamily& ViewFamil
|
|||||||
```
|
```
|
||||||
|
|
||||||
### PrePass
|
### 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**即可(默认已开启):
|
@ -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<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++
|
||||||
|
```
|
Loading…
x
Reference in New Issue
Block a user