© 2014 by LEEGOONZ HOME.

  • JP Lee

图形开销在哪里

应该先弄清楚图形显示消耗的是CPU还是GPU。因为优化CPU和GPU的策略常常是不同的。

GPU常常受限于填充率和Memory bandwith

GPU

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

CPU常常受限于需要渲染的物品数量(我们通常叫它“draw calls)

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

其他的原因有

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

在GPU处理时,Vertex数过多。可以处理的Vertex数是根据GPU或者Vertex Shader的复杂程度来决定的。一般建议在Mobile不要超过10万个,在PC不要超过百万个。

在CPU而言,处理的Vertex数过多。在CPU上要对Vertex进行处理的情况(如Skinned mesh, cloth simulation, particle等等)

优化CPU : Draw Call 个数

为了使渲染场景中的对象,CPU的工作量很大。如寻找受光影响的对象,进行ShaderSetting,向graphicriver发送Draw Command等。每一个对象进行这些工作的时候,都会增加CPU的开销。所以,场景中的对象数量多的话,CPU的开销也会变多。

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

为了减少CPU的工作量,减少可见对象的方法

将接近的对象合并。(直接手动合并或使用DrawCall batching)

在对象使用更少的材质(将单独的Texture合并成一个大的Atlas Texture)

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

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

GPU :优化Model Object

优化Model Object时,有两个基本原则

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

尽可能减少UV mapping seams / Hard edges(doubled-up vertices)

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

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

光照性能

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

它的运行速度要快很多(对于Per-pixel light,快2-3倍)

可以使用Global Illumination和lightmapper,让 它看起来效果更好

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

Lights in forward rendering

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

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

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

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

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

GPU : Texture压缩和多重Texture

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

使用多重纹理

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

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

LOD 和 Per-Layer Cull Distance(LOD and Per-Layer Cull Distance)

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

可以使用LOD系统,或者在相机上设置 手工 每层剔除距离,来做剔除。

你可以把小物体放入一个独立的layer,然后使用Camera.layerCullDistance脚本函数设置每层的剔除距离。

实时阴影

实时阴影效果很好,但是会消耗很多的性能,包括CPU额外的draw call和GPU 额外的处理。

GPU : 写着高性能Shader的提示

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

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

复杂的数学运算

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

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

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

实数运算

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

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

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

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

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

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

为了快速开发游戏而做的简单目录

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

动态批处理.

在Runtime中,动态批处理工作过程如下。

以材质为依据,对场景中可见的对象进行收集和任意分类。

分类后,如果同组中的对象使用同一材质,Unity使用mobile devices的CPU,进行对象顶点的变换。

这个过程并不是在GPU中完成的。

只是把结果临时存在Vertex buffer中。

设定这组对象的材质和shader。

描绘一次合并的Geometry。

顶点变换和draw call(dp)都通过CPU进行处理

设备中用一个命令处理多个数据的SIMD处理器支持ARM体系结构扩大向量浮动小数点。

利用了这个VFP coprocessor设计的Unity,通过优化的程序进行顶点变换处理。VFP coprocessor可以比GPU更快的处理这项工作。

所以为了优化Unity的性能,用VFP coprocessor,减少批处理的draw call。

如果CPU处理顶点变换的过程比用draw call(dp)更省时间的话,选择批处理会更好。

简单来说,某个顶点(只是mesh的顶点)在300个以下的对象,用CPU进行顶点变换开销更少。所以,批处理是最佳选择。

但是如果超过300个顶点的对象的话,不经过VFP coprocessor处理,直接描绘的方法更好。

即使不在同一个mesh,只是共用材质的话,也可以减少dynamic batching规格明细中调用的draw call。

dynamic batching的最大顶点数取决于Shader的使用。

顶点的属性是由数量决定的。因此,应该制定可以进行动态批处理的对象顶点的Shader属性应小于900个这一详细规则。

조회 21회