优化图形性能        

良好的性能对大部分游戏的成功具有决定作用。下面是一些简单的指导,用来最大限度地提高游戏的图形渲染。

图形需要哪些开销

游戏的图形部分主要开销来自电脑的两个系统: GPU 或 CPU。优化的第一条原则是找到性能出现问题的地方;因为 GPU 和 CPU 的优化策略不尽相同(甚至可能截然不同 ― 因此,通常在优化 CPU 的时候会给 GPU 带来更多工作,反之亦然)。

主要瓶颈以及检查瓶颈的方式:

  • GPU 通常受供给比率或内存带宽的限制。

    • 以更低的显示分辨率运行游戏是否更快? 如果是,您极有可能受到了 GPU 上的攻击比率限制。

  • CPU 通常受需要渲染的对象的数量限制,又称“绘制调用”。

    • 在渲染统计信息 (Rendering Statistics) 窗口中查看“绘制调用”,如果数值超过数千(电脑)或数百(手机),那么您可能需要优化对象数量。

当然,这些只是经验法则;瓶颈也可能存在于其他地方。非主要瓶颈:

  • 渲染不是问题,不管是对 GPU 还是 CPU!例如,您的脚本或物理可能是问题的根源。使用分析器  找出问题所在。

  • GPU 需要处理的顶点过多。多少个顶点比较“适中”取决于 GPU 以及顶点着色器的复杂程度。对于手机来说,主要图形“不应超过 10 万个”,电脑则“不应超过 1000 万个”。

  • CPU 要处理的顶点过多,因为这些对象要在 CPU 上进行顶点处理。这可能是蒙皮网格、棉布仿真、粒子等。

CPU 优化 ― 绘制调用数量

为了渲染屏幕上的所有对象,CPU 任务艰巨 ― 这些工作包括识别哪些光照效果会影响对象,设置着色器和着色器参数,向显卡驱动发送调用命令,然后显卡驱动准备将命令发送至显卡。所有这些“逐对象” CPU 开销都不小,因此,如果您拥有大量可见对象,它可以累计。

例如,如果您有 1000 个三角形,如果所有三角形都在一个网格内,而不是 1000 个独立的网格分别拥有一个三角形,那么,成本将明显降低。

 这两种情况在 GPU 的开销非常类似,但是 CPU 渲染 1000 个对象(而不是一个)需要进行非常繁重的工作。

为了减少 CPU 的工作,最好减少可见对象的数量。

  • 手动或使用 Unity 的绘制调用批处理将邻近的对象组合在一起。

  • 通过将独立的纹理放在更大的纹理地图集等操作,在对象中使用更少的材质。

  • 尽量避免使用会导致对象多次渲染的效果(反光、阴影、逐像素光照等,如下所示)。

将对象组合起来,让每个网格至少拥有数百个三角形,且每个网格仅使用一种材质 (Material)。组合两个材质不同的对象不会给性能带来任何的改善,理解这一点非常重要。拥有多种材质最常见的原因是两个网格不共享相同的纹理,因此,如果要优化 CPU 性能,应该确保组合的所有对象共享同样的纹理。

但是,在正向渲染路径中使用大量像素光照时,有一些情况会使得合并物体不奏效,解释如下。

GPU: 优化模型几何体

在优化模型几何体时,有两条基本规则:

  • 切勿使用过多的三角形

  • 尽量降低 UV 贴图接缝和硬边缘的数目(顶点增加一倍)

请注意,图形硬件处理顶点的实际数量通常和 3D 应用程序显示的数量有所不同。建模应用程序通常显示几何顶点的数量,例如构建模型不同角点的数量。但是,对于图形卡,将需要一些几何顶点拆分成两个或两个以上的逻辑顶点来渲染。如果顶点有多个法线、UV 坐标或顶点颜色,则必须分割。因此,在 Unity 的顶点计数始终比 3D 应用程序计数高很多。

虽然模型中的几何体的总数主要与 GPU 相关,Unity 的某些特性可以在 CPU 上处理模型,例如,网格蒙皮。

光照性能

完全不需要计算的光照始终是最快的!使用光照贴图 “烘焙”一次静态光照,而不是逐帧计算。生成光照贴图环境的过程仅比在 Unity 将光照直接放置在场景稍微长一点点,但是:

  • 它的运行更快(2 种逐像素光照快 2-3 倍)

  • 由于可以烘焙全局光照,且光照贴图渲染器可以使渲染结果更光滑,因此,它的视觉效果更好。

在很多情况下,这是着色器和内容可以使用的简单的技巧,而无需在场景中添加更多的光照。例如,您无需添加直接照射至相机的光照,取得“边缘照明”效果,只需直接在着色器中添加专门的“边缘照明”计算即可。

正向渲染光照

逐像素的动态照明将显著增加每个受影响的像素的渲染开销,并可能导致对象多次渲染。在硬件较弱的设备上,如手机或低端 PC GPU,应避免多于一个像素照明 (Pixel Light) 照亮任何单一物件,并尽量使用光照贴图照亮静态物体,而不是逐帧计算其光照。逐顶点的动态照明显著增加顶点转变的开销。尽量避免多个灯照亮任何给定物体。

如果使用像素光照,那么,每个网格渲染的次数和被像素灯照亮的次数一样。如果结合两个距离较远的网格,就将增加组合物体的有效大小。照亮这个组合物体的任何一部分的所有像素灯都会在渲染过程中计算,所需的渲染通道数量也会增加。一般而言,渲染组合物体的通道数量是每个单独物体的通道数量之和,因此,组合不会有任何好处。出于这个原因,您不应组合距离较远而不会同时受到不同像素灯影响的网格。

在渲染时,Unity 找到了网格周围的所有灯光,并计算出哪些灯光对网格具有最大的影响。质量设置 (Quality Settings) 用来修改最终成为像素光照的灯光数量,以及顶点光照的数量。每种灯光都会按照灯光与网格的距离,以及灯光的强度计算其重要性。此外,仅从游戏环境来看,某些灯光更为重要。因此,每种灯光都可以设置渲染模式 (Render Mode)重要 (Important) 或者不重要 (Not Important)。标记为不重要 (Not Important) 的光照通常具有较低的渲染开销。

举个例子,试想一下一款赛车游戏,玩家的汽车打开车头灯在夜间行驶。车头灯是游戏中最重要的光源。因此,它们的渲染模式 (Render Mode) 应设置为重要 (Important)。另一方面,在游戏中其他不那么重要的灯(如其他汽车的尾灯),即使成为像素灯也不会很大地提升游戏的视觉效果。此类灯光的渲染模式 (Render Mode) 可放心设置为不重要 (Not Important),避免在不能改善游戏的地方浪费渲染性能。

优化逐像素光照可同时节约 CPU 和 GPU: CPU 的绘制调用更少,GPU 要处理的顶点更少,且所有这些额外的对象渲染需要的点阵化像素更少。

GPU: 纹理压缩和 Mipmaps

使用压缩纹理 (Compressed Textures) 可以减少纹理的尺寸(使加载时间更快,内存占用更少),同时也可以显著增强渲染性能。压缩纹理仅使用未压缩 32 位 RGBA 纹理所需的内存带宽的一小部分。

使用 Mip Maps 纹理

根据经验,三维场景中使用的纹理应始终启用生成 Mip Maps (Generate Mip Maps)。在 GPU 渲染时,纹理压缩可以以同样的方式帮助限制纹理数据传输量,mip 贴图的纹理让 GPU 能让较小的三角形使用较低分辨率的纹理。

此规则的唯一例外是当 texel(纹理像素)1:1 映射到渲染屏幕像素时,如 UI 元素或二维游戏中。

LOD 和每层消隐距离 (Per-Layer Cull Distances)

在一些游戏中,可能更多地要剔除小对象,以减少 CPU 和 GPU 负荷。例如,在足够远的距离内,大型建筑物依然可见,而小石块和碎片可以隐藏。

这可以通过细节层次  系统或在相机上手动设置每一层的消隐距离实现。可以将更小的对象放在隔离层中,并使用 Camera.layerCullDistances 脚本功能设置每层消隐距离。

实时阴影

实时阴影非常精美,但是它们可能消耗大量性能,不管是 CPU 的额外绘制调用,还是 GPU 的额外处理。更多详细信息,请参阅阴影页面。

GPU: 编写高性能着色器的技巧

高端 PC GPU 和低端移动 GPU 的性能可能有天壤之别。就算单个平台同样如此。在 PC,高速 GPU 的运行速度可能比迟钝的集成 GPU 快几十倍;在移动平台上,您也会发现 GPU 大有不同。

因此,请记住,移动平台和低端 PC 的 GPU 性能比您的开发机器要慢得多。一般来说,着色器需要手动优化,以减少计算和纹理读取,获得良好的性能。例如,一些内置 Unity 着色器的“手机”等价物更为快速(但是有一些限制或极限,这也正是其快速运行的原因)。

以下是一些指导,让您了解哪些因素对于移动和低端 PC 显卡最为重要:

复杂的数学运算

复杂的数学函数(如 powexplogcossintan 等等)会大大增加 GPU 负担,所以一个好的经验法则是,此类运算在每个像素中不得超过一个。考虑在合适时使用查找纹理作为替代选择。

但是,我们不建议您编写自己的 normalizedotinversesqrt 等运算。如果使用内置运算,驱动程序会为您生成更好的代码。

请记住:alpha 测试(抛弃 (discard))运算会让片段速度变慢。

浮点运算

编写自定义的着色器时,应始终指定浮点变量精度。为获得最佳性能,挑选精度尽可能小的浮点格式至关重要。很多台式机 GPU 均完全忽略运算精确,但是它对于大量移动 GPU 的性能具有重大影响。

如果着色器使用 Cg/HLSL 编写,那么精度规定如下:

  • float ― 完整的 32 位浮点格式,适合用于顶点变换,但性能最慢。

  • half ― 简化的 16 位浮点格式,适用于纹理 UV 坐标且比 float 大约快两倍。

  • fixed ― 10位定点格式,适合色彩、照明计算和其它高性能操作,速度大约比 float 快 4 倍

如果着色器以 GLSL ES 编写,那么浮点精度将分别规定为 highpmediumplowp

让游戏速度更快的简易检查表

  • 保持顶点数在 200K 以下,针对 PC 时每帧应为 3M,主要取决于目标 GPU。

  • 若使用内置着色器,请在移动 (Mobile) 或未点亮 (Unlit) 的类别中选择。它们对非移动平台同样有效,但是是更复杂的着色器的简化和限制版本。

  • 每个场景中不同材质的数量尽可能少 ― 不同对象之间尽可能共享相同的材质。

  • 在非移动对象上设置静态 (Static) 属性,允许静态批处理 (static batching) 等内部优化。

  • 切勿在不必要的情况下使用像素灯 ― 选择只有一个(最好是方向光)像素灯的光线影响您的几何图形。

  • 切勿在不必要的情况下使用动态光源 ― 而应选择烘焙照明。

  • 可能的话使用压缩纹理格式,否则使用 16 位纹理优于 32 位。

  • 切勿在不必要的情况下使用雾效。

  • 了解遮挡剔除  的好处,在有大量遮挡的复杂静态场景中,可以用它来减少可见几何体的数量和绘制调用。计划您的关卡,以充分利用遮挡剔除。

  • 使用天空盒制造“伪造”的遥远几何体。

  • 使用像素着色器或纹理组合器混合多种纹理,而不使用多通道方法。

  • 如果编写自定义着色器,应始终使用最小的浮点格式:

    • fixed / lowp ― 用于颜色、光照信息和法线,

    • half / mediump ― 用于纹理 UV 坐标,

    • float / highp ― 避免在像素着色器中计算顶点的位置,而应使用顶点着色器。

  • 在像素着色器中尽量避免使用复杂数学运算,如 powsincos 等。

  • 每个片段中使用较少的纹理。


,