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
|
||||
}
|
||||
}
|
||||
},
|
||||
"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又被称为提前深度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