Unity 中的 DrawCall 与优化核心
✨ 核心概念速览
| 概念 | 描述 | 目的 |
|---|---|---|
| DrawCall | CPU 准备数据并命令 GPU 渲染的操作14 | 通知 GPU 绘制物体 |
| 批处理 | Unity 将多个物体合并后用一次 DrawCall 渲染1 | 减少 DrawCall 数量 |
| 图集 | 将多个小纹理合并到一个大纹理中38 | 减少材质切换,促进批处理 |
| 动态批处理 | 对共用相同材质的动态物体自动批处理1 | 降低动态物体 DrawCall |
| 静态批处理 | 对标记为 Static 的静止物体进行批处理1 | 大幅减少静态物体 DrawCall |
🔍 什么是 DrawCall 及其增加原因
在 Unity 中,DrawCall 是 CPU 准备好渲染数据(如顶点、纹理、着色器等)后,通知 GPU 开始渲染的命令14。每次 DrawCall 之前,CPU 都需要进行大量准备工作,例如设置渲染状态、准备数据等,这个过程本身就有开销14。
DrawCall 会增加的主要原因包括:
-
材质切换:每次更改材质通常都会产生一个 DrawCall2。
-
纹理不同:使用不同纹理的物体一般需要不同的材质,从而增加 DrawCall7。
-
着色器差异:使用不同着色器的物体无法批量处理。
-
渲染状态改变:例如深度测试、混合模式等的变化也可能中断批处理。
CPU 需要花费时间处理这些命令并将其提交给 GPU。如果 DrawCall 数量过多,CPU 就可能忙于提交这些命令,导致CPU 过载,进而引发游戏卡顿14。
🤔 为什么要打图集
图集(Texture Atlas) 是将多个小纹理或精灵(Sprite)合并到一张大纹理中的技术38。
打图集的主要目的是:
-
减少 DrawCall:这是最直接的目的。多个精灵共享同一张大纹理和材质,渲染它们可以减少 DrawCall38。
-
提升批处理效率:Unity 的批处理系统能更高效地处理共享相同材质的对象。使用图集让多个对象共享同一材质,极大提升了批处理效率3。
-
减少纹理交换:GPU 频繁加载和卸载不同纹理会产生开销。图集减少了纹理切换,有助于保持较高帧率3。
-
优化内存与包体:将多张小图(尤其非2的幂次方尺寸)合并为一张2的幂次方大图,便于GPU处理,有时还能减小包体体积8。
📉 打图集如何降低 DrawCall
打图集通过以下方式降低 DrawCall:
-
材质统一:原本每个不同的纹理都可能需要一个独立的材质。打图集后,多个精灵共享同一张大纹理,从而共享同一个材质。
-
促进批处理:Unity 的批处理系统会自动尝试将使用相同材质的物体合并渲染1。当多个 UI 元素或精灵使用同一图集(即相同材质)时,它们就更容易被批量处理,从而将原本需要的多次 DrawCall 合并为一次或少数几次3。
这就好比搬运货物:打图集相当于把多个小件货物打包进一个大箱子(大纹理),一次性搬运(一次 DrawCall),而不是来回多次搬运每个小件(多次 DrawCall)。
🧩 图片叠加与 DrawCall 的关系
图片(UI 元素或精灵)叠加时是否增加 DrawCall,不取决于叠加本身,而取决于它们的材质和图层关系。
-
不同图集/材质叠加:如果叠加的图片来自不同的图集(意味着材质不同),或者纹理、材质、着色器不同,通常会增加 DrawCall。UGUI 根据层级顺序和材质等因素决定合批。当不同材质的元素重叠相交时,容易中断合批,导致 DrawCall 增加59。
-
同一图集叠加:如果叠加的图片都来自同一图集(即共享同一材质),并且满足合批条件(如层级相邻且无其他中断合批的因素),那么它们通常不会被额外的 DrawCall。UGUI 的合批算法会尝试将这些相同材质的元素合并在一个 DrawCall 中5。
⚠️ 多个按钮叠加的影响
多个按钮(通常是 Image 组件)叠在一起时,是否增加 DrawCall,同样遵循上述原则:
-
如果这些按钮使用同一图集的精灵,且布局合理(例如在 Hierarchy 中顺序相邻),它们通常可以合批,不会单纯因为叠加而增加 DrawCall。
-
如果按钮使用了不同图集的精灵,或者叠加方式破坏了合批顺序(例如来自不同图集的元素穿插重叠),就很可能增加 DrawCall9。
-
按钮上的 TextMeshPro 或 Legacy Text:文本往往是“破坏分子”。即使按钮图像来自同一图集,如果按钮上的文本使用了不同字体纹理(本质上是另一种材质),就极易中断批处理,增加 DrawCall。
📝 文本增加 DrawCall 的原因
文本(包括 Legacy Text 和 TextMeshPro)增加 DrawCall 的主要原因:
-
独特的材质与纹理:每个字体文件都有对应的纹理图集,用于存储字符。文本渲染时使用自身的材质和字体纹理,这与普通的 Image 组件完全不同。当文本与其他UI元素(如图片)混合时,材质的差异会中断批处理。
-
频繁更新与动态生成:尤其是动态变化的文本(如得分、血量),其网格可能需要频繁更新,这会阻碍批处理。
-
轮廓、阴影等效果:这些效果通常通过额外通道或特殊着色器实现,进一步增加了材质的复杂性,使得文本更难与其他元素合批。
💡 优化 DrawCall 的实用建议
-
充分利用图集:
-
规划 UI 时,将频繁同时出现的元素放在同一图集3。
-
使用 Unity 的
Sprite Atlas进行打包和管理3。
-
-
注意 UI 层级与排序:
-
在 Hierarchy 中,合理安排 UI 元素的顺序,让使用相同材质的元素尽量相邻,有助于合批5。
-
尽量避免不同图集的元素深度交错重叠。
-
-
谨慎使用 Mask 组件:
Mask和RectMask2D会中断合批并产生额外 DrawCall,谨慎使用9。考虑使用RectMask2D替代Mask,因为前者对滚动列表内的内容合批更友好。 -
减少不必要的 Raycast Target:为不需要接收射线检测的 Text 或 Image 取消勾选
Raycast Target,能轻微减少 CPU 开销9。 -
静态批处理与动态批处理:
-
对于场景中静止不动的物体,尽量标记为
Static,利用静态批处理1。 -
了解动态批处理的限制(如顶点数限制),但不要过分依赖,其优化效果有时不明显1。
-
-
文本优化:
-
尽量减少使用过多不同字体的文本。
-
对于不常更新且内容固定的文本,可以考虑将其转换为图片并入图集(但会失去灵活性)。
-

浙公网安备 33010602011771号