Bootstrap

【Unreal从0到1】【第二章:引擎渲染管线分析】2.3,移动端渲染管线特性分析

【Unreal从0到1】【第二章:引擎渲染管线分析】2.3,移动端渲染管线特性分析

https://zhuanlan.zhihu.com/p/401583420

对于大多数国内开发者来说,我们可能更关心UE4在移动端的表现,所以本文除了会像 上一篇文章那样对渲染管线的组织逻辑进行分析,还会拿出相当一部分笔墨来梳理shader的表现。

如无特殊说明,本专栏的分析全部基于UE4.26

一,移动端渲染管线

类似PC端的思路,直接找到FMobileSceneRenderer类,相比较于FDeferredShadingSceneRenderer,这个类定义非常简单。对哪方面特性感兴趣,直接去cpp文件中翻具体实现就好了。让我们把主要精力放到Render函数上:

好家伙,262行~,对比PC端的1500行这太寒酸了吧。。。不用看了,肯定要什么没什么!我们下篇文章见吧~

咳咳,言归正传,先过一遍

1,SetCurrentStat

这是FRHIComandListImmediate类的一个成员参数,这个类在2.1中有提到,用于在RHI线程中管理渲染操作队列。SetCurrentState通过一个可变参数宏实现。就是把FRHICommandStat给Stat。用来设置当前渲染状态。

2,UpdataAllPrimitiveSceneInfos

与PC端一致,都是调用FScene类的UpdataAllPrimitiveSceneInfos成员函数

3,InitView

Mobile管线有自己独立的InitView方法,首先依旧是PreVisibilityFrameSetup,,ComputeViewVisibilityPostVisibilityFrameSetup(ILCTaskData),与PC端一致,都是直接继承自FSceneRenderer,不再赘述。

然后设置RT尺寸分辨率,然后是用于前向的平面反射与移动端AO计算的初始化

然后是对深度处理的预判断,包括判断是否需要存储深度进行后期处理以及在 PowerVR 上上的处理

然后InitializeTextures来初始化系统纹理,申请RT

然后是一系列的判断:是否进行延迟着色,如果启用则需准备GBuffer与动用MRT;是否渲染天空大气,如有需为视图初始化SkyAtmosphere,与PC端一致;是否启用虚拟纹理;是否执行逐像素的平面反射;是否包含Custom Depth;是否使用动态阴影等feature

然后是UpdateMovablePointLightUniformBufferAndShadowInfo()用于更新所有可移动点光源的统一缓冲区和阴影信息。SetupMobileBasePassAfterShadowInit设置BasePass。

然后初始化视图的统一缓冲区

然后是UpdataGPUScene,移动端直接放到InitView里来了~

最后更新天光反射的Uniform,更新ILC数据,开始这一帧的渲染

4,TBDR光源剔除

FSortedLightSetSceneInfo用于灯光排序

UE4的灯光分类中有是否为简单光源的划分(我猜简单光源应该就是指那些没有挂一堆属性的灯光),会先索引简单光源(简单光源都可以参与分块分簇),然后是分块分簇的灯光列表。这里的分块分簇应该就是指TBDR【Tile Based Deferred Rendering】CDR【Cluster Deferred Rendering】,CDR其实TBDR的一种优化方案——在深度方向进行了更细粒度的划分,从而避免TBDR在深度范围内跳变很大时光源剪裁效率降低的问题。

红色:Tile,绿色:Implicit,紫色:Explicit

而CDR再细分又有Implicit(隐式分簇)Explicit(显示分簇)两种,灯光划分的准确度对比如上

再跳回主线,下一个是带阴影的光源索引(非简单光源),最后两个成员变量简单光源列表与有序光源列表分别存储了两种复杂度的光源。SortedLights是一个容器,进去看下UE4的物理光源到底包含那些属性或者说特点:

union中是一个键组合,含义简单概括就如注释上所示,为了保证文章主线脉络的清晰,关于UE4的物理照明会安排在第三章:Disney原则的物理渲染中PBL部分进行详细阐述,UE4的PBL设计思想以及为什么它的灯光是物理的

然后继续在主线中走,GatherAndSortLights又是一个需要重点关注的函数,它实现了“搜集简单光源,构建可见光源列表,光源划分(是否参与分块分簇渲染),光源排序”等。

5,天空大气渲染(如果启用)

就是LUT的大气多散射方案,具体会安排在本专栏”第四章:实时天气系统,第一节,物理的大气系统“中进行说明,各位也可以先行去读论文。

6,Shadow Pass

就是CSM了,有“并行生成”与“串行生成”两种方式

这些成员函数所执行的功能看名字应该就可以猜的差不多,具体实现绘制的看代码是上面的RebderDepthInner:

这里的DispatchDraw就是MeshDrawCommand中用来向RHI线程分发绘制命令的方法。

7,前向渲染与延迟渲染

RenderDeferred负责完成移动端延迟渲染的组织,RenderForward负责完成移动端前向渲染的逻辑组织,两个函数都没什么特殊的东西,常规那一套

8,FX Pass

9,Ambient Occlusion

找到PostPrcocessAmbientOcclusionMobile.cpp,可以看到一个RDG格式的pass:

UE4在移动端的AO表现一直平淡无奇,直到4.26官方集成了GTAO,这是一个很大的进步,以前的版本在代码层不支持的情况下,美术想要实现细腻的间接光屏蔽效果是很难的。关于PC端与移动端的渲染特性差异,以及如何尽可能高的提升移动端画质的思路会在下一篇文章中详细描述。我们可以在FMobileSceneRenderer::RenderAmbientOcclusion中找到GTAO的具体实现,这是一种HBAO的改进算法(其实都是SSAO),关于它的分析满大街的文章,也可以去读这篇论文[1]:https://www.activision.com/cdn/research/Practical_Real_Time_Strategies_for_Accurate_Indirect_Occlusion_NEW%20VERSION_COLOR.pdf

10,后处理

可以看到,相比较于PC端,移动端可选的后处理功能会有限一些:

这里的Tonemap不是完整的ACES,对比PC端是有缩水的;EyeAdaption是4.24开始支持的,之前也是只在PC端有;Bloom与SeparateTranslucency我还没有详细比对,不清楚是不是功能简化版;

再Review下UE4的移动端渲染管线,绿色部分为需要重点关注的:

二,移动端着色方案

然后来看一下UE4.26 Mobile的着色计算,需要关注的文件有这些:

可以看到与PC端相比着色框架体量很轻,也没什么“花里胡哨”的渲染特性,只提供了一个基础版的框架,上图“标绿”部分为需要重点关注的

1,MobileGGX

拟合版的GGX分布,技术原型来源于Google的开源引擎Filament[2]:

由于在中等精度下当NoH^2接近0的时候会出现浮点消除的问题,并且NoH在接近1.0精度不够。为了解决这个问题使用拉格朗日恒等式进行数学变换,拉格朗日恒等式可以使用两个点积计算代替原来的叉积计算如下:

因为N与H都是归一化的单位向量,所以根据上式就有||N X H||^2 = 1.0 - NoH^2,这样就可以避免精度不够的问题,完整的代码计算如下:

这里有一个PI的问题需要额外注意,就是把原本应该在D项中除掉的Pi放到了后续环节中处理。

2,Mobile Shading Model与BRDF

在移动端用来计算直接光照高光反馈情况的BRDF并不是Cook-Torrance模型,也是一个拟合版本,用到了上面MobileGGX的NDF:

与PC端相比可以看到这里的SpecularBRDF光照模型进行了大量近似,如下:

  • D项的近似采用了Feilament的思路,代码位于MobileGGX.ush中
  • G项与F项以及分母被近似为了roughness的线性函数,使用PC平台的FG模型(G = Vis_SmithJointApprox ,F = F_Schlick),当n = l = v 时,就有(Roughness*0.25 + 0.25)

对于PBR来说,高光是影响质感非常重要的特性,这套拟合的BRDF会比Cook-Torrance那套拥有更短的拖尾,所以相同材质参数下的DefaultLIt移动端会更“油”一些。如果需要在移动端生产更为柔和的高光分布(比如哑光类质感),材质参数上可以去调整Roughness贴图,计算模型上可以使用更长拖尾的NDF。

除了DefaultLit,从UE4.25开始,移动端开始集成一套“透明涂层”光照模型,这套ShadingModel并没有PC端那套物理,不过效果也还OK。透明涂层类光照模型一个典型特点就是双层高光,底层颜色层,外面包覆一层薄薄的透明涂层,所以拥有双层高光反馈,层间透射,吸收与菲涅尔。这样一套物理模型非常适合模拟“车漆”类材料(色涂层 + 清漆层)

关于透明涂层更详细分析会安排在第三章:Disney原则的物理渲染中进行详细阐述。

值得注意的是从4.25开始才有MobileShadingModel.,ush这个文件,之前的版本移动端就一个Unlit,一个DefalutLit,如果大家有听过《2019年虚幻引擎技术开放日》的Talk,会非常清楚这一点。那么从4.25开始,增加了移动端车漆效果,所以仿照PC端单独布置在了一个专门的文件中,同样使用MobileIntegrateBxDF这样的思路进行封装。

这种管理不同光照计算分支的思路是很好的,如果我们在项目中需要封装比较多的shadingmodel,也可以用这种思路搭建着色框架,从而避免MobileBasePassPixelShader这个文件过于冗长。另外ush可以自由添加,也就是说不会像usf那样,新增一个文件需要在C++部分写很多代码进行配置【具体原因会在第三章第一节进行说明】,不过新增文件后还是要重新构建一下解决方案,否则Visual Studio无法自动加载新添文件(但还是会起作用的)。

3,传统前向着色

移动端的前向管线逻辑就非常简单了,4.25之前只有一个MobileBasePassPixelShader,也就是说绝大部分光照着色计算都在这个文件中,维护起来很方便,4.25把直接光照计算封装了ShadingModel置于MobileShadingModel.ush中。

MobileBasePassPixelShader这个文件也就1000行HLSL,结构还是比较简单清楚的,大致可以划分为5个Code Block,如下:

这里吐槽一下,我个人不是很喜欢UE4静态分支全部首列对齐的骚操作。。。该缩进就缩进啊,这样看着不难受嘛。【上图是我重新整理过的版本】。里面的代码块与块内计算方式各个版本略有不同。另外其实移动端有各种小问题,4.24修复4.23的,4.25修复4.24的,所以如果是做项目我可能更偏向于直接用最新版。

接下来让我们来看一下漫长的着色管道中各个环节都发生了什么,对应环节的计算又会对最终画面效果产生怎样的贡献和影响,这对TA来说非常的重要。

(1)Step1:宏定义

首先是一堆宏,可以对比下不同版本间功能支持的异同,不过对比了也没用,看样子Epic从4.24,25开始才认真对待起移动端的开发,不信可以去看4.23及以前的版本,PC端各种牛逼特性层出不穷,移动端要啥没啥。。。

(2)Step2:反射混合

这个就是“【第一章:物理的全局光照实现】1.1,PRT:预计算的全局光照”部分提到的移动端最高支持3个反射捕获混合,在HQ_REFECTIONS宏启用的情况下参与计算。实际上3个混合的情况可能还比较少,大部分时候2个感觉就足够了。

(3)Step3:反射采集方式定义

移动端前向管线中同样支持“球形捕获,平面捕获与方盒捕获”,GetImageBasedReflectionLighting就是负责采样辐照图贴图的函数了,UE4默认都是针对Cube的球采样,如果要是移动端性能需要使用反射向量,双抛物面采样等方式,就要改动比较多的东西,因为许多变量都是从GetMobileReflectionCaptureUniformBuffer传到shader中的

从4.23~4.26【再早的版本没怎么看过~】每一次版本更新,反射采集或多或少都有所更改,4,23~4.24增加了MobileComputeMixingWeight用来对反射进行缩放,4.25与4.26分别对GetImageBasedReflectionLighting进行了重构,具体算法与函数封装变动大家可以自行去阅读代码。

(4)Step4:几种混合模式

(5)Step5:动态点光遍历

Mobile默认最高支持4盏动态点光,UE4的Point Light Function也是的物理的,支持BRDF运算。具体在第三章:Disney原则的PBR的物理照明环节进行详细分析

(6)Step6:vlm,Lightmap计算方式的定义

ComputeIndirect在处理内部计算时会分成两段静态分支,一支采样Lightmap用于处理烘焙的StaticMesh,注意移动端是LQ编码。关于UE4lightmap编码解码方式网上有文章分析的很详细了[3]。

在第一章PRT部分有说UE4的vlm多好多好,但那是PC端的~~,移动端是一个简化版,不是逐像素的,球谐的阶数与PC端也不同,所以能提供的间接光质量会PC差不少。

计算得到的lightmap与vlm会返回到Color(颜色)与IndirectIrradiance(强度),以便继续参与后续光照分量的叠加。

(7)Step7:pixel shder 的main函数

该定义的功能函数都定义了,接下来进入main函数,让我们一步步拆解UE4的光照组成,尝试弄清楚着色管道每一个环节都发生了什么,每一个光照组分背后的物理含义,以及它们又是如何影响最终画面效果的

1.首先是结构体的初始化

2.像素深度偏移

3.数据存入GBuffer

4.shadingmodelContent初始化

5.计算环境漫反射光照

现在用于着色计算的数据都有了,该叠加光照了,光栅化嘛PRT嘛就是把一层层效果叠加到framebuffer上,叠加方式要么是乘要么就是加。

第一个光照组分是环境漫反射光照

此时GI = vlm or lightmap,这一阶段影响PC与移动端渲染质量差异的主要因素来自Lightmap编码方式与VLM的计算精度

6.天光球谐计算

我们知道Skylight Component可以看做一种特殊的反射探针,本质上是“天光球谐 + 天光IBL”,天光球谐是这这一步算的,天光IBL则是在后续阶段与EnvRefelction的IBL一起通过GetImageBasedLighting函数实现

不过可以看到,这一步只是叠加了强度值,颜色值会在后面叠加到Color上,此时GI = vlm or lightmap + skylight

这一步影响画面与性能的主要因素是天光球谐的阶数。

7.迭上AO

AO的算法与策略对画面层次感影响比较大。要实现暗部没那么死黑,整体亮度控制在一个比较亮的范围内,间接光反弹肯定要很好,间接光亮了,还要考虑AO能不能把漏光的地方压下去,烘焙的AO压肯定比较暴力(容易出现亮部调整起来没那么明显,AO很微弱,但是暗部调整起来又幅度比较大)就很难均衡亮部与暗部的层次,就又回到了是否使用SSAO的问题上。

SSAO肯定比烘焙AO效果要好,但是SSAO也有很多细分变种与优化策略,不同的方案对性能与画面影响是不同的,这也就是为什么在项目前期预研阶段,一定要有做渲染的TA参与进去的原因。

8.计算CSM阴影

只是一个基础的CSM阴影,没有做“封闭百分比过滤”,什么画面提分项都没有,所以shadow module也是一个潜在的画质提升思路。

9.计算直接光照

通过封装好的MobileIntegratBxDF来计算不同ShadingModel的直接高光反射直接漫反射对于PBR来说,高光非常的重要,所以这里也是一个容易出现画面效果差异的地方,主要看ShadingModel怎么定义的以及BxDF模型。可以看到上一步算得的Shadow作为屏蔽因子乘到了直接光照上,这也符合PBS的思想:

shadow就是乘到直接光照上的,AO就是乘到间接漫反射上的,SO或BentNormalMap就是作用到环境高光上的。从效果上来说它们用于塑造不同的明暗分布与层次,从渲染上来说,它们用于解决不同的漏光问题。

10.计算IBL

不同的光照模型会对应不同的环境反射表现,所以这里依旧设计成通过静态分支来区分不同的环境反射,比如ClearCoat就需要计算两层IBL

我们自己添加新的shadingmodel思路也是这样的,反射辐照图对低粗糙度物体的质感表现影响特别大,如何迭代HDR图也是一个提升品质的重要方向。卷积过滤采样就那些东西,与其硬刚算法还不如在keyshot或者PS里处理下用于生成irrandance map的HDR图来的直接.

此时的GI = DirectionLight + IBL + (skylight + vlm&lightmap) * DiffuseColor * AO

11.迭上天光漫射颜色(球谐算得的颜色)


12.迭上自发光

移动端叠加自发光的方式也是最简单的那种,什么都没有拿到后直接加上去

此时的GI = DirectionLight + IBL + (skylight + vlm&lightmap) * DiffuseColor * AO + Emissivep

13.进行混合

4,移动端延迟着色

(1) GBuffer设计

主要在MobileDefrredShading.usf中

移动端的Macro Buffer使用了5RT的方案,存储的信息为:

  • GBufferA
    • rg:world normal
    • b:预计算的阴影因子
    • a:PerObjectGBufferData
  • GBufferB
    • r: Metallic
    • g:Specular
    • b:Roughness
    • a:ShadingModelID
  • GBufferC
    • rgb:basecolor
    • a:AO
  • GBufferD
    • rgba :预留的,用于存储CustomData
  • SceneDepth
    • 场景深度

(2) 延迟着色

目前支持还比较基础,直接到MobileDeferredShadingPS函数,一共以下几个着色阶段:

1.初始化Macro GBuffer与RT数据写入

2.计算CSM阴影

3.初始化ShadingModelContent

4.灯光列表索引

5.直接光照计算

6.环境光照计算

7.天光球谐计算

Reference:

[1] 《Practical Real-Time Strategies for Accurate Indirect Occlusion》:https://www.activision.com/cdn/research/Practical_Real_Time_Strategies_for_Accurate_Indirect_Occlusion_NEW%20VERSION_COLOR.pdf

[2] GGX Mobile : https://gist.github.com/romainguy/a2e9208f14cae37c579448be99f78f25

[3] UE4 Lightmap格式解析: https://zhuanlan.zhihu.com/p/68

;