流云 发表于 2022-11-15 18:33:33

Unity3D图形性能优化最佳攻略 游戏开发

Unity官方文档中有一篇是讲图形性能优化的,这篇文章无疑是指导Unity开发图形优化的最佳文章。Unity圣典曾翻译过旧版,但是太老了,跟最新文档差别很大。我试着翻译一下最新文档。

点击查看E文链接

图形性能优化

良好的性能是很多游戏成功的关键。以下是一些最大化提高游戏图形渲染速度的简单指导。

图形开销在哪里

游戏的图形显示部分主要消耗计算器的两个系统:GPU和CPU。任何优化的第一规则都是查明性能瓶颈在哪里,因为优化GPU和优化CPU的策略常常是不同的(甚至常常是相互对立的——常常为了优化CPU而把它的一些工作交给GPU,反之亦然)。

典型的瓶颈和检查方法:

GPU常常受限于填充率和存储器带宽。

如果游戏在较低的显示分辨率上运行更快,则很可能是受限于GPU的填充率。

CPU常常受限于需要渲染的物品数量,我们通常叫它“draw calls”(绘制调用,一般用专业术语“draw call”)

在Rendering Statistics窗口查看“drawcalls”,如果在PC上它常常多达几千个,或者在移动设备上多达几百个,那么你可能需要优化物体的数量。

当然,这只是根据经验的主要开销点,而瓶颈也可能在别处。不那么典型的瓶颈有:

GPU和CPU在渲染方面都没有问题!比如,你的脚本或者物理可能是真正的问题所在,使用Unity的Profiler查明问题。

GPU处理太多的顶点。到底多少个顶点是ok的,依赖于具体的GPU设备和顶点着色器的复杂度。典型的数字是,在移动设备上“不要超过10万个”,在PC上“不要超过数百万个”。

CPU处理太多的顶点 —— 针对CPU处理顶点的情况。这可能是蒙皮网格、衣服模拟、粒子等。

CPU优化:draw call数量

为了渲染一个物体到屏幕上,CPU需要做一些工作——比如,哪些灯光影响物体,建立着色器和着色器参数,给显卡驱动发送绘制命令,然后准备发送给显卡的命令。单个物体的CPU开销并不昂贵,但是如果有很多可见物体,这些开销会累加。

所以,举例来说,比如你有1000个三角形,相比每个三角形一个独立的网格,它们都在一个网格中对CPU开销要低得多。这两种方案对于GPU来说差别不大,但是CPU渲染1000个物体(代替1个)的开销多很多。

为了让CPU做更少的工作,减少可见对象的数量是很有效的:

合并接近的物体。可以手工合并,也可以利用Unity的drawcall batching(批量draw call)。

在物体中使用更少的材质。可以把独立的纹理合并成一个更大的纹理图集。

避免使用导致物体被渲染多次的效果(比如反射、阴影、像素光照等,见下面)。

合并物体使每个网格有至少几百个三角形,并且整个网格只使用一种材质。合并两个不共用同一材质的物体并不会提升性能,理解这一点很重要。拥有多个材质的最常见原因是两个网格不共用相同的纹理,所以为了优化CPU性能,你要确保合并的物体共用相同的纹理。

然而,如果在正向渲染路径下使用很多像素光照,有一些情况下合并物体并没有效果,下面解释。

GPU:优化模型几何

有两个优化模型几何的基本原则:

不用使用任何非必要的多余三角形

尽可能减少UV贴图接缝和硬边(顶点增多了一倍)的数量。

注意,图形硬件处理的顶点实际数量常常跟3D应用程序报告的不一致。建模应用常常显示几何顶点数量,即构成模型的不同角点的数量。然而,图形显卡为了渲染目的可能会把一些几何顶点拆分成两个或者更多个逻辑顶点。如果一个顶点有多个法线、UV坐标或者顶点颜色,那么必须把它拆分。因此,Unity中的顶点数量一定会比3D应用程序给的定点数多。

模型的几何数量主要对GPU有意义,Unity中的一些特性也在CPU上处理模型,比如网格蒙皮。

光照性能

不需要计算的光照是最快的。使用光照贴图烘焙静态光,只需要一次,代替了每帧计算。生成光照贴图环境,比在Unity的场景中放一个光源消耗的时间仅仅多一点,但是:

1、它的运行速度要快很多(对于2个逐像素光照,快2-3倍)

2、并且,因为可以烘焙全局光照,并且lightmapper(光照贴图工具)可以让烘焙的结果更平滑,所以它看起来效果也好很多

许多情况下,有一些着色器和内容的简单技巧,而不是在所有地方添加更多的光源。比如,为了获得“边缘光照”效果,可以直接在着色器中添加一次“边缘光照”计算,而不是添加一个直射相机的灯。

正向渲染中的光照

对每一个受到影响的像素,逐像素动态光会累加可观的渲染耗费,并且会导致物体在多个通道被渲染。在性能比较差的设备上,比如移动设备或者低端PC的GPU,避免使用多于一个的像素灯照射任何单个物体,并且使用光照贴图来照亮静态物体而不是每帧计算光照。逐顶点动态光照会在顶点转换上累加客观的消耗。努力避免多个灯光照射任何给定物体的情况。

如果你使用像素光照,对于每一个网格,像素光照射它多少次,它将被渲染多少次。如果你合并两个相距较远的网格,将会增大合并物体的有效大小。渲染的时候,照射到合并物体任一部位的所有的像素光都会被计算,所以,需要渲染通道数量会增加。一般地,渲染合并物体的通道数量等于分别渲染独立物体的通道数量之和,所以,合并没有作用。因此,你不应该合并足够远以至于受不同像素光影响的网格。

渲染的时候,Unity查找所有网格周围所有的光,并计算哪一个对网格影响最大。QualitySettings(质量设置)可以修改最终多少个光是像素光,多少个是顶点光。每一个光基于距离网格的距离计算它的权重和光照强度。此外,取决于游戏内容,有些光比别的光更重要。因此,每个光源有渲染模式设置,可以把它设置为重要或者不重要,标记为不重要的光一般有更低的渲染开销。

举例来说,考虑一个赛车游戏,玩家的车开着车头灯,在黑夜中行驶。车头灯是游戏中最重要的可见光,所以它们的渲染模式可能要设置为重要。另一方面,可能游戏里的其它灯光没那么重要(比如其它汽车的尾灯),对这些灯光来说,使用像素光照提升可视效果作用不大,可以把它们设置为不重要,避免在只能获得较少效果的地方浪费渲染性能。

对于CPU和GPU来说,优化逐像素光照都可以减少开销:CPU需要处理的draw call少了,GPU需要处理的顶点和光栅化所有这些额外对象的渲染的像素少了。

GPU:纹理压缩和多重纹理

使用压缩纹理会减少纹理大小(结果是更快的加载速度和更小的内存占用)并且大幅提高渲染性能。压缩纹理占用的存储带宽只有未压缩的32位RGBA纹理的一小部分。

使用多重纹理

作为经验,在3D场景中使用的纹理总是启用生成多重纹理。以同样的方式,GPU渲染时,纹理压缩可以帮助限制传输的纹理数据量,因为对于较小的三角形,多重纹理允许GPU使用较低分辨率的纹理。

这条规则的例外是,知道texel(纹理像素)是1:1映射到渲染的屏幕像素,比如UI元素或者在2D游戏中。

LOD(多细节层次)和每层剔除距离

在一些游戏中,为了减少CPU和GPU负担,可以适当剔除小物体。比如,远距离的小石头和碎片可以设为不可见,而大的建筑物是可见的。

可以使用LOD系统,或者在相机上设置手工每层剔除距离,来做剔除。你可以把小物体放入一个独立的层,然后使用Camera.layerCullDistance脚本函数设置每层的剔除距离。

实时阴影

实时阴影效果很好,但是会消耗很多的性能,包括CPU额外的drawcall和GPU额外的处理。更多细节,看文档的阴影页面。

GPU:写高性能着色器的提示

毫不夸张的说,高端PC和低端移动设备的GPU性能可能相差几百倍,甚至在同一个平台上也相差这么大。在PC上,一个快的GPU几十倍速于低端集成GPU;在移动设备上,也是如此。

所以,请记住,在移动设备和低端PC上的GPU性能,可能比你的开发机器低得多。典型地,为了良好的性能,着色器需要手工优化来减少计算和纹理读取。例如,一些内置的Unity着色器有快得多的等价的“移动”版本(但是有些限制或者是近似值 - 就是这些使得更快)。

下面是一些针对移动设备或者低端PC显卡的指南:

复杂的数学运算

复杂的数学函数(比如pow、exp、log、cos、sin、tan等)开销很大,所以一个好的经验是不要在每个像素上使用这些函数。如果可以,考虑使用查找纹理作为替换。

不建议自己实现normalize、dot、inversesqrt等运算,使用内置的函数,驱动会生成更好的代码。

记住,alpha测试(裁剪)操作会使你的片段更慢。

浮点运算

写自定义的着色器时,应该指定浮点数精度。为了获得更好的性能,选用最小的可行浮点数格式是很关键的。运算精度在很多台式机GPU上完全被忽略,但是在移动设备GPU上,它对于性能很关键。

如果着色器是Cg/HLSL写的,精度如下:

1、float - 32位浮点数格式,适合用于顶点变换,但是性能最慢。

2、half - 减半的16位浮点数格式,适合用于纹理UV坐标,性能大约是float的2倍。

3、fixed - 10位顶点格式,适合用于颜色、光照计算和其它高性能操作,比float大约快4倍。

如果着色器用GLSL ES写的,浮点数格式分别是:highp, mediump, lowp。

了解着色器性能的更多细节,请阅读文档着色器性能页面。

让你的游戏更快的简要清单

如果目标设备是PC,保持顶点数低于20万-300万,依赖于目标GPU。

如果你使用内置着色器,选用Mobile或Unlit种类的。它们也可以在非移动平台良好工作,是复杂着色器的简化或者近似值版本。

1、保持每次场景不同材质的数量 —— 不同物体尽可能共享材质。

2、对于不移动的物体,设置Static属性,允许像静态批处理这样的内部优化。

3、除非必要,不要使用像素光 —— 只选用一个(尽可能平行光)像素光影响你的几何体。

4、 除非必要,不要使用动态光 —— 选择烘焙光照来代替。

5、如果可以,尽量使用压缩纹理格式;否则,16位格式纹理性能好于32位的。

6、除非必要,不要使用雾效果。

7、学习遮挡剔除的好处,然后使用它来降低可见几何体和有许多遮挡的复杂静态场景drawcall的数量。设计你准备从遮挡剔除获得好处的等级。

8、使用天空盒“冒充”远距离的几何体。

9、使用像素着色器或纹理合并来混合几个纹理,而不是多通道逼近。

10、 如果写自定义着色器,使用尽可能小的浮点数格式:

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

half / mediump -用于纹理UV坐标,

float / highp -避免在像素着色器中使用,用于顶点着色器的位置计算比较好。

11、在像素着色器中,尽可能少用复杂的数学函数,比如pow、sin、cos等。

12、每个片段使用较少的纹理。





Gad祝大家新年快乐!

Gad-GameDev∣腾讯游戏开发者平台

长按,识别二维码,加关注

免费组件下载丨专业知识学习

组队游戏制作丨项目孵化渠道

国内业界首家专业独立游戏开发者平台。

面向游戏从业者,

打造开放、互联、回馈、分享的专业游戏人社区。
页: [1]
查看完整版本: Unity3D图形性能优化最佳攻略 游戏开发