本节摘要:关键字让一张图按开关生成多份编译变体,功能强大但代价隐蔽:开关组合数是乘法关系,项目大了以后构建时间与包体被隐形吃掉。本节讲清变体的产生机制,给出数量估算公式,并提供"属性优先、关键字断后"的决策顺序。
先看机制。2.2 节讲过:改属性值不重新编译,因为属性只是函数参数。那如果效果差异大到"不是一个参数能表达的"呢?比如一个材质要有"普通版"与"带描边版"两条完全不同的节点链。Shader Graph 的答案是关键字(Keyword):声明一个 Boolean 关键字并在图里做分支,编译器会为每种开关组合各生成一份完整着色器代码——这就是变体(Variant)。材质运行时按自己的开关状态选对应变体执行。
一张图里一个开关,编译产物是两份;两个开关是四份;三个开关是八份——乘法关系。再叠加渲染管线与平台维度,变体数量轻易上百。变体不是免费的:每份都要参与构建(包体变大)、按需编译(首次出现该组合时可能卡顿)、存储与加载。

Shader Graph 里的关键字分两层。材质级(Exposed):开关暴露在材质面板上,每个材质实例自选状态,运行时按需取用对应变体——灵活,但所有组合的变体都会打进构建(除非做变体剔除配置)。全局多编译(Multi-Compile):开关不暴露给材质,而是对整个项目全局编译所有变体,典型如引擎内置的雾开关——所有组合全量进构建包。两者包体代价差异巨大:同样一个开关,材质级在未使用组合多时更省(可剔除),全局多编译是无条件全量。
实操上的推荐路径:图里声明 Boolean 关键字,作用域选材质级;分支结构用 Keyword Node(不是 If 节点——If 是运行时按属性走的分支,两个分支都算一遍;Keyword 是编译期选择,只编译选中的分支,成本差异在分支体量大时非常明显)。5.2 节描边案例里"一张图里切换描边开关"的需求,正确实现就是 Keyword Node。
项目里怎么发现变体失控?三个征兆按出现顺序排。征兆一:构建时间莫名变长——每次出包着色器编译阶段明显拖长,先查最近是否往高频复用图里加了关键字。征兆二:包体着色器占比异常——构建报告里着色器体积占比升高,用构建报告的变体统计定位具体图。征兆三:运行时偶发卡顿——某材质第一次用到某个冷门组合,触发即时编译,表现为特定操作后的掉帧尖峰。三个征兆的共同解法都指向同一件事:审计开关,把"能用属性表达的"降级回属性,把"全项目从不开的"直接删掉分支。
一个实用的审计心法:给图里每个关键字问三个问题——这个开关在项目里真的会两种状态都用吗?两种状态的差异是结构差异还是参数差异?未来三个月会出现第三种状态吗?三问之后,大多数当初"先加个开关以防万一"的关键字都会降级或消失。
Boolean 之外还有枚举关键字(Enum):一个关键字带多个选项,编译器为每个选项各生成一份变体。典型用途是风格档位:描边粗细分三档,枚举关键字三个选项对应三条宽度常量分支。注意枚举的变体数量按选项数线性增长(三个选项即三份),仍远好于用三个 Boolean 的八份组合。需要互斥的多个状态时,枚举关键字是正确工具——Boolean 只该用于真正二态的场景。
两条查询路径。路径一:构建报告——出包后的构建日志里有按着色器统计的变体数量,定位数量异常的图。路径二:编辑器内估算——按 7.3 节的乘法公式手算(Boolean 相乘、枚举乘选项数、再乘管线与平台档位),三分钟的估算通常与实际同数量级。日常开发用路径二建立心算习惯,出包检查用路径一验证,两条腿走路。
本节收尾给一个执行工具:对图里每个关键字写下三行文档——它切换什么、两种状态各在什么场景使用、为什么不用属性表达。写不出第三行答案的关键字,大概率该降级成属性;写不出第二行答案的,该直接删除。这张三行文档随图入库,既是审计工具也是交接文档——关键字是全册所有机制里"遗忘代价"最大的一个,文档化是唯一的解药。
关键字在图里以分支节点呈现,两个规范让它可读。规范一:关键字名用疑问句式或状态词(比如"是否描边"或"带描边"),分支两条路在分组框上直接标注开与关的语义——读图的人不需要翻属性定义就知道每条路是什么状态。规范二:分支体尽量整段化——把该状态下的完整节点链放进分支,而不是在链中间频繁开关(多次小分支的变体数与一次大分支相同,但可读性天壤之别)。两个规范合起来:关键字分支应该是"整页切换",而不是"逐行批注"。
技术上可以通过脚本改材质关键字开关(关键字启用接口),但每次切换会触发该材质切换变体——首次使用某变体时的编译尖峰随之而来(7.3 节征兆三的运行时版本)。因此运行时切换要克制:状态切换频率低(关卡级、形态级)才适合关键字;频率高(每帧、每次受击)的一律属性。判断口径一句话:一天切几次用关键字,一秒切几次用属性。
关键字放进 Sub Graph 时有一个容易忽视的放大效应:组件被五张图引用,组件里的一个关键字等于在五张图里各生成一套变体——组件的复利同样适用于变体的复利。因此组件内的关键字审查要比普通图更严格,能提到调用方做的开关就提到调用方(组件输出两路结果,调用方自己选)。原则一句话:变体的产生点尽量靠近使用点,别让组件替所有调用方预先买单。
模块化的三件套讲完,你的效果已经从"练习"升级为"资产"。下一章换一副眼镜审视它们:性能——每个节点到底花在哪、怎么量、怎么省。