unity中的渲染优化技术——(节省带宽、减少计算复杂度)

一、节省带宽

大量使用未经压缩的纹理以及使用过大的分辨率都会造成由于宽带而引发的性能瓶颈。

1.1 减少纹理大小

使用图集可以帮助我们减少draw call数目,而这些纹理的大小同样是一个需要考虑的问题,所有纹理的长宽比最好是正方形,而且长宽值最好是2的整数幂,这是因为有很多优化策略只有在这种时候才可以发挥最大效用,在unity5中,即便我们导入的纹理长宽值并不是2的整数幂,unity也会自动把长宽转换到离他最近的2的整数幂值,但仍然要注意这个,防止由于放缩造成不好的影响。

除此以外还应该尽可能使用多级渐远纹理技术(mipmapping)和纹理压缩,在unity中我们可以通过纹理导入面板来查看纹理的各个导入属性。通过把纹理类型设置为Advanced,就可以自定义许多选项,例如是否生成多级渐远纹理,如果勾选了,unity就会为同一张纹理创建出很多不同大小的小纹理,构成一个纹理金字塔,在游戏运行中就可以根据距离物体的远近,来动态选择使用哪一个纹理,这是因为在距离物体很远的时候,我们使用了非常精细的纹理,但肉眼也是分辨不出来的。这种时候,我们完全可以使用更小、更模糊的纹理来代替,这可以让GPU使用分辨率更小的纹理,大量节省访问的像素数目。在某些设备上,关闭mipmap往往会造成严重的性能问题,除非我们确定该纹理不会发生缩放,例如GUI和2D游戏中使用的纹理等,都应该为纹理生成相应的多级渐远纹理。

纹理压缩同样可以节省带宽,但是像Android这样的平台有很多不同架构的GPU,纹理压缩变得有点复杂,因为不同的GPU架构有它自己的纹理压缩格式,例如PowerVRAM的PVRTC、Tegra的DXT格式、Adreno的ATC格式,所幸的是,unity可以根据不同的设备选择不同的压缩格式,而我们只需要把纹理压缩格式设置为自动压缩即可。但是GUI类型的纹理同样是个例外,一些时候由于对画质的要求,我们不希望这些纹理进行压缩。

1.2 利用分辨率缩放

过高的屏幕分辨率也是造成性能下降的原因之一,尤其是对于很多低端手机,除了分辨率高其他硬件条件并不尽如人意,而这恰恰是游戏性能的两个瓶颈:过大的屏幕分辨率和糟糕的GPU。因此我们可能需要对于特定机器进行分辨率的放缩,当然可能会造成游戏效果的下降,但性能和画面之间永远是个需要权衡的话题。在unity中设置屏幕分辨率可以直接调用Screen.SetResolution。

二、减少计算复杂度

计算复杂度同样会影响游戏的渲染性能,本节会介绍两个方面的技术来减少计算复杂度。

2.1 shader的LOD技术

shader的LOD技术可以控制使用的shader的等级,它的原理是,只有shader的LOD值小于某个设定的值,这个shader才会被使用,而使用了那些超过设定值的shader的物体将不会被渲染。

我们通常在subshader中使用类似下面的语句来指明该shader的LOD值:

SubShader{
Tags{ "RenderType"="Opaque" }
LOD 200
}

我们也可以在unity shader的导入面板上看到该shader使用的LOD值,在默认情况下,允许的LOD等级是无限大的,这意味着任何当前显卡支持的shader都可以被使用,但是在某些情况下我们可能需要去掉一些使用了复杂计算的shader渲染,这时我们可以使用Shader.maximumLOD或Shader.globalMaximumLOD来设置允许的最大LOD值。

unity内置的shader使用了不同的LOD值,例如Diffuse的LOD为200,而Bumped Specular的LOD为400。

2.2 代码方面的优化

在实现游戏效果时,我们可以选择在哪里进行某些特定的运算,通常来讲游戏需要计算的对象、顶点和像素的数目排序是对象数<顶点数<像素数。因此我们应该尽肯能把计算放在每个对象或逐顶点上,例如高斯模糊或边缘检测时,我们把采样坐标的计算放在了顶点着色器中,这样的做法远好于把它们放在片元着色器中。

在具体的代码编写上,不同的硬件甚至需要不同的处理。因此一些普遍的规则在某些硬件上可能并不成立,更不幸的是,通常shader代码的优化并不那么直观,尤其是一些平台上缺少相关的分析器,例如IOS平台,尽管如此,本节还是会给出一些被认为是普遍成立的优化策略,如果发现某些设备上性能反而有所下降的话,并不奇怪。

1. 尽可能使用低精度浮点值进行运算,最高精度的float/highp适用于存储诸如顶点坐标等变量,但它的计算速度是最慢的,我们应该尽量避免在片元着色器中使用这种精度进行计算,而half/mediump适用于一些标量、纹理坐标等变量,它的计算速度大约是float的两倍。而fixed/lowp适用于绝大多数颜色变量和归一化后的方向矢量,在进行一些对精度要求不高的计算时,我们应该尽量使用这种精度的变量。它的计算速度大约是float的4倍,但要避免对这些低精度变量进行频繁的swizzle操作(如color.xwxw)。还需要注意的是,我们应当尽量避免在不同精度之间的转换,这有可能会造成一定的性能下降。

2. 对于绝大多数GPU来说,在使用插值寄存器把数据从顶点着色器传递给下一个阶段时,我们应该使用尽可能少的插值变量,例如如果要对两个纹理坐标进行插值,我们通常会把它们打包在同一个float4类型的变量中,两个纹理坐标分别对应了xy分量和zw分量,对于PowerVR平台来说,直接把不同纹理坐标存储在不同插值变量有时反而性能更好,这种插值变量是非常廉价的。

3. 尽可能不要使用全屏后处理效果,如果美术风格实在需要使用类似bloom、热扰动这样的屏幕特效,我们应该尽量使用fixed/lowp进行低精度运算(纹理坐标除外,可以使用half/mediump)。那么高精度的运算可以使用查找表(LUT)或者转移到顶点着色器中进行处理。除此之外,尽量把多个特效合并到一个shader中,例如我们可以把颜色校正和添加噪声等屏幕特效在Bloom特效的最后一个Pass中进行合成。

还有一些其他代码优化规则:

4. 尽可能不要使用分支语句或循环语句。

5. 尽可能避免使用类似sin、tan、pow、log等较为复杂的数学运算,我们可以使用查找表来作为替代。

6. 尽可能不要使用discard操作,因为这会影响硬件的某些优化。

2.3 根据硬件条件进行缩放

诸如IOS和Android这样的移动平台,不同设备之间性能千差万别,如何确保游戏可以同时流畅地运行在不同性能的移动设备上呢?一个非常简单的实用方式是使用所谓的放缩(scaling)思想,我们首先保证游戏最基本的配置可以在所有的平台上运行良好,而对于一些具有更高表现能力的设备,我们可以开启一些更“养眼”的效果,比如使用更高的分辨率、开启屏幕后处理特效、开启粒子效果等。

版权声明:本文为CSDN博主「小橙子0」的原创文章,
遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/cgy56191948/article/details/102986224