Unity Shader unity文档学习笔记(二十二):shader变体

who:变体是什么

why:为什么会产生变体,为什么要注意变体

Shader 写逻辑时复杂的逻辑不能用 if 来处理:

GPU 按"像素块"并行执行同一指令,如 2x2 或 4x4 像素一组。组内任一像素走 true 分支,GPU 就须把 true 和 false 两条路径全跑一遍,再用掩码扔掉不需要的结果,即"分支发散"或"双轨执行"。‌‌

那就得用 #pragma multi_compile 或 #pragma shader_feature 声明 Keyword

Unity 在编译期把 Shader 拆成多份独立 GPU 程序,每份对应一组 Keyword 组合,代码里用 #ifdef 或 #if defined() 区分逻辑。‌‌

运行时通过Material.EnableKeyword切换变体,GPU拿到的是已裁剪好的、无分支判断的专用版本

how:怎么去控制变体,收集变体,剔除变体

shader关键字过多 导致的变体过多导致的AB过大

为什么需要收集shader变体:

1.收集后预加载为了优化运行卡顿问题

2.需要对shader进行无用变体剔除,为了优化内存

预热:

运行时首次使用特定变体时,Unity 需在目标平台(尤其是移动端)实时编译,导致帧率下降

预编译指定变体并缓存结果

怎么去收集变体:

方法1:

这个是相对自动的方法,使用方法是在ProjectSetting>Graphics的最下面,先Clear掉当前的记录,然后进行游戏,尽量覆盖大多数游戏内容,之后点击Save to asset保存。

也是总有跑不到的逻辑,容易漏掉某些变体

方法2:定制化变体收集工具

1.新增一个场景把所有需要打包的预制体都加到该场景中

2.收集代码里面所有运行时设置EnableKeyword的逻辑

3.在当场景运行收集变体时,依次运行所有EnableKeyword的逻辑

完整方案:

更完整的推荐方案(混合)

按投入产出,建议做成 3 层,而不是二选一:

A. 静态层(最稳、最该先做)

扫描打包范围内所有 Material / 预制体引用材质:

记录 shader + shaderKeywords + 常用 Pass

直接写入 ShaderVariantCollection

这能吃掉「资源上已经定好」的绝大部分 shader_feature

B. 模拟运行时开关层(方法2的正确形态)

收集项目里所有:

  1. Material Inspector + Toggle(_XXX) / KeywordEnum
    Shader 里把属性绑到 keyword,Unity 保存材质时自动 开关 keyword,运行时不必写代码:
    Toggle(_DISSOLVE_ON) _Dissolve("溶解开关", Float) = 0
    #pragma shader_feature_local _DISSOLVE_ON
    你们 QMUIEffectUberEX.shader 里大量是这样写的。在 Inspector 勾选/改枚举,底层就是改 material.shaderKeywords,等价于 Enable/Disable。
    KeywordEnum(R, G, B, A) _DistortChannel 会生成 _DISTORTCHANNEL_R 等,改下拉框即可切换。
  2. 自定义 ShaderGUI(Editor 里间接改 keyword)
    你们 QMUberEffectGUI.cs 在 Draw 面板时调用 material.EnableKeyword / DisableKeyword,本质仍是改材质 keyword,只是不用业务运行时脚本,而是 Editor 调材质时 同步。
  3. 直接改 material.shaderKeywords
    material.shaderKeywords = new\[\] { "_DISSOLVE_ON", "_DISTORT_ON" };
    和逐个 EnableKeyword/DisableKeyword 效果一样,适合一次性设组合。
  4. URP / Unity 管线自动设置(multi_compile 常见)
    很多 非 local 的 multi_compile 不需要你手动 EnableKeyword,例如:
    _MAIN_LIGHT_SHADOWS、_SHADOWS_SOFT → URP 按 URP Asset / 光源 / 阴影设置 自动设全局 keyword
    INSTANCING_ON → 材质勾选 Enable GPU Instancing 且 Renderer 走 instancing 时自动生效
    LIGHTMAP_ON、FOG 等 → 场景有 Lightmap、开启 Fog 时引擎设置
    这类是 管线/引擎改全局 keyword,不是你在业务里 material.EnableKeyword。
  5. Unity 2021.2+:LocalKeyword / GlobalKeyword API
    比字符串 EnableKeyword 更安全(编译期校验、少 GC):
    LocalKeyword kw = new LocalKeyword(material.shader, "_DISSOLVE_ON");
    material.SetKeyword(kw, true);
    或全局:
    Shader.EnableKeyword("_MAIN_LIGHT_SHADOWS");
    仍是"设 keyword",只是 API 换了。
C. 游玩追踪层(方法1保留)

分场景/分模块多次 Clear → Play → Save

再 Merge 多个 SVC

CI 或打包前自动跑一遍「主流程冒烟」(登录、主城、战斗、探索...)

额外强烈建议:

Development Player 真机再收一轮(补 Editor 收不到的变体)

构建时用 IPreprocessShaders:只保留「合并 SVC 并集」里的变体(或反过来:白名单外剥离),避免包体失控

加载时对关键 SVC 做 WarmUp / WarmUpProgressively(防卡顿另说,和「收全」是两件事)

最终完整的变体收集方案:

1.点击工具后依次打开所有场景

2.最后进入一个空场景实例化所有会打包的预制体,并且记录预制用到的材质或者shader第三步需要用到

3.填入几个会动态变化的Keyword,需要对第二步记录的所有材质判断存不存在该Keyword,

如果存在则需要在代码里对该材质开关一遍对应Keyword,并且确保至少渲染一帧(unity才能收集到)

4.手动跑游戏收集变体

Shader变体 预热:

AssetBundle.LoadAsset("shaderVariants").WarmUp();

需要注意:

Unity 官方对 Legacy AssetBundle 的描述也是:编译某个 Bundle 的 Shader 时,使用的是"该 Bundle 内"的 Material、SVC 等信息。

意思就是shader和SVC是需要一起打包的,不然某些变体可能就会被剔除

相关推荐
蒸蒸yyyyzwd7 分钟前
从后端面经学到的 3 个知识
笔记·学习
坤坤子吖40 分钟前
Linux网络编程:自定义应用层协议与 JSON 序列化
linux·服务器·网络·c++·笔记·json
路灯上の马陈3 小时前
Java 内部类 学习笔记
笔记·学习
希和20273 小时前
51 单片机入门笔记:江科大课程代码逐行讲解(按键控制 LED 流水灯)
笔记·单片机·mongodb
前进的李工3 小时前
Agent 学习笔记 12:Agent 的工具应该怎么设计
人工智能·笔记·学习·agent·工具调用·skill·工具设计
ever_you_love3 小时前
2026.11.9录屏笔记
笔记
li星野3 小时前
从 DICOM 到声学属性学习记录:HU 值怎么变成密度和声速
学习
希和20273 小时前
51 单片机按键控制 LED:江科大课程代码小白笔记
笔记·单片机·嵌入式硬件
希和20273 小时前
51 单片机 LED 流水灯:从直接法到循环法(江科大课程笔记)
笔记·单片机·mongodb