关于《Thinking in Unity3D》
笔者在研究和使用Unity3D的过程中,获得了一些Unity3D方面的信息,同时也感叹Unity3D设计之精妙。不得不说,笔者最近几年的引擎研发工作中,早已习惯性的从Unity3D中寻找解决方案。
Unity3D虽比不上UE那么老练沉稳,气势磅礴。也比不上CE那样炫丽多姿,盛气凌人。但它的发展势如破竹,早已遍地生花!故而在此记录一些自己的心得体会,供大家参详交流。若有欠妥之处,还望各位及时指正。
HI,你好
Unity3D发展至今,已经是5.x版本了。对于一个引擎迭代来说,特性、效果、效率的提升,无不波及到渲染管线的改动。而与渲染管线最为紧密挂钩的就是材质系统和渲染流程,但由于材质系统和渲染流程涉及的范围和内容更广。我们将用单独的篇幅来描述。本文主要目标是渲染管线中的Rendering Path。
由于历史发展,以及各种平台、机型、版本兼容的原因。Unity3D中,目前保存了4种Rending Path.
1、Deferred Shading Rendering Path(延迟渲染)
2、Forward Rendering Path(前向渲染)
3、Lagacy Deferred Lighting Rendering Path(延迟光照)
4、Vertex Lit Rendering Path(顶点光照)
Forward Rendering Path(前向渲染)
传统的前向着色渲染方式是一种简单粗暴的方式。就是一次性将一个物体绘制完再绘制下一个。光照计算也是如此。前向着色最主要的问题就是光照问题。当光源过多的时候,就需要进行处理。 而Unity3D在光源处理方面,也结合了像素光照(per-pixel lighting) ,顶点光照(per-vertex lighting),球谐光照(Spherical Harmonics lighting)等多种方案,其复杂程度可见一斑。
Unity3D中前向渲染的光源策略
前向着色渲染由于效率和寄存器限制等原因。不可能老老实实地去处理所有的光源。因此,需要根据光源的远近,范围,重要程度等因子决定计算方式。
1、如果一个光源的RenderMode没有设置为重要(Important),那么它将永远是逐顶点或者球谐光照计算方式。
2、最亮的(Brightest)方向光源会做为逐像素光照计算。
3、如果一个光源的RenderMode设置为重要(Important),那么它将会采用逐像素光照计算方式
4、如果上面的结果中的用于逐像素计算的光源数目小于了Project Settings->Quality面板中的Pixel Light Count值。 为了减少亮度误差,有一些光源会被当作像素光源计算(PS:非像素光源无法完全计算阴影关系。会导致场景偏亮)。
5、Unity3D对顶点光照,像素光照做了最大限制。 分别为4个。而场景中不可能只有这么多光源。因此,使用了一个优先级判定。
左:光源位置图 右:光源分配图
从图中我们可以看到。 D参与了像素和顶点光照。 G参与了顶点光照和球谐光照。 这样的处理应该是为了做一个光照信息的平滑衔接,不至于变得很突兀。
Unity3D中前向渲染的光照的计算过程
前向渲染中,光照计算被分成了多个PASS。通常它们由1*FarwardBase Pass + N*FarwardAdd Pass组成。可以看出,光源的数目在前向渲染管线中是一个致命的效率杀手。
1、Base Pass 提交一个逐像素方向光和所有的逐顶点光照和球谐光照。
2、Additional Pass 提交一个逐像素的光照计算。 如果一个物体受更多的光源影响。那么就会有许多PASS需要进行绘制。
注:在Shader中。可以指定FarwardBase来关掉Additional 功能。
注:在前向渲染中,只能有一个方向光实时阴影。
Unity3D中的球谐光照
球谐光照是一个CPU光照算法。它可以处理大规模的光源信息,非常适合一些很小的动态点光源。但是它存在以下几个问题。
1、球谐光照是基于物体顶点的算法,并非像素级。这就意味着它不能使用一些像素级的效果增强手段。比如法线贴图。
2、基于效率的考虑,球谐光照的更新频率非常低。如果光源移动过快,就会穿邦。
3、球谐光照是基于全局空间的计算,当一个面离点光源或者聚光灯很近时,效果是错的。
Deferred Shading Rendering Path(延迟渲染)
延迟渲染技术简介
延迟渲染技术已经是一个成熟且稳定的技术。已经广泛用于各种商业引擎中。它主要解决的是当光源数目过多时带来的复杂光照计算的开销。 在传统前向渲染中,假设一个场景中有M个物体,N个光源。 那么理论上进行的光照计算次数为 M*N。 而DS管线可以使其变为M+N。
但是DS管线并非万能的。有以下情况需要注意
延迟渲染技术缺陷
1、DS需要多渲染目标(MRT)的支持、深度纹理、双面模板缓存。
这个特性,需要显卡支持SM3.0的API。2006年以后的PC显卡应该不成问题。比如GeForce 8xxxx,AMD Radeon X2400,Intel G45以后的显卡。
2、DS管线会消耗更多的显存,用于缓存G-Buffer。同时,会要求显卡拥有更大的位宽(bit counts)。
在手机上目前(2016年)还不实现。
3、DS管线无法处理半透明物体。
半透明物体需要退回到传统前向渲染中进行
4、硬件抗锯齿(MSAA)无法使用。
只能使用一些后期算法,如FXAA等。
5、这句话中的内容目前未测试,故无法准确体会其中的意思:There is also no support for the Mesh Renderer’s Receive Shadows flag and culling masks are only supported in a limited way. You can only use up to four culling masks. That is, your culling layer mask must at least contain all layers minus four arbitrary layers, so 28 of the 32 layers must be set. Otherwise you will get graphical artefacts.
延迟渲染技术在Unity3D中的实现方案
Unity3D中,延迟渲染管线为分两个阶段进行。G-Buffer阶段和光照计算(Lighting)阶段。
G-Buffer阶段
Unity3D渲染所有的非透明对象到各个RT中,RT的内容分布见 G-Buffer内容。Unity3D将各个RT做成了全局变量,方面Shader中进行操作。像这样:CameraGBufferTexture0 .. CameraGBufferTexture3
光照计算阶段
这个阶段的主要目的就是根据G-Buffer的内容进行光照计算。由于是屏幕空间的计算,显然要比之前的前向渲染来得容易得多。
注:实时阴影的计算是在光照计算之前的。 DS管线并不能减少实时阴影的开销。应该怎么整还得怎么整。最后,每个物体受到的实时阴影的影响会叠加到RT3中。一些特殊的爆光效果,Lightmap光影贴图效果等都会进入RT3。 参见G-Buffer内容
G-Buffer内容
•RT0, ARGB32 format: Diffuse color (RGB), occlusion (A).
•RT1, ARGB32 format: Specular color (RGB), roughness (A).
•RT2, ARGB2101010 format: World space normal (RGB), unused (A).
•RT3, ARGB2101010 (non-HDR) or ARGBHalf (HDR) format: Emission + lighting + lightmaps + reflection probes buffer.
•Depth+Stencil buffer.
•RT1, ARGB32 format: Specular color (RGB), roughness (A).
•RT2, ARGB2101010 format: World space normal (RGB), unused (A).
•RT3, ARGB2101010 (non-HDR) or ARGBHalf (HDR) format: Emission + lighting + lightmaps + reflection probes buffer.
•Depth+Stencil buffer.
注:为了减少显存和渲染开销。当前场景的摄像机开启了HDR模式时,RT3将不会被创建。而是直接使用摄像机的HDR RT。
自定义DS管线
Unity3D中,标准的DS管线是Standard系列。如果你想修改某一个部分。你可以新建一个自己的Shader。然后替换掉默认的即可。
替换的位置在Edit->Project Settings->Graphics面板中。把Deferred属性下拉框变成Custom,就可以进行替换操作了。
内置Shader设置
Lagacy Deferred Lighting Rendering Path(延迟光照)
注:从5.0开始,DL渲染已经不被Unity3D推荐,Unity3D更推荐大家新项目使用DS管线方式。因为DL方式不好实现基于PBR着色的Standard材质,以及场景反射。
注:如果摄像机设置为了正交投影。会强制退回前向渲染
注:DL也不会降低实时阴影的开销
和DS渲染管线一样,DL的出现同样是为了解决光照计算的复杂度问题。然后不同的是,DL仅仅把光照计算拿出去了。简单说来,DL管线工作流程如下。
步骤一、渲染场景中的对象,输出光照计算需要的RT(深度、法线、高光信息)
步骤二、使用上面的RT进行光照计算
步骤三、再次渲染场景中的对象,并与计算机来的光照信息结合。
不难看出,DL相比DS而言,不需要大量的G-Buffer支持。甚至不需要MRT的支持。但是对深度图的要求是必须的。如果遇上无法访问深度BUFFER的情况。那就需要做一次Pre-Depth Pass渲染。
比起DS而言,DL由于不需要MRT的支持。在硬件特性需求和位宽上,少了许多开销。
1、SM3.0
2、PC:GeForce FX、Radeon X1300、Intel 965 / GMA X3100
3、Mobile:所有支持OpenGL ES 3.0的设备,部分支持OpenGL ES 2.0的设备。
Vertex Lit Rendering Path(顶点光照)
注:这不是一个通用的技术名词,只有Unity3D中有。
注:主机平台这个不顶用。
注:不支持实时阴影和高精度高光计算。
Vertex Lit Rendering就是指,所有的光照计算都通过顶点进行。而在Unity3D中,它的主要目的是为了支持那些没有可编程管线的设备。VL也提供了几种方法,用于支持不同的材质类型。
1、Vertex 使用Blinn-Phong进行光照处理,针对没有lightmap的对象使用。
2、VertexLMRGBM 针对lightmap贴图使用RGBM加密(PC和主机平台)的对象, 没有额外的光照计算。
3、VertexLMM 针对lightmap贴图使用double-LDR加密(移动平台)的对象,没有额外的光照计算。
Unity3D对几种渲染管线的统一处理
在上面我描述中,我们发现,不管哪一种管线。都会涉及到几个部分。 1、光照计算 2、透明渲染 3、阴影计算 4、图像输出。 那Unity3D又是如何对这些进行统一流程控制的呢。我们看下面的图就明白了。
Unity3D渲染管线图
从图中我们可以看到。Unity3D的传统渲染管线和延迟渲染管线,在非透明物体渲染和光照阶段是分离的。当处理完以后,紧接着处理非透明图像效果->天空盒->透明物体渲染->后期效果。
结束语
总的来说,Unity3D的渲染管线还算稳定和易用,其的渲染管线包含了常见的三种方案。最主要目的还是在想着光照计算和效果显示的处理。同时保持对上层透明。其Graphics Commander Buffer提供了扩展管线的能力。而Graphics设置面板里,也提供了自定义延迟管线Shader的功能。然而也有诸多的美中不足,比如,正交摄像机模式下,不能使用延迟渲染技术。 延迟光照下无法实现PBR和实时环境反射抽取等等。
参考文献