
本文属Unity项目优化技巧系列第三篇。你可以用它们指导用更少的资源运行更高的帧率。尝试了这些最佳实践后,请务必查看系列的其他页面:
请参阅我们为 Unity 6 开发者和美术师提供的最新优化指南:
了解目标硬件的局限性以及如何对GPU进行性能分析以优化图形渲染。尝试以下技巧和最佳实践来减少GPU的工作负载。
在性能分析时,我们最好从基准测试开始,明确特定GPU的性能分析结果。
参阅 GFXBench 以获取大量不同的 GPU 和显卡行业标准基准。该网站很好地概述了当前可用的GPU以及它们如何相互堆叠。
观看渲染统计信息
单击 Game 视图右上角的 Stats 按钮。该窗口显示在运行模式下应用程序的实时渲染信息。你可以用这些数据来优化性能:
- FPS:帧数/秒
- CPU 主站:处理一帧(并为所有Windows更新编辑器)的总时间
- CPU 渲染:渲染游戏视图一帧的总时间
- 批处理:绘制调用组要绘制到一起
- 三角形和顶点:网格几何体
- SetPass 调用:Unity 必须切换着色器通道以在屏幕上渲染游戏对象的次数;每次通道都会引入额外的 CPU 开销。
注意:编辑器内的FPS不一定能转化为构建性能。我们建议对构建进行性能分析以获得最准确的结果。在基准测试时,以毫秒为单位的帧时间比每秒帧更精确。更多详情请在电子书《Unity游戏性能分析终极指南》(Unity 6版)中了解。

为了绘制游戏对象,Unity 向图形 API (例如 OpenGL、Vulkan 或 Direct3D) 发出绘制调用。每次绘制调用都会占用大量资源。绘制调用之间的状态更改(例如切换材质)可能会导致 CPU 端的性能开销。
PC 和游戏主机硬件可以推送大量绘制调用,但每次调用的开销仍然很高,值得尝试减少它们。在移动设备上,绘制调用优化至关重要。您可以通过绘制调用批处理实现这一点。
绘制调用批处理可最大限度减少这些状态更改,并降低渲染对象的 CPU 成本。Unity 可以使用多种技术将多个对象组合成更少的批处理:
SRP批处理:如果使用HDRP或URP,请在Advanced下的Pipeline Asset中启用SRP Batcher。当使用兼容的着色器时,SRP Batcher 会减少绘制调用之间的 GPU 设置,并使材质数据在 GPU 内存中持久化。这可以显著加快 CPU 渲染时间。用最少的关键词减少着色器变体,以改进SRP批处理。查阅SRP文档,了解您的项目如何利用这一渲染工作流程。
- GPU实例化:如果有大量相同的对象(例如,具有相同网格和材质的建筑物、树木、草地等 ) , 请使用 GPU 实例化。该技术使用图形硬件进行批处理。要对材质启用GPU实例化,请在Project窗口中选择Material,然后在Inspector中勾选Enable Instancing复选框。
- 静态批处理:对于非移动几何体,Unity 可以减少共享相同材质的任何网格的绘制调用。它比动态批处理更有效,但会占用更多内存。在Inspector中将从未移动的所有网格标记为批处理静态。Unity 在构建时将所有静态网格整合到一个大网格中。StaticBatchingUtility还允许您在运行时(例如,在生成程序级别的非移动部分之后)自行创建这些静态批处理。
- 动态批处理:对于较小的网格,Unity可以在CPU上对顶点进行分组和变换,然后一次性绘制所有顶点。注意:除非有足够的低多边形网格(每个不超过 300 个顶点和 900 个总顶点属性 ) , 否则不要使用它。否则,启用它只会浪费CPU时间来查找要批处理的小网格。
您可以使用一些简单的规则来最大化批处理效果:
- 在场景中使用最少的纹理。更少的纹理需要更少的独特材质,使它们更容易批处理。此外,纹理图集也需尽可能地使用。
- 始终以尽可能大的图集烘焙光照贴图。更少的光照贴图需要更少的材质状态更改,但要注意内存足迹。
- 小心别把材质分层在脚本中访问 Renderer.material 会复制材质并返回对新副本的引用。这会中断任何已包含材质的现有批处理。如果你想访问批处理对象的材质,请使用Renderer.sharedMaterial。
- 在优化期间使用性能分析器或渲染统计信息,密切关注静态和动态批处理计数与绘制调用总数的关系。
有关更多信息,请参阅绘制调用批处理文档。

Frame Debugger(帧调试器)可将播放冻结在一帧上,并逐步了解Unity如何构建场景,以确定优化机会。查找不必要的渲染游戏对象,禁用它们以减少每帧的绘制调用。
注意:Frame Debugger(帧调试器)不显示单独的绘制调用或状态更改。只有原生GPU性能分析器会提供详细的绘制调用和计时信息。但是,Frame Debugger 在调试管线问题或批处理问题时仍然非常有用。
Unity帧调试器的一个优点是可以将绘制调用与场景中的特定游戏对象相关联。这使得调查某些在外部帧调试器中可能无法完成的问题变得更加容易。
有关更多信息,请参阅帧调试器文档,请参阅

填充率是指GPU每秒可以渲染到屏幕上的像素数。
如果游戏受到填充率的限制,这意味着它试图每帧绘制的像素数超过了 GPU 的处理能力。
在同一像素上多次绘制称为过度绘制。过度绘制会降低填充率并消耗额外的内存带宽。导致过度绘制的最常见的原因是:
- 不透明或透明几何体重叠
- 复杂的着色器,通常有多个渲染通道
- 未优化的粒子
- 重叠的UI元素
虽然你想尽量减少过度绘制的影响,但并没有一种万能的方法来解决过度绘制问题。首先尝试一下上述因素,以减少它们的影响。
与其他平台一样,控制台上的优化通常意味着减少绘制调用批处理。这里有一些技巧可能会有所帮助。
- 使用遮挡剔除来移除隐藏在前景对象后面的对象并减少过度绘制。请注意,这需要额外的 CPU 处理,因此使用 Profiler 可确保将工作从 GPU 转移到 CPU 是有好处的。
- GPU 实例化如果有许多共享相同网格和材质的对象,也可以减少批处理。限制场景中的模型数量可以提高性能。只要巧妙地完成,就可以构建复杂的场景,而又不让它显得重复。
- SRP Batcher可以通过批处理Bind和Draw GPU命令来减少DrawCall之间的GPU设置。SRP批处理的材质越多,使用的着色器数量也越多(比如URP和HDRP的Lit和Unlit着色器)。
遮挡剔除可禁用被其他游戏对象完全隐藏(遮挡)的游戏对象。这样一来,CPU和GPU就无法用时间来渲染摄像机看不见的对象。
每个摄像机都会发生剔除。这对性能有很大影响,尤其是在同时启用多个摄像机时。Unity 使用两种剔除类型:视椎体剔除和遮挡剔除。
每个摄像机都会自动执行视椎体剔除。它可以防止渲染视椎体之外的游戏对象,帮助优化性能。
你可以通过 Camera.layerCullDistances 手动设置每层剔除距离。这样,您可以在比默认farClipPlane更短的距离上剔除小游戏对象。
将游戏对象组织成层。使用 layerCullDistances 数组为每个 32 个图层分配一个小于 farClipPlane 的值(或使用 0 默认值为 farClipPlane)。
Unity 先逐层剔除,仅将游戏对象保留在摄像机使用的图层上。然后,视椎体剔除将移除摄像机视椎体之外的任何游戏对象。视椎体剔除作为一系列作业执行,以利用可用的工作线程。
每个图层的剔除测试都非常快(基本上只需要一点遮罩操作)。但是,这个成本加起来可能还是会涉及大量的游戏对象。如果项目出现问题,你可能需要用某种系统将场景划分成“扇区”并禁用摄像机视锥体以外的扇区,以减轻Unity图层/视锥体剔除系统的压力。
如果摄像机看不到任何GameObject,Occlusion Culling会从Game视图中移除它们。使用该功能可以防止渲染隐藏在其他对象后面的对象,因为这些对象仍然会渲染和消耗资源。比如,如果门关着,摄像机无法进入房间,就没必要再渲染另一个房间。
启用遮挡剔除可以显著提高性能,但也可能需要更多磁盘空间、CPU 时间和 RAM。Unity 在构建期间烘焙遮挡数据,然后在加载场景时需要将其从磁盘加载到 RAM。
在摄像机视图之外的锥体剔除是自动的,而遮挡剔除是一个烘焙过程。只需将对象标记为Static.Occluders或Occludees,然后通过Window > Rendering > Occlusion Culling对话框进行烘焙。
更多详情请在Occlusion Culling操作指引中了解。

允许动态分辨率 (Allow Dynamic Resolution) 是一种摄像机设置,可让您动态缩放各个渲染目标,以减少 GPU 工作量。在应用程序的权重降低时,可以逐步降低分辨率,以保持一致的权重。
如果性能数据表明帧权重因受 GPU 束缚而即将降低,Unity 会触发这种缩放。您还可以使用脚本手动抢先触发这种缩放。如果您要进入应用程序的 GPU 密集型部分,这很有用。如果逐渐缩放,动态分辨率几乎可以忽略不计。
有关更多信息和支持的平台列表,请参阅动态分辨率手册页面。
有时,在游戏中,您可能需要从多个角度进行渲染。比如,FPS游戏里经常用不同的视野(FOV)来分别描绘玩家的武器和环境。这样一来,前景对象就不会在广角FOV里显得过于扭曲。
你可以在URP中使用Camera Stacking来渲染多个摄像机视图。但是,每台摄像机仍然需要进行大量的剔除和渲染。每台摄像机都会产生一些开销,无论它是否在做有意义的工作。仅使用渲染所需的摄像机组件。在移动平台上,每个激活的摄像机即使什么也不渲染,也最多可以使用 1 毫秒的 CPU 时间。

在URP中,不要使用多个摄像机,而应尝试自定义Render Objects Renderer Feature。在 Renderer Data 资源中选择 Add Renderer Feature。选择 Render Object。
覆盖每个渲染对象时,你可以:
- 将其与事件关联并注入渲染循环的特定时间
- 按渲染队列(透明或不透明)和 LayerMask 过滤
- 影响深度和模板设置
- 修改摄像机设置(视野和位置偏移)

在 HDRP 中,您可以使用自定义通道来实现类似的效果。使用自定义通道体积配置自定义通道类似于使用HDRP体积。
自定义通行证可让您:
- 更改场景中材质的外观
- 更改Unity渲染游戏对象的顺序
- 将摄像机缓冲区读取到着色器中
使用HDRP的Custom Pass Volume 可帮助你避免使用额外的摄像机和额外的开销。自定义通道可以更灵活地与着色器交互。您还可以扩展自定义通道类。


分析后处理效果,了解它们在GPU上的开销。某些全屏效果(如泛光和景深)的成本可能很高,但请进行试验,直到在视觉质量和性能之间找到满意的平衡。
后处理在运行时波动不大。确定Volume Overrides(体积覆盖)后,将帖子特效分配到总帧预算的静态部分。

曲面细分将形状细分为更小的形状版本。这可以通过增加几何体来增强细节。虽然有些曲面细分的确可行,比如写实的树皮,但一般来说,不要在游戏主机上使用曲面细分。它们可能会在GPU上消耗大量性能。
当您向 GPU 发送绘制调用时,该工作会拆分成许多波面,Unity 将其分布在 GPU 中的可用 SIMD 中。
每个 SIMD 都有可同时运行的最大波面数。波面占用率是指当前使用中的波面数与最大值之间的差值。你可以用它来衡量GPU的潜力。控制台特定性能分析工具非常详细地显示了波前占用率。
在下方例子中,顶点着色器的波面显示为绿色。像素着色器波面显示为蓝色。在底部图表中,许多顶点着色器波面出现时像素着色器的活动并不多。这表明GPU的潜力没有得到充分利用。
如果顶点着色器运算量很大,但最终没有生成像素,则说明效率较低。虽然低波前占用率并不一定糟糕,但优化着色器、找出其他瓶颈也是个指标。比如内存或运算导致停滞,提高占用率可以提高性能。另一方面,处理中的波面过多也可能导致缓存抖动并降低性能。

如果有GPU使用率较低的时间间隔,异步计算可将有用的计算着色器工作并行移动到图形队列。这可以更好地利用这些GPU资源。
例如,在生成阴影贴图时,GPU 仅执行深度渲染。此时,像素着色器工作很少,许多波面仍无人占用。
如果计算着色器的工作能与深度渲染同步,GPU的整体使用也会更好。未使用的波面可用于屏幕空间环境光遮挡或补充当前工作的任何任务。
观看关于Unite优化高端游戏主机性能的会话级别/会话次数。

GPU Resident Drawer(适用于URP和HDRP)是一种由GPU驱动的渲染系统,旨在优化CPU时间,提供显著的性能优势。它支持跨平台渲染,并且开箱即用,适用于现有项目。
系统可在HDRP或URP Render Pipeline Asset中启用。选择 Instanced Drawing 以启用它。您还可以选择仅在运行模式下启用它,还是在编辑模式下启用它。
在启用GPU Resident Drawer后,大量绘制调用导致CPU占用的游戏会随着绘制调用数量的减少而提高性能。您将看到哪些改进取决于场景规模和使用的实例量。渲染的可实例化对象越多,您将获得的收益就越大。
选择Instanced Drawing选项后,UI中可能会有一条警告消息,“BatchRenderGroup Variants设置必须设为‘Keep All’”。在图形设置中调整此选项可完成GPU Resident Drawer的设置。
要想了解更多,请在这里查看我们的讨论贴。



您可以从 Unity 最佳实践中心找到更多面向高级 Unity 开发者和创作者的最佳实践和技巧。从 30 多份由行业专家、Unity 工程师和技术美术师编写的指南中进行选择,这些指南将帮助您高效使用 Unity 的工具集和系统。