UnitySahder入门精要笔记---Unity中的渲染优化技术
Unity中的渲染优化技术笔记
最近一直在研究关于UnityShader的知识,由于是小白入门,对于相关的文章比较感兴趣,而对于UnitySahder入门精要的阅读和练习,也确实收益匪浅,也因此想着写下一些读书笔记来做一些记录以备自己以后翻看查阅,同时也希望为有需要的同学提供些许帮助吧,这一篇的笔记主要是记录一些自己在对于Unity中渲染优化技术一章的理解和练习吧,如有不对的地方,请大家谅解,也欢迎指正,从而学习到更多的知识!
1.移动平台特点
对于游戏的开发和性能优化永远离不开的两个话题就是CPU和GPU的优化,通俗一些的理解就是CPU的帧率保证,玩游戏不卡帧,GPU保证分辨率等相关处理,画面精致游戏感强,同时又由于手游的便携性和普遍性,相比于端游的开发,手游的开发更易于投入市场,但是也由于手机的硬件配置和PC机存在很大的区别,对于性能的优化来说,手游的优化要求也相对较多一些,在这里对于渲染性能的优化也同样适用于移动平台手游。
2.影响性能的因素分析
2.1 影响因素
上文中已经提到了CPU和GPU两个方面对于游戏性能的影响,同时带宽也是会造成一定的影响的,因此下文中也主要以这几个方面来介绍:
CPU方面
1)过多的draw call (对于draw call的研究在这里就不多解析了,有时间的话会专 门把draw call的研究写出来的)
2)复杂的脚本和物理模拟
GPU方面
1)顶点处理
a.过多的顶点
b.过多的逐顶点计算
2)片元处理
a.过多的片元(有分辨率和overdraw两种原因)
b.过多的逐片元计算
带宽
1)使用了尺寸很大未压缩的纹理
2)分辨率过高的帧缓存
2.2 性能优化技术简要分析
CPU优化
使用批处理技术减少draw call数目
GPU优化
1)减少需要处理的顶点数目
a. 优化几何体
b. 使用模型的LOD(Level of Detail)技术
c.使用遮挡剔除(Occlusion Culling)技术
2)减少需要处理的片元数目
a. 控制绘制顺序
b.警惕透明物体
c.减少实时光照
3)减少计算复杂度
a.使用Shader的LOD技术
b.代码方面的优化
节省内存带宽
1 ) 减少纹理大小
2)利用分辨率缩放
3.减少draw call 数目
批处理的实现原理就是为了减少每一帧需要的draw call 数目,对于可以进行批处理的前提是使用同一个材质,使用同一个材质的物体,之间的不同只是顶点数据的差别,可以进行顶点数据的合并,再发送给GPU,完成一次批处理。
Unity3D支持两种批处理方式,一种是动态批处理(Dynamic Batching),一种是静态批处理(Static Batching)。
动态批处理:
优点:一切处理是Unity自动完成,不需要认为进行任何操作,场景中的物体可以进行任何移动。
缺点:限制条件比较多,很容易打破动态批处理规则,导致动态批处理失效
静态批处理:
优点:自由度比较高,限制很少
缺点:占用更多内存,经过静态批处理的物体在场景中不可以再次移动,否则打破静态批处理规则,不可进行静态批处理了。
3.1 动态批处理
动态批处理不需要人为的去设置,Unity会自动进行整合,前提是这些模型共享的同一个材质,同时需要满足一些条件,在这里主要是对这些条件进行了总结:
能够进行动态批处理的网格顶点属性规模要小于900.例如,如果Shader中需要使用顶点位置、法线和纹理坐标这3个顶点属性,那么想要让模型能够被动态批处理,它的顶点数目不能超过300。需要注意的是,这个数字未来有可能会发生变化,因此不要依赖这个数据。(注:经过笔者测试,在当前Unity2017,Unity2018版本中测试,对于顶点数目的限制未发生变化,所以说该限制条件依然有效)
一般来说,所有对象都需要使用同一个缩放尺度。一个例外的情况是,如果所有的物体都使用了不同的非统一缩放,那么它们也是可以被动态批处理的。(注:该条件限制已经无效,经笔者在Unity2018中测试,不同的缩放尺度不影响动态批处理)
使用光照纹理的物体需要格外小心处理。这些物体需要额外的渲染参数,例如,在光照纹理上的索引、偏移量和缩放信息等。因此,为了让这些物体可以被动态批处理,我们需要保证它们指向光照纹理中的同一个位置。
多Pass的Shader会中断批处理。在前向渲染中,我们有时需要使用额外的Pass来为模型添加更多的光照效果,但这样一来模型就会被动态批处理了。
3.2 静态批处理
实现原理: 只在运行开始阶段,把需要进行静态批处理的模型合并到一个新的网格结构中,这意味着这些模型不可以再运行时刻被移动。只需要一次的合并操作,任何大小且使用同一个材质的几何模型都可适用,缺点在于,它往往需要占用更多的内存来存储合并后的几何结构。这是因为,如果在静态批处理前一些物体共享了相同的网格,那么在内存中每一个物体都会对应一个该网格的复制品,即一个网格会变成多个网格再发送给GPU。如果游戏中使用的这一类模型比较多,更会增加内存上的消耗。解决方法是要么忍受,要么在符合要求的情况下使用动态批处理,或者自己编写批处理方法。
无论选择使用动态批处理或者静态批处理,这里有一些小的建议:
1 尽可能使用静态批处理,但得时间小心对内存的消耗,并且记住经过静态批处理的物体不可以再被移动。
2 如果无法进行静态批处理,而要使用动态批处理的话,小心上面提到的各种限制。
3 对于游戏中的小道具,例如可以捡拾的金币,可以使用动态批处理。
4 对于包含动画的这类物体,我们无法全部使用静态批处理,但其中如果有不动的部分,可以将这部分标识成“static”。
还有需要注意的一些地方:批处理是将多个模型变换到世界空间中进行合并,涉及到Shader中的一些基于模型空间的坐标计算,可能会出现错误的结果, 解决方案:在shader的编写中使用 DisableBatching 标签来强制使用该Shader的材质不进行批处理,另一个需要注意的是使用半透明材质的物体通常需要使用严格的从后往前的绘制顺序来保证透明度混合的正确性。对于这些物体,Unity会首先保证它们的绘制顺序,再尝试对它们进行批处理。这意味着,当绘制顺序无法满足时,批处理无法在这些物体上被成功应用。
4.减少需要处理的顶点项目
3种顶点优化策略
1.优化几何体:主要的应用阶段是美工阶段,模型制作阶段,在不影响模型造型基础上尽可能减少顶点数目,从而减少模型的三角面数。
2.模型的LOD技术:原理是,当一个物体原理摄像机很远时,模型的细节是无法被察觉的,因此可以减少模型上的面片数量,从而提高性能,也就是在Unity中使用LOD Group组件,进行模型等级随距离的划分,在这里不做细致的讲解了,后续会做LOD的试验。
3. 遮挡剔除技术:遮挡剔除技术就是在裁剪空间范围内的被其他物体遮挡看不到的物体进行消除,不会对这些不被看到的物体进行渲染,提高性能,
(注:遮挡剔除(Occlusion Culling)和视锥体剔除(Frustum Culling))区分开来。视锥体剔除只会剔除掉那些不在摄像机的视野范围内的对象,但不会判断视野中是否有物体被其他物体挡住)
5.减少需要处理的片元数目
另一个造成GPU瓶颈的是需要处理过多的片元。这部分优化重点在于减少overdraw。简单来说,overdraw 值得是同一个像素被绘制了多次。主要是在3个方面避免overdraw。
1.控制绘制顺序
由于深度测试的存在,如果我们可以保证物体都是从前往后绘制的,那么就可以很大程度上减少overdraw。这是因为,在后面绘制的物体由于无法通过深度测试,因此,就不会再进行后面的渲染处理。
在Unity中,那些渲染队列数目小于2500(如”Background” “Geometry” 和 “AlphaTest”)的对象都被认为是不透明物体,这些物体总体上是从前往后绘制的,而使用其他的队列(如”Transparent” “Overlay”)的物体,则是从后往前绘制的。这意味着,我们可以尽可能地把物体的队列设置为不透明物体的渲染队列,而尽量避免使用半透明队列。我们还可以充分利用Unity的渲染队列来控制绘制顺序。 例如将队列设置”Geometry+1”这样。
2.时刻警惕透明物体
对于半透明物体来说,本身并没有开启深度写入,如果要得到正确的渲染效果,必须是从后往前渲染,也意味着,半透明物体几乎一定会造成overdraw。例如GUI对象来说,它们大多数被设置为半透明物体,如果屏幕中GUI占据的比例太多。而主摄像机又没有进行调整而是投影整个屏幕,那么GUI就会造成大量的overdraw。
如果场景中包含大面积半透明对象,或者多层相互覆盖叠加的半透明对象,或者透明粒子效果,在移动设备上都会造成大量overdraw,
因此,我们尽量减少窗口中GUI所占的面积。也可以把GUI的绘制和三维场景的绘制交给不同的摄像机,而其中负责三维场景的摄像机的视角范围尽量不要和GUI的相互重叠。
在移动平台上,透明度测试也会影响游戏性能。虽然透明度测试没有关闭深度测试,但由于它的实现使用了discard或clip操作,而这些操作会导致一些硬件的优化策略失效。
3.减少实时光照和阴影
较为有效的方法以及当前普遍使用的方法就是使用了烘焙技术,将光照和阴影提前烘焙为一张光照纹理,当游戏运行时,只是根据纹理采样得到光照即可。
6.节省带宽
1.减少纹理大小
在减少draw call数目中使用到的是一种将纹理整合成纹理图集,根据uv采样坐标信息来进行模型的不同纹理使用,纹理的长宽普遍为正方形,而且最好是2的整数幂,主要是这样有利于很多优化策略最大有效值。
另外就是使用多级渐远纹理技术,即mipmapping以及纹理压缩。
2.利用分辨率的缩放
过高的屏幕分辨率也是造成性能下降的原因之一,尤其是对于很多低端手机,除了分辨率高其他硬件条件并不尽如人意,这恰恰是游戏性能的两个瓶颈:过大的屏幕分辨率和糟糕的GPU。因此,我们可能需要对于特定及其进行分辨率的缩放。当然,这样可能会造成游戏效果的下降,但性能和画面需要平衡。
7.减少计算复杂度
1.Shader的LOD技术
类似与模型的LOD技术,根据距离来控制Sahder的等级,原理:只有Shader 的LOD 值小于某个设定的值,这个Shader才会被使用,而使用了那些超过设定值的Shader 的物体将不会被渲染。
2.代码优化方面
对于实现游戏效果时,存在着一定的特定运算,对于游戏对象,顶点和像素的计算的复杂程度排序是:对象数<顶点数<像素数,因此尽量将允许的计算放在每个对象和逐顶点上。
对于代码的优化规则,有以下几点建议:
1.在允许范围内,使用低精度浮点值进行运算,减少不同精度之间的换算
2.尽量不使用全屏幕的屏幕后处理效果
3.尽可能不要使用分支语句和循环语句
4.尽可能避免使用类似sin、tan、pow、log等较为复杂的数学运算。使用查找表来替代它。
5,尽可能不要使用discard操作,会影响某些硬件的优化(注:笔者对于这一点没有测试,存在一定的疑问,待研究)
8. 小结
这是笔者第一次写博文,因此也存在很多不足,以及对文章中的一些优化策略并没有进行完全的试验,有些仍然需要去摸索探究,后续有时间会继续将这些测试试验写出来的,望谅解,同时也推荐有时间的话,可以读一下《UnitySahder入门精要》这本书,书中的讲解远比笔者写的更详细全面,这里的笔记也只是笔者自己的理解。
参考:
链接:
《UnitySahder入门精要》
Unity教程之再谈Unity中的优化技术
https://www.cnblogs.com/gabo/p/4592194.html
---------------------
作者:ghaokl
来源:CSDN
原文:https://blog.csdn.net/ghaokl/article/details/86382117
版权声明:本文为博主原创文章,转载请附上博文链接!