每一帧,Unity 都需要确定必须渲染哪些对象,然后创建绘制调用。绘制调用是调用图形 API 来绘制对象(如三角形),而批处理是要一起执行的一组绘制调用。
随着项目变得更加复杂,您需要用管线来优化 GPU 的工作负载。通用渲染管线 (URP) 目前使用单通道前向渲染器将高质量图形传输给移动平台(未来版本中将提供延迟渲染功能)。来自游戏主机和 PC 的、基于物理的光照和材质也可以缩放为适合手机或平板电脑。
以下指导原则可以帮助您提高图形处理速度。
批处理执行绘制调用
将要绘制的对象组合为批次,可以尽可能减少在批次中绘制每个对象所需的状态更改。这种方式通过减少渲染对象的 CPU 开销,可以改善性能。Unity 可以使用以下几种方法将多个对象组合为较少的批次 :
- 动态批处理 :对于小网格,Unity 在 CPU 上分组和转换顶点,然后一次性绘制它们。注意 :只在有足够低复杂度网格(少于 900 个顶点属性和不超过 300 个顶点)时使用这一方法。动态批处理程序不会对更大的网格进行批处理,如果启用会浪费 CPU 时间在每一帧都去查找要批处理的小网格。
- 静态批处理 :对于不移动的几何体,Unity 可以减少所有共享相同材质的网格的绘制调用。它比动态批处理更有效,但使用更多内存。
- GPU 实例化 :如果有大量相同的对象,这种方法通过图像硬件对它们进行更有效地批处理。
- SRP 批处理 :在 Advanced 下面的 Universal Render Pipeline Asset 中启用 SRP Batcher。这样可以大幅提高 CPU 渲染速度,具体取决于场景。
组织游戏对象,以便利用这些批处理方法。
使用帧调试器 (Frame Debugger)
帧调试器介绍每一帧是如何通过各绘制调用构建的。这是非常重要的工具,可以解决着色器属性问题,帮助您分析游戏渲染方式。
第一次使用帧调试器?此处可查看该简介。
帧调试器将每一帧分到不同的步骤中。
避免使用过多动态光线
与旧版前向渲染器相比,URP 可以减少绘制调用的数量。避免向移动端应用程序添加过多动态光线。考虑采用其他方式,如对动态网格使用自定义着色器效果和光照探针,以及对静态网格使用烘焙光照。
对于 URP 的特定限制以及内置渲染管线实时光照,请参阅此功能对比表。
禁用阴影
阴影投射可按 MeshRenderer 和光线禁用。尽可能禁用阴影可以减少绘制调用。
您也可以通过向简单网格应用模糊纹理或在角色下面应用四边形来创建伪阴影。另外,可以使用自定义着色器创建模糊阴影。
禁用阴影投射可以减少绘制调用。
将光照烘焙到光照贴图中
使用全局光照 (GI) 向静态几何体添加动态光照。使用 Contribute GI 标记对象,以便您可以将高质量的光照存储为光照贴图的形式。
这样,烘焙阴影和光照的渲染不会影响运行时性能。渐进式 CPU 和 GPU 光照贴图可以加快全局光照的烘焙。
启用 Contribute GI。
调整 Lightmapping Settings (Windows > Rendering > Lighting Settings) 和 Lightmap 大小,从而限制内存使用量。
请参阅手册指南和有关光照优化的文章了解如何开始在 Unity 中使用光照贴图。
使用光源层
对于多光源的复杂场景,将对象分层,然后限定每个光源对特定剔除遮罩 (culling mask) 的影响。
层可以限制光源对特定剔除遮罩的影响。
对移动对象使用光照探针
光照探针存储场景中的空白空间的烘焙光照信息并且提供高质量的光照(直接和间接)。它们使用球谐函数,这种函数的计算速度比动态光照快很多。
光照探针照亮背景中的动态对象。
使用细节级别 (lOD)
随着对象移向远处,细节级别可以将它们切换为使用更简单的网格,以及更简单的材质和着色器,从而帮助提高 GPU 性能。
请参阅 Unity Learn 上的使用 LOD 级别课程了解更多详细信息。
使用 LOD Group 的网格示例。
源网格,以不同分辨率建模。
使用遮挡剔除来移除隐藏的对象
隐藏在其他对象之后的对象仍然可能渲染和使用资源。使用遮挡剔除可以将它们丢弃。摄像机之外的视锥体剔除 (frustum culling) 是自动执行的,遮挡剔除 (occlusion culling) 是则要经过烘焙过程。只需将对象标记为静态遮挡物或被遮挡物,然后通过 Window > Rendering > Occlusion Culling 对话框进行烘焙。尽管不是所有场景都适合,剔除在很多情况下都能改善性能。
请参阅使用遮挡剔除教程了解更多信息。
避免使用移动端原生分辨率
手机和平板电脑越来越高级,新生代设备往往采用极高的分辨率。
使用 Screen.SetResolution(width, height, false) 可降低输出分辨率,提升一定的性能。配置多个分辨率,在质量和速度之间找到最佳平衡。
限制摄像机的使用
每个摄像机都会产生开销,无论它是否在做有意义的工作。只在有必要渲染时才使用摄像机组件。在低端移动平台,每个摄像机最多可以使用 1 ms CPU 时间。
保持着色器简单
通用渲染管线包含几个轻量级光照和无光照着色器,它们已针对移动平台进行了优化。让着色器变体的数量尽可能保持较少的状态,这样可以大幅减少运行时内存使用量。如果默认 URP 着色器不满足需要,可以使用 Shader Graph 自定义材质的外观。请参阅此处了解如何使用 Shader Graph 以可视化的方式构建着色器。
使用 Shader Graph 创建自定义着色器。
尽可能减少过度绘制和Alpha 混合
避免绘制不必要的透明或半透明图像。这种方式导致的过度绘制和 Alpha 混合会极大影响移动平台。不要重叠几乎看不到的图像或效果。可以使用 RenderDoc 图形调试器检查过度绘制。
限制后期处理效果
全屏幕后期处理效果(如发光)会极大降低性能。请在游戏的美术设计中谨慎使用这些效果。
在移动应用程序中使用简单后期处理。
小心使用 Renderer.material
在脚本中访问 Renderer.material 会复制材质并返回对新副本的引用。这会破坏任何已包含该材质的现有批次。如果要访问批次中对象的材质,请改用 Renderer.sharedMaterial。
优化 SkinnedMeshRenderer
蒙皮网格的渲染开销很大。确保每一个使用 SkinnedMeshRenderer 的对象都是需要它的。如果游戏对象只在某些时候需要动画,请使用 BakeMesh 函数将蒙皮网格冻结在静态姿势中,并在运行时切换为简单的 MeshRenderer。
尽可能减少反射探针
反射探针可以创建逼真的反射,但在批处理中,它的开销巨大。使用低分辨率立方体贴图、剔除遮罩和纹理压缩可以改善运行时性能。