本节摘要:Sub Graph 把一段节点链封装成带输入输出口的"自定义节点",一处维护多处复用。本节把第 6 章的溶解效果正式封装为 Sub Graph,讲清输出节点的类型约束、属性继承规则与封装粒度的判断标准。
先算一笔复制粘贴的账:溶解效果要在三个角色和一个 Boss 上使用,如果不封装,每张图里都要铺一遍那二十来个节点;之后美术说"边缘色换成青色",你要打开四张图各改一处——漏一处就是线上 bug。Sub Graph 把这段节点链变成一个组件:组件内部改一次,所有引用它的图全部生效。
Sub Graph 与普通图的差别在"接口":它没有主栈,取而代之是一个 Sub Graph Output 节点——你在上面添加输出口,相当于声明"我这个组件提供什么结果";Blackboard 里声明的属性自动成为组件的输入口,相当于声明"我这个组件需要什么参数"。普通图里放一个 Sub Graph 节点,接上输入、取走输出,组件即插即用。

按五步走。第一步:右键项目窗口创建"Sub Graph"资产,命名 SG_Dissolve。第二步:把 6.4 节溶解图里的"噪声组、边缘识别组、配色组"整体复制进来(主色组留在调用方——组件只负责溶解机制,不管物体本身长什么样,这是粒度判断的直接应用)。第三步:Blackboard 里声明输入属性:Noise(Texture2D)、Progress(Vector1)、EdgeInner 与 EdgeOuter(Color)、EdgeWidth(Vector1),它们自动出现在 Sub Graph 节点预览的输入口上。第四步:在 Sub Graph Output 节点上添加两个输出口:EdgeIntensity(Vector1,送调用方的裁剪阈值)与 EdgeColor(Vector3,送调用方的颜色叠加)。第五步:把内部链路与输出口接好,保存。
回到溶解图:删掉被封装走的那三组节点,拖入 SG_Dissolve 节点,把原有属性接上对应输入口,两个输出口分别接裁剪阈值与 Base Color 的颜色叠加。保存后行为与封装前完全一致——这就是封装成功的验收标准:对外零变化,对内可维护。
约束一:Sub Graph 里不能出现主栈特有内容。 裁剪阈值、混合模式这些主栈设置留在调用方,组件只输出数据——这也是"边缘强度送阈值"而不是"组件内部直接裁剪"的原因。同理,Sub Graph 里不要试图设置透明与队列,那是图的职责。
约束二:属性引用与预览。 Sub Graph 的属性在调用方材质面板上不直接出现(调用方看到的是 Sub Graph 节点的输入口),如果某些参数既要在组件内用、又要暴露到最终材质面板,做法是把该输入口接成调用方自身的属性节点。另外注意 Sub Graph 嵌套是允许的(组件套组件),但嵌套超过两层后编译产物膨胀明显,第 8 章会给量化感受,实践中建议控制在两层以内。
💡 封装还有一个隐性收益:团队评审。组件的输入输出口就是它对外的"承诺清单",技术美术评审一个 Sub Graph 的接口,比逐节点审一张大图高效得多——接口对了,内部怎么改都不影响别人。
组件被多张图引用后,改组件就是改所有引用方——这既是优点也是风险:一次修改可能让某个调用方的效果悄然变化。团队策略建议两条。策略一:组件改动遵循"接口冻结"原则——已有输入输出口的语义与默认值不轻易改,新增能力用新输出口扩展而不是改旧口。策略二:重大重构时复制出新版本组件(命名加版本号后缀),新图用新版、旧图留在旧版,迁移完再淘汰旧版。两条策略合成的原则一句话:组件的演化要像软件的接口一样有兼容意识。
Sub Graph 编辑器里也有预览,但它依赖输入属性的默认值——属性没设默认值时预览可能全黑或报错。先给所有输入属性设合理默认值(这也是 7.1 节"接口默认值要合理"的另一个理由),预览即恢复。另一个可能的坑:组件内部用了主栈相关的节点(不小心拖进来的),保存时会被拒绝或行为异常——组件内保持"纯数据处理"的纪律,问题自然消失。
给封装一个物理落点:项目里建专门的组件目录(比如按功能分类:溶解类、描边类、UV 动画类),所有 Sub Graph 入库。三个月后这个目录就是团队的"效果速查库"——新需求先翻库再动手,避免重复造轮子;库里的组件按 7.1 节标准封装,接进来就能用。目录结构本身也是文档:分类清晰说明团队做过哪类效果,新人上手先读库。
组件库里最值钱的不是节点链而是接口说明。给每个组件维护一小段接口文档(写进组件的分组框标题或单独的说明文件):三个输入是什么语义、两个输出什么范围、默认值对应什么效果状态。这份文档在两个时刻发挥作用:新人选组件时不用打开组件逐节点读,接口一眼即懂;调用方效果异常时,先对照文档检查输入是否在预期范围内——大部分"组件不好用"的反馈,最终是调用方喂了超出文档范围的输入。接口即契约,契约要落在纸面。
不会冲突——Sub Graph 的属性在编译时被独立处理,与调用方的 Blackboard 是两个命名空间。但为了人的可读性,建议组件内部属性加前缀(比如下划线开头)与调用方区分。真正要留心的是另一种情况:调用方把一个属性接进组件输入口的同时,组件内部同名属性还留着默认值——行为以接线为准,但两组名字并存会误导后来者。原则一句话:接了线就删冗余,图的状态与文档保持一致。
组件化解决"复用",下一节解决"够不着":节点拼不出的数学,Custom Function 直接请 HLSL 上场——顺便把图与代码的真实关系摊开讲清。