跳到主要内容
腾讯题库 · Q13

原题 Q13 · 当前收录 12 / 49

学习状态 未开始

粒子 Shader 优化

Shader particles · performance 对应课程:Shader 与材质

面试官问

PDF 第 30 页

你如何优化一个包含 100+ Draw Call 的粒子特效 Shader?请列出至少 5 种优化策略。(必问题,理清逻辑)

学习进度

0/9 已勾

只保存在这台浏览器;勾满 9 项即视为本题完成。

先把题目说成人话

爆炸特效一出现,手机画面开始卡顿;抓帧里虽然有一百多次绘制,但这个数字只说明中央处理器反复叫图形处理器开工,并没有说明最贵的是叫得太频繁、烟片反复盖满屏幕、每个像素读了太多纹理,还是粒子模拟本身。现实优化要先给每笔成本称重,再对症减少,而不是只追求一个更小的绘制数字。

一次向图形处理器提交网格、材质和状态的命令叫绘制调用(Draw Call),相同状态下可以合在一起的工作叫批次(Batch)。同一屏幕区域被多层透明粒子反复着色叫重复着色(Overdraw),同一效果为不同功能生成的代码路径叫着色变体(Shader Variant),额外的绘制阶段叫渲染通道(Pass)。

这是虚构教学故事,不是个人项目经历。

场景:先让问题变得看得见

一场爆炸由火焰、烟、火星、冲击波和地面余烬组成。Frame Debugger 显示一百多个粒子 Draw:有些来自不同材质和 Shader 关键字,有些来自多个 Renderer,有些是同一效果的额外 Pass。

Profiler 同时显示 CPU 提交压力和 GPU 透明阶段都值得调查。现在不能只挑一个数字开刀。

(右脑)画面:把一次爆炸拆开放到多盘秤上。火焰、烟、火星和冲击波先在“搬来多少批东西”的秤上称重,再到“每批要换多少状态”的秤,随后落到屏幕上称覆盖面积与重叠层数,最后还要给粒子更新、纹理读取和着色计算单独记账。某一盘显示很多次提交,只说明送货频繁,并不能证明它就是最重的盘;大片半透明烟雾可能只送几批,却反复涂满大量像素。优化时先用证据找到真正压住帧时间的秤,再决定合批、减层、换材质还是减少模拟,不能只追一个醒目的数字。

(左脑)对表:核验对象包括 Draw/SetPass 数、Render Thread 与主线程时间、粒子数、覆盖像素和层数、纹理采样/Pass、GPU 毫秒及目标分辨率。100+ Draw 是观察事实,“所以合批一定是首要收益”是假设;证据要由 Profiler、Frame Debugger、overdraw 与 GPU capture 共同把原因定位到阶段。毫秒、像素、字节和实例数不可混成同一指标,编辑器数据也不能替代目标 Player。称重与网络类比只组织关系,类比不等于某项优化已生效。

小明:先形成一个完整猜测

小明想把所有贴图塞进一张图集、所有粒子换成同一材质。这个方向能减少因纹理与材质切换产生的批次,是合理的第一层模型。

但火焰使用 Additive,烟使用 Alpha Blend,冲击波还需要 Distortion Pass;把不同渲染状态硬塞进同一批会改变画面,或者在一个万能 Shader 里执行大量无用分支。合批不是越多越好。

小红:把猜测放回现实检查

小红先把 Draw 按“为什么被拆开”分类:Shader/Pass、关键字、Blend 与深度状态、纹理、网格、Renderer 和排序。然后在 GPU Usage 中检查透明 Pass,在 overdraw 视图中看粒子覆盖,在抓帧中查看每个 Pass 的纹理采样与分支。

证据显示:一部分 Draw 能在保持渲染状态一致的前提下合并;另一部分虽然 Draw 不多,却由巨大烟片反复覆盖屏幕。于是优化必须同时处理提交和 fill rate。

我:用检查结果修正模型

我按证据组合八种策略,每一种都写清它省什么:

  1. 合并兼容材质与 Shader Variant:统一真正相同的 Blend、ZTest、纹理和关键字,减少 CPU 状态切换。
  2. 图集或 Texture2DArray:让相同渲染状态的粒子共享材质,并以 UV/索引选择外观;图集还要为 Mip 留足边距并收紧透明网格,否则可能以串色或更大 overdraw 换来少量批次。
  3. 合并发射器或使用 GPU 粒子/VFX Graph:减少 Renderer 与 CPU 模拟、提交开销;它把一部分模拟、缓冲更新和剔除责任移到 GPU,不是免费消失,是否适用取决于管线和平台。
  4. 削减透明 overdraw:收紧粒子网格、减少重叠层和超大软烟片,按屏幕占比限制数量。
  5. 简化 Shader:移除未使用关键字与 Pass,减少纹理采样、噪声层、昂贵分支和不必要精度。
  6. 按档位降低分辨率或关闭昂贵效果:Distortion、深度软粒子、实时光照和高频噪声分别设质量档。
  7. LOD 与剔除:按距离/屏幕占比减少发射率、粒子数、网格复杂度,并设置可信的 bounds,避免永不剔除。
  8. 降低更新频率并复用数据:远处粒子减少模拟频率,曲线和噪声尽量预烘焙到查找纹理。

验收不能只看 Draw Call。CPU 看 Render Thread/主线程提交,GPU 看透明、后处理与采样成本;画面看爆发节奏、轮廓、颜色层次和战斗可读性。一次只禁用一类效果定位,最终组合方案再完整复测,防止瓶颈迁移。

(右脑)画面修正:小明把贴图并进图集并统一材质,提交秤确实变轻;小红再看抓帧和 overdraw 热图,却发现 Additive、Alpha Blend 与 Distortion 不能无损混批,巨大烟片还在反复烧同一片像素。于是每笔成本各自结账:兼容状态才合材质、变体、图集或纹理数组,发射器与 GPU 模拟处理提交和更新,收紧烟片网格与层数处理覆盖,削减采样、Pass 与分支处理片元,再以质量档、LOD、剔除和降频控制远景;有意义的渲染状态边界保留,不为追求一个 Draw 制造万能 Shader。

(左脑)严格对表:执行顺序为:先逐 Draw 标注拆分键与 Pass;再基线记录 CPU/GPU 时间、overdraw 和画面;一次只应用一种与证据对应的措施,例如兼容材质合并、图集/数组、发射器或 GPU 模拟合并、收紧网格与层数、Shader/Pass 精简、质量档、LOD/剔除、降低更新频率;每次复测同一镜头;最后组合并检查瓶颈迁移。验收要求至少五项措施各写明受益阶段,目标帧时间改善且爆发节奏、轮廓、颜色和可读性仍过关,不能只以 Draw 数下降判定成功。

老师解惑

  • 批次拆分:材质实例、Shader Pass/Variant、纹理、Blend/Z 状态、网格与排序都可能让粒子无法同批。
  • Overdraw:同一屏幕区域被多层透明粒子重复着色;它主要增加片元、采样和带宽成本。
  • 万能 Shader 陷阱:少几个 Draw 却让每个像素执行更多分支和采样,可能得不偿失。
  • Bounds:过大的包围盒会让粒子系统始终被视为可见;过小则会突然消失。
  • 瓶颈迁移:提交优化后,真正限制帧率的部分可能变成 fill rate、模拟或后处理,需要重新测量。

工具分工

  • Unity Frame Debugger:逐个确认 Draw 为什么拆分、各自使用什么 Pass 与状态。
  • Unity Profiler(CPU/GPU Usage):区分提交、模拟和 GPU 渲染成本。
  • Overdraw / RenderDoc:定位透明覆盖和昂贵采样。
  • Memory Profiler:检查图集、Texture2DArray 与重复纹理的内存取舍。

记忆钩子

先拆 100 个 Draw 为什么存在,再同时治提交、覆盖和 Shader;合批只是五种以上手段中的一种。

这是教学场景,不是目标项目的真实捕获或个人经历。

下一步:验证这条因果

用帧时间与 overdraw 证据决定该减哪一项。

教学边界:它只验证这一条画面因果,不替代完整答案、项目经历或目标设备数据。

进入教学 Lab:「粒子贵在哪里:不是数量一个数字」