1、优化流程
优化是指选择一个应用程序并使其更有效率的过程。对于图形应用程序,这通常是指对应用程序进行修改,使其运行更快。
优化流程包含下列步骤:
a. 使用分析器测量应用程序。
b. 分析数据,找到瓶颈。
c. 确定要运用的相关优化。
d. 验证优化是否成功。
e. 如果性能不可接受,请返回至 a ,然后重复该流程。
2、Unity 质量设置
Unity 包含许多可以改变游戏图像质量的选项。部分选项计算量较高,会对游戏性能产生不利影响。
下图显示了检视面板中的质量设置:
有许多选项可以提高游戏图像质量,代价只是极小幅度地降低性能。
例如,如果游戏的帧率较低,GPU 可能正在处理过多执行复杂图形效果的信息。
可以执行较简单版本的图形效果,例如阴影或光照。
相比而言,这对图形质量产生的影响较小。更简单的效果可以显著降低 GPU 负载,从而提供更高的帧率。
光照默认设置有时对于移动设备而言过于复杂,因此针对移动平台编写的部分游戏应避免复杂的技术,或者根据每个游戏使用相应的技术。这可能涉及将光照预烘焙为光照贴图的技术,或者用投影纹理代替投射阴影的技术。
在QualitySettings中,有许多可以对游戏性能产生巨大影响的选项:
a、像素光源数量
像素光源数量是指可以影响给定像素的光源数量。较高的像素光源数量需要执行大量计算。大部分游戏能够在使用极少量动态实时光源的同时,最大限度降低对质量的影响。如果光照引发了性能问题,请考虑在游戏中使用光照贴图和投影纹理等技术。
b、纹理质量
纹理质量可以给 GPU 带来负载,但通常不会引发性能问题。降低纹理质量会对游戏的视觉质量产生不良影响,所以请只在必要的情况下降低纹理质量。在冰穴演示中,纹理质量设为全分辨率。
如果纹理引发了性能问题,可尝试使用纹理映射。纹理映射可降低计算和带宽要求,同时不会影响图像质量。
c、抗锯齿
抗锯齿是一项边缘平滑技术,该技术混合了三角形边周围的像素。这显著提高了游戏的视觉质量。有多种抗锯齿方式,但是此种情况下采用的是多重采样抗锯齿 (MSAA)。4x MSAA 在 Mali GPU 上的运算量较低,应尽可能使用。
d、软粒子
软粒子需要渲染到深度纹理或在延迟模式下渲染。这会提高 GPU 负载,但可以获得逼真的粒子效果,因此值得采用。在移动平台上,渲染到深度纹理和从深度纹理读取会消耗掉宝贵的带宽,并且使用延迟路径进行渲染意味着你不能使用 MASS。请考虑软粒子是否足够重要到需要在游戏中使用。
e、各向异性纹理
各向异性纹理技术可消除在高梯度下绘制的纹理的失真。这项技术可提高图像质量,但是非常消耗资源。除非失真特别明显,否则请避免使用此技术。
f、阴影
阴影具有高质量时,计算量较大。如果阴影引发了性能问题,请尝试采用简单的阴影或关闭阴影。如果阴影对于游戏非常重要,请考虑使用简单的动态阴影技术,例如投影纹理。
g、实时反射探测器
实时反射探测器选项对运行时性能存在显著的负面影响。
在渲染反射探测器时,立方体贴图的每一面都由探测器原点处的摄像机进行单独渲染。如果考虑相互反射,此过程会在每个反射反弹级别进行一次。在光泽反射情形中,立方体贴图纹理映射也用于应用模糊处理。
h、剔除遮罩
在渲染立方体贴图时使用剔除遮罩,从而避免对反射中任何无关几何体进行渲染。
3、应用程序分析
分析工具:
a、Unity Profiler
b、Unity Frame Debugger
c、ARM Mali Graphics Debugger
d、ARM DS-5 Streamline
Unity Profiler以一系列图表的形式提供详细的每帧性能数据,帮助你查找游戏中的瓶颈。
点击Unity Profiler的一个图表,就会看到垂直切片。如果选择某个单帧,就可以在屏幕底部的显示面板中读取该帧的信息。如果在没有修改所选帧的情况下点击另一图表,面板将显示已选择的分析器的数据。
Profiler.BeginSample() 和 Profiler.EndSample() 方法
Unity 分析器可让你采用 Profiler.BeginSample() 和 Profiler.EndSample() 方法。你可以在脚本中标记一个区域,然后附上自定义标签,此区域将作为单独的条目出现在分析器层级中。通过执行此操作,可以获取特定代码的信息,而无需采用深度分析选项,从而节省计算和内存开销。例:
1 2 3 4 5 |
|
Frame Debugger 是一款分析工具,可以在每一帧基础上追踪绘制调用。
Frame Debugger 可以从窗口菜单中选用。
左窗格中显示该帧中发出的绘制调用树。
右窗格中显示与选定绘制调用相关的其他信息,如几何体详情以及绘制它的着色器等。
下图显示了 Frame Debugger 中的 Phoenix 对象:
如果摄像机渲染到所选绘制调用的目标,可以在游戏视图中查看被渲染纹理的视觉呈现。
在一个场景中,对象渲染顺序对性能非常重要。
如果按照任意顺序渲染多个对象,则可能会出现一个对象在渲染之后被其前面的另一对象遮挡。也就是说,渲染被遮挡对象需要的计算全都浪费。
利用 Unity Frame Debugger 可以查看渲染顺序。
4、CPU优化列表之使用协程代替 Invoke()
Monobehaviour.Invoke() 方法可快速便捷地在时间延迟的情况下调用类中的方法,但存在以下局限性:
a. 它使用 C# 反射查找调用方法,这比直接调用方法的速度更慢。
b. 没有针对方法签名的编译时检查。
c. 无法提供附加参数。
下列代码显示了 Invoke() 函数:
1 2 3 4 |
|
替代方法是使用协程。协程是 IEnumerator 类型的函数,可以使用特殊 yield return 语句将控制权归还给 Unity。可以稍后再次调用函数,它将从之前中断的位置恢复运行。
可以通过 MonoBehaviour.StartCoroutine() 方法调用协程:
1 2 3 4 5 |
|
从 Monobehaviour.Invoke() 方法转为使用协程,可以在传递至处理动画状态的函数的参数上提供更高的灵活性。
5、CPU优化列表之删除空回调函数
如果代码包含 Awake()、Start() 或 Update() 等函数的空定义,则将其删除。这会产生不必要的开销,因为引擎在函数为空时仍会尝试访问它们。
6、CPU优化列表之避免每帧使用 GameObject.Find()
GameObject.Find() 函数用于循环访问场景中的每个对象。如果它在代码中使用的位置不正确,则会导致主线程大小显著增加。
更好的做法是在启动时调用 GameObject.Find() 并缓存结果,将结果缓存在 Start() 或 Awake() 函数中。
7、CPU优化列表之使用 StringBuilder 类连接字符串
连接复杂字符串时,请使用 System.Text.StringBuilder 类。其速度远快于 string.Format() 方法,并且使用的内存少于通过加号运算符进行连接时所用的内存。
8、CPU优化列表之使用 CompareTag代替标记属性
使用 GameObject.CompareTag() 方法代替 GameObject.tag 属性。CompareTag() 方法的速度更快,并且不会分配额外的内存。
下图显示了 CompareTag() 的用法:
9、CPU优化列表之使用对象池
如果游戏有许多同类对象在运行时创建和破坏,则可以使用设计模式对象池。该设计模式可避免在动态分配和释放众多对象时产生性能损失。
如果知道所需对象的总数,则可以直接创建所有对象,并禁用暂时不需要的对象。需要新对象时,请搜索首个未用对象的池并启用该对象。
不再需要某个对象时,可以将其放回至池中。这意味着将对象重置为默认开始状态并禁用该对象。
此技术可以与敌人、抛射物和粒子等对象结合使用。如果不知道所需对象的准确数目,则进行测试,找出使用的对象数并创建一个数目稍大于此数的对象池。
10、CPU优化列表之缓存组件检索
缓存 GameObject.GetComponent
Gameobject.camera、Gameobject.rendener 或 Gameobject.transform 等属性是对应 GameObject.GetComponent
考虑缓存 Transform.position 的返回值。即便它是 C# getter 属性,也会在用于计算全局位置的转换层级上产生与迭代相关的开销。
Unity 5 及更高版本会自动缓存转换组件。
11、GPU优化列表
a. 使用静态批处理
静态批处理是一种常见的优化技术,可以减少绘制调用数量,从而降低应用程序处理器的使用率。
动态批处理可由 Unity 以透明的方式执行,但是无法运用至大量顶点组成的对象,因为计算开销过大。
静态批处理可在大量顶点组成的对象上运行,但是经过批处理的对象在渲染过程中不得移动、旋转或扩大。
若要使 Unity 能够集合要进行静态批处理的对象,请在“检视面板”中将它们标记为静态。
b. 使用细节层次
Unity 引擎可以使用细节层次 (LOD) 技术根据与摄像机的距离渲染同一对象的不同网格。
当对象更接近摄像机时,几何结构更为清晰。随着对象远离摄像机,细节层次降低。当处于最远距离时,可以使用平面公告板。 必须恰当地设置 LOD 组,以管理要使用的网格以及相应的距离范围。 要访问 LOD 组的设置,请选择:添加组件 > 渲染 > LOD 组。
在 Unity 5 中,可以设置渐变模式使每个 LOD 层次混合为连续的 LOD。这可以平滑它们之间的过渡。Unity 可以根据对象的屏幕大小计算混合因子,并将它传递到着色器以进行混合。必须在着色器中实施几何体混合。
c. 光照贴图和灯光探测器
运行时光照计算的计算成本很高。一项用于降低计算要求的常见技巧称为光照贴图,它预先进行光照计算并将它们烘焙为名为光照贴图的纹理。
这意味着会丧失完全动态光照环境的灵活性,但可以生成质量非常高的图像,而不会影响性能。
在静态光照贴图中烘焙生成的光照
将接收光照的几何体设置为静态。
将灯光中的烘焙选项设为已烘焙,而不是运行时。
在光照贴图窗口的“场景”选项卡中,选中已烘焙 GI 选项。
查看生成的光照贴图:
选择几何体。
选择窗口 > 光照,以打开光照窗口。
按对象按钮。
选择预览选项中的烘焙强度光照贴图。
如果选择了持续烘焙选项,Unity 将烘焙该光照贴图,并在几秒后更新编辑器中的场景。
若要快速检查光照贴图设置是否正确,可在编辑器中运行游戏,并禁用光源。如果光照仍在,则光照贴图已经正确创建并在使用中。
使用定向光照贴图:
如果无法针对双重光照贴图使用延迟光照,则另外一种方法是使用定向光照贴图。这能够使你在没有实时光照的情况下使用法线贴图和镜面反射光照。
如果必须保存法线贴图但未提供双重光照贴图,则可使用定向光照贴图。移动设备通常就属于这种情况。
针对游戏中的动态对象使用灯光探测器:
可以使用灯光探测器向进行光照贴图的场景添加一些动态光照。
下图显示了灯光探测器设置:
d. ASTC 纹理压缩
不要在构建设置中以一种格式压缩所有纹理。使纹理压缩保持为请勿覆盖。
在项目层级中查找纹理并使其显示在检视面板。Unity 通常会以纹理类型导入纹理。该类型仅为压缩提供一定数量的选项。将类型设为高级可显示更多选项。
下图显示了具有一定透明度的 GUI 纹理的设置。此纹理适用于 GUI,因此 sRGB 和纹理映射已被禁用。若要包含透明度,则需要 alpha 通道。为此,请选中 Alpha 透明方框和覆盖 Android 方框。
下图显示了高级纹理设置
e. 纹理映射
纹理映射技术与能够同时提高游戏视觉质量和性能的纹理相关。
纹理映射是不同大小的纹理的预计算版本。每个生成的纹理称为一个层级,它们的宽度和高度是前一个纹理的一半。Unity 能够自动生成完整的层级,从原始尺寸的第一层级到 1x1 像素版本。
若要生成纹理映射,需要执行以下操作:
在项目窗口中选择某一纹理。
将纹理类型更改为高级。
在检视面板中启用生成纹理映射选项。
下图显示了纹理映射设置:
如果纹理没有纹理映射层级,当具有纹理的表面覆盖的区域(以像素表示)小于纹理尺寸时,GPU 会将纹理缩小至适合更小的区域。但是,此过程中会丧失部分准确性,即便使用滤波器插值像素颜色。
如果纹理具有纹理映射层级,GPU 将从最接近对象大小的层级中提取像素数据以渲染纹理。这可提高图像质量并降低带宽,因为 GPU 为获得更高质量已经离线扩展了层级,并且 GPU 仅从恰当的层级中提取纹理数据。多级渐进纹理 (mipmapping) 的劣势在于它额外需要 33% 的内存来存储纹理数据
f. 阴影
阴影有助于增加场景的透视度和真实感。没有阴影,有时很难告知对象的深度,尤其是它们与周围对象相似时。
阴影算法可能会非常复杂,渲染高分辨率的精确阴影时尤为如此。请确保在游戏中为阴影选择了相应级别的复杂度和分辨率。
Unity 支持转换反馈,实现了对实时阴影的计算。
Unity 的Edit > ProjectSettings> Quality下包含多个阴影选项,它们可影响游戏的性能:
硬阴影/硬加软阴影
软阴影看起来更为真实,但是计算时间较长。
阴影距离
阴影距离选项可定义与出现阴影的摄像机的距离。增大阴影距离会增加可见阴影的数量,从而加大计算量。增大阴影距离还会增加用于阴影贴图中阴影的像素元数量,从而被动地提高阴影分辨率。
可以使用阴影距离较小且分辨率较高的硬阴影。这会在距离摄像机的适当范围内产生不是很复杂且质量较高的阴影。
进行光照贴图的对象不会产生实时阴影,在场景中烘焙的静态阴影越多,GPU 执行的实时计算就越少。
实时阴影可大幅提高场景的真实度,但是它们的计算量非常大。
在移动平台上,请尝试限制仅包含实时阴影的光源数量,并尝试使用光照贴图。
请考虑使用场景对象的网格渲染器组件。如果不想使用它们投射或接收阴影,请相应地禁用投射阴影和接收阴影选项。这可降低渲染阴影的计算量。
可以在质量设置部分找到更多阴影设置,例如:
阴影分辨率:可让你选择质量和处理时间之间的平衡值。
阴影距离:可限制距离摄像机较近的对象生成的阴影。
阴影级联:可让你选择质量和处理时间之间的平衡值。可以将它设置为零、二或四。级联阴影贴图用于定向光源,可获得非常好的阴影质量,尤其是视距较远时。级联数越高,质量越好,但会增加处理开销。