Shader变体(Shader Variants)是什么?
- 定义:通过预处理器指令(如
#if)动态修改Shader代码分支,生成不同功能的Shader实例。 - 用途:实现动态光照、平台适配、画质分级等功能,避免运行时分支判断开销。
- 代价:变体过多会导致包体增大、编译时间增长、内存占用上升。
两种变体
multi_compile
强制编译所有组合:无论是否使用,声明中的所有变体组合都会被编译。
- 语法:
GLSL
#pragma multi_compile A B C // 生成变体:A, B, C #pragma multi_compile _ D E // 生成变体:<空>, D, E - 典型场景:
- 需运行时动态切换的功能(如昼夜系统)
- 全局开关(如 DEBUG 模式)
- 缺点:易导致变体爆炸(多个
multi_compile会组合叠加)。
shader_feature
按需编译:仅编译材质实际使用的变体(依赖材质参数或Keywords设置)。
- 语法:
GLSL
#pragma shader_feature _TOON _CELLSHADING // 只编译材质启用的Keyword - 典型场景:
- 材质专属功能(如风格化渲染选项)
- 不频繁切换的静态效果
- 关键限制:变体仅包含在最终Build中需满足:
- 材质显式启用了该Keyword
- Shader中通过
#pragma shader_feature声明 - 变体被场景或Resources文件夹引用
示例代码
Shader "Custom/Example" {
Properties {
[Toggle(TOON)] _UseToon ("Toon Shading", Int) = 0
}
SubShader {
Pass {
// 1. 使用multi_compile
#pragma multi_compile _ TOON
#pragma multi_compile TOON // 缺少空变体"_"
// 2. 使用shader_feature
#pragma shader_feature TOON
#pragma shader_feature _ TOON
#ifdef TOON
// 卡通渲染逻辑
#else
// 其他渲染逻辑
#endif
}
}
}解析:
multi_compile:- 会强制编译所有组合。在代码
2.中,包含空变体变体_和 变体TOON; - 第一个变体为常开变体,故一般情况下第一个变体为空变体
_。
- 会强制编译所有组合。在代码
shader_feature:- 仅编译启用的组合;
- 当仅有一个变体时,可以不需要添加空变体
_; - 当有多个变体时,第一个变体为常开变体,故一般情况下第一个变体为空变体
_。
区别对比
| 特性 | multi_compile |
shader_feature |
|---|---|---|
| 编译策略 | 强制编译所有组合 | 按材质实际使用编译 |
| 包体影响 | 可能包含未使用变体 | 仅包含被引用的变体 |
| 运行时切换 | 支持(Material.EnableKeyword) |
支持,但需预编译进包 |
| 适用场景 | 全局/高频切换功能 | 材质级/低频切换功能 |
| 变体丢失风险 | 低 | 高(未引用的材质变体会被剔除) |
优化变体数量
- 优先用
shader_feature:减少未使用变体的编译。 - 避免冗余组合:
GLSL
// 错误:生成 3x3=9 个变体 #pragma multi_compile A B C #pragma multi_compile D E F // 正确:合并为单一声明 #pragma multi_compile A_D A_E A_F B_D ... // 显式控制组合 - 使用
#pragma skip_variants:跳过指定变体编译。 - 限制Keyword数量:N个Keyword → 2^N个变体(慎用!)。
变体管理
- 变体收集:
- 方法:
Edit > Project Settings > Graphics > Shader Stripping - 添加需保留的变体Keyword(仅对
shader_feature生效)。
- 方法:
- 调试工具:
- 查看编译结果:在Shader Inspector点击 「Compile and Show Code」。
- 检查当前Keyword:
ShaderVariantCollection资源。
- 代码中切换Keyword:
CSHARP
material.EnableKeyword("_TOON"); // 启用变体分支 material.DisableKeyword("_CELLSHADING");
实践经验:
- 移动端项目严格控制
shader_feature的Keyword数量(≤4个)。 - 用
ShaderVariantCollection预加载高频变体避免卡顿。 - 发布前使用
ShaderUtil.GetVariantCount()检查变体总量。