【ue4】【架构】渲染系统

渲染系统

UE4 的渲染器是在其自身的渲染线程中执行的

这个渲染线程通常落后于游戏线程1到2帧

所以有一些类会将游戏线程上的状态信息连接到渲染线程中来 (通常以F开头)

FScene -- 表示游戏场景, 是 UWorld 的渲染器版本

FPrimitiveSceneProxy -- UPrimitiveComponent 的渲染器版本, 为渲染线程映射 UPrimitiveComponent 状态

FPrimitiveSceneInfo -- FPrimitiveSceneProxy 的状态, 只存在于渲染器

FViewInfo -- 当前屏幕视图的渲染器 对应游戏模块中的 FSceneView

FSceneViewState -- FViewInfo 的状态, 与游戏中 UPlayer 一一对应

FLightSceneProxy 与 FLightSceneInfo -- 跟 Primitive差不多

FPrimitiveSceneProxy 存在于引擎模块中, 有两个 API 比较重要

DrawDynamicElements -- 在与之相关的任何 Pass 中绘制该拥有动态相关性的代理物体

DrawStaticElements -- 在与游戏线程相连时绘制该拥有静态相关性的代理物体

这也说明 UE4 对几何体的渲染是分了 Static 和 Dynamic 分别来处理的

  • Static Render
    • FPrimitiveSceneProxy 被放入场景时, 会调用其 DrawStaticElements 来收集 FStaticMeshElements, 然后创建对应的 Draw Policy 放到 FSceneDraw List 里去。
  • Dynamic Render
    • 在判定 FPrimitiveSceneProxy 可见后即调用 DrawDynamicElements() 来收集 FMeshElements

【Tips】 Proxy 的意思为渲染模块在游戏模块中出现的代理, 实际存在于引擎模块中, 而非渲染模块中

材质在渲染时也需要连接游戏线程和渲染线程的接口

FMaterial -- 材质的接口, 提供属性的查询等 -- 纯虚类

FMaterialResoruce -- FMaterial 的一个具体实现, 对应 UMaterial

FMaterialRenderProxy -- 提供给渲染线程使用的代理

渲染流程

=============================================

【Pass_0】 Depth Only Pass 【Pass_1】 Base Pass 【Pass_2】 Issue Occlusion Queries 【Pass_3】 ShadowMap 【Pass_4】 Lighting 【Pass_5】 Fog 【Pass_6】 Post Processing

============================================= 【Tips】 基于 Engine\Source\Runtime\Renderer\Private\DeferredShadingRenderer.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

class FDeferredShadingSceneRenderer : public FSceneRenderer
{
public:

// Pass_0
bool RenderPrePassViewDynamic(FRHICommandList& RHICmdList, const FViewInfo& View, const FDrawingPolicyRenderState& DrawRenderState);
bool RenderPrePassView(FRHICommandList& RHICmdList, const FViewInfo& View, const FDrawingPolicyRenderState& DrawRenderState);

// Pass_1
bool RenderBasePass(FRHICommandListImmediate& RHICmdList, FExclusiveDepthStencil::Type BasePassDepthStencilAccess, IPooledRenderTarget* ForwardScreenSpaceShadowMask);
bool RenderBasePassView(FRHICommandListImmediate& RHICmdList, FViewInfo& View, FExclusiveDepthStencil::Type BasePassDepthStencilAccess, const FDrawingPolicyRenderState& InDrawRenderState);

// Pass_2
void RenderOcclusion(FRHICommandListImmediate& RHICmdList);
void FinishOcclusion(FRHICommandListImmediate& RHICmdList)

// Pass_3
void RenderShadowDepthMaps(FRHICommandListImmediate& RHICmdList);
void RenderShadowDepthMapAtlases(FRHICommandListImmediate& RHICmdList);

// Pass_4
virtual void Render(FRHICommandListImmediate& RHICmdList) override;
void RenderLights(FRHICommandListImmediate& RHICmdList);

// Pass_5
virtual void Render(FRHICommandListImmediate& RHICmdList) override;
bool RenderFog(FRHICommandListImmediate& RHICmdList, const FLightShaftsOutput& LightShaftsOutput);

// Pass_6
virtual void Render(FRHICommandListImmediate& RHICmdList) override;


}

【Pass_0】 Depth Only Pass

深度处理阶段

绘制 Depth 到 Depth_Buffer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* Renders the scene's prepass for a particular view
* @return true if anything was rendered
*/
bool RenderPrePassView(FRHICommandList& RHICmdList, const FViewInfo& View, const FDrawingPolicyRenderState& DrawRenderState)
{
// ...
{ bDirty |= RenderPrePassViewDynamic(); }
return bDirty;
}

/**
* Renders the scene's prepass for a particular view
* @return true if anything was rendered
*/
bool RenderPrePassViewDynamic(FRHICommandList& RHICmdList, const FViewInfo& View, const FDrawingPolicyRenderState& DrawRenderState)
{
foreach (mesh)
{
// 判断可见性代码
// ...
if (可见) { FDepthDrawingPolicyFactory::DrawDynamicMesh(); }
}
return true;
}

【Tips】 DrawingPolicyFactory 为绘制规则的工厂类

【Pass_1】 Base Pass

绘制 不透明的 和 Masked Material属性的 Mesh

填充 G_Buffer

UE4 中的 G_Buffer 结构 (来知乎专栏_虚幻4渲染编程)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Engine\Source\Runtime\Renderer\Private\BasePassRendering.cpp

/**
* Renders the scene's base pass
* @return true if anything was rendered
*/
bool RenderBasePass(FRHICommandListImmediate& RHICmdList, FExclusiveDepthStencil::Type BasePassDepthStencilAccess, IPooledRenderTarget* ForwardScreenSpaceShadowMask)
{
// ...
bDirty |= RenderBasePassView(RHICmdList, View, BasePassDepthStencilAccess, DrawRenderState);
return bDirty;
}

/** Renders the basepass for a given View. */
bool RenderBasePassView(FRHICommandListImmediate& RHICmdList, FViewInfo& View, FExclusiveDepthStencil::Type BasePassDepthStencilAccess, const FDrawingPolicyRenderState& InDrawRenderState)
{
bDirty |= RenderBasePassStaticData(RHICmdList, View, DrawRenderState);
RenderBasePassDynamicData(RHICmdList, View, DrawRenderState, bDirty);
return bDirty;
}

此时 Shader文件可从下面代码

1
2
3
4
5
6
7
8
// Engine\Source\Runtime\Renderer\Private\BasePassRendering.cpp 113

#define IMPLEMENT_BASEPASS_PIXELSHADER_TYPE(LightMapPolicyType,LightMapPolicyName,bEnableSkyLight,SkyLightName) \
typedef TBasePassPS<LightMapPolicyType, bEnableSkyLight> TBasePassPS##LightMapPolicyName##SkyLightName; \
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TBasePassPS##LightMapPolicyName##SkyLightName, \
TEXT(" /Engine/Private/BasePassPixelShader.usf"), \
TEXT("MainPS"),SF_Pixel);

/Engine/Private/BasePassPixelShader.usf 696

1
2
3
4
5
6
7
8
9
10
11
// is called in MainPS() from PixelShaderOutputCommon.usf
void FPixelShaderInOut_MainPS(
FVertexFactoryInterpolantsVSToPS Interpolants,
FBasePassInterpolantsVSToPS BasePassInterpolants,
in FPixelShaderIn In,
inout FPixelShaderOut Out)
{
// ...
// Fill G_Buffer
// ...
}

这就是 Bass Pass 的 着色器入口了

【Pass_2】 Issue Occlusion Queries

遮挡查询, 为下一帧的可见性判断提供信息

ue4 的遮挡查询是对包围盒进行深度测试来判断的

1
2
void RenderOcclusion(FRHICommandListImmediate& RHICmdList) {}
void FinishOcclusion(FRHICommandListImmediate& RHICmdList) {}

【Pass_3】 ShadowMap

阴影计算

1
2
void RenderShadowDepthMaps(FRHICommandListImmediate& RHICmdList) {}
void RenderShadowDepthMapAtlases(FRHICommandListImmediate& RHICmdList) {}

【Pass_4】 Lighting

光照计算

首先预处理 延迟贴花 SSAO 等, 然后再进行光照计算

UE4 的光照计算采用 TBDR (Tiled Based Deferred Rendering)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void FDeferredShadingSceneRenderer::Render(FRHICommandListImmediate& RHICmdList)
{
// ...
// Pre-lighting composition lighting stage
// e.g. deferred decals, SSAO
// ...

// Render lighting.
RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_Lighting));
RenderLights(RHICmdList);
RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_AfterLighting));

// Pre-lighting composition lighting stage
// e.g. LPV indirect
// ...
}

然后是处理光照的着色器入口

1
2
3
4
5
6
7
8
9
10
// Engine\Shaders\Private\TiledDeferredLightShaders.usf 55

[numthreads(THREADGROUP_SIZEX, THREADGROUP_SIZEY, 1)]
void TiledDeferredLightingMain(
uint3 GroupId : SV_GroupID,
uint3 DispatchThreadId : SV_DispatchThreadID,
uint3 GroupThreadId : SV_GroupThreadID)
{
// ...
}

【Pass_5】 Fog

雾计算

非透明表面逐像素计算 Fog

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void FDeferredShadingSceneRenderer::Render(FRHICommandListImmediate& RHICmdList)
{
// ...
// Draw fog.
if (ShouldRenderFog(ViewFamily))
{
SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_RenderFog);
RenderFog(RHICmdList, LightShaftOutput);
ServiceLocalQueue();
}
}

/** Renders the scene's fogging. */
bool RenderFog(FRHICommandListImmediate& RHICmdList, const FLightShaftsOutput& LightShaftsOutput)
{
// ...
}

【Pass_6】 Post Processing

后处理效果

1
2
3
4
5
6
7
8
9
10
11
void FDeferredShadingSceneRenderer::Render(FRHICommandListImmediate& RHICmdList)
{
// ...

// Resolve the scene color for post processing.
ResolveSceneColor(RHICmdList);

GetRendererModule().RenderPostResolvedSceneColorExtension(RHICmdList, SceneContext);

CopySceneCaptureComponentToTarget(RHICmdList);
}

【Tips】 UE4 移动端似乎只有 Forward Rendering -- 没有 深度渲染器 和 GBuffer