学习进度
0/9 已勾只保存在这台浏览器;勾满 9 项即视为本题完成。
先把题目说成人话
先把另一套引擎放在一边。假设你在熟悉的节点式着色器编辑器(Unity Shader Graph)里做了一串“判断物体边缘位置”的节点,五份着色器节点图都复制了它。后来公式需要修改,你改了四份、漏掉一份,同一种效果便悄悄变成两个版本。
你熟悉的引擎可以把公共节点抽成子图(Sub Graph)。另一套引擎也有同一种用途的资产,叫材质函数(Unreal Material Function)。因此,这题真正问的是两件事:哪些计算应该只保留一份,让许多材质共同使用;公共计算改变以后,怎样避免所有旧材质一起被悄悄改坏。两边的复用思想相近,但材质与实例体系并不完全相同。
这是虚构教学故事,不是个人项目经历。
场景:先让问题变得看得见
先在你熟悉的 Unity 场景里观察,理解以后再把名词翻译成 Unreal。
(右脑)画面:五个角色并排站在同一盏灯下。皮肤、盔甲、布料和幽灵各有不同材质,但身体外沿都需要一圈相同的黑白范围。最初,这串计算被分别复制进五份图里。小明修正公式时只改了四份。角色一起转身后,四个人的轮廓已经变得自然,第五个人仍顶着原来那圈刺眼白边。五份看起来相同的节点,已经在不知不觉中长成了不同版本。
(左脑)对表:在 Unity 中,这五份完整节点图可以理解为五份 Shader Graph,共同计算可以抽成一份 Sub Graph;在 Unreal 中,完整材质图是父材质(Parent Material,团队也常俗称 Master Material),公共计算是 Material Function。当前能直接观察到的事实是“五份输出不一致”;原因可能是漏改、参数不同或平台分支不同,还要在同一灯光与参数下比较。把节点放进公共资产只能防止复制内容继续漂移,不能自动证明共享范围选得正确。
小明:先形成一个完整猜测
小明认为,既然公共函数能防止漏改,不如把边缘计算、颜色、透明度、粗糙度、噪声和质量开关全部塞进同一个大函数。
五份材质确实接上了同一个模块,但盔甲被迫看见幽灵才需要的透明参数,皮肤也携带自己不会使用的闪烁开关。为了照顾不同材质,函数里不断增加分支;每次调整幽灵效果,团队又必须确认皮肤和盔甲有没有被牵连。
小明解决了“节点被复制五遍”,却把五种不同的艺术用途绑进一个公共黑盒。可以共享同一个问题的计算方法,不能因为它们都叫边缘光,就强迫所有材质以同一种方式使用结果。
小红:把猜测放回现实检查
小红把公共范围缩小到一件稳定的事情:输入表面方向、观察方向和少量控制值,输出一张从零到一的黑白边缘遮罩。
公共模块不负责颜色,也不负责透明、粗糙或闪烁。五份材质收到同一张遮罩以后,各自决定用途:皮肤用它添加一点暖色,盔甲用它加强边缘反光,幽灵用它控制透明,布料只使用很弱的一部分。
在 Unity 中,这是一份输出遮罩的 Sub Graph;在 Unreal 中,同样的公共节点资产叫 Material Function。它通过函数输入与输出(Function Input / Function Output)接收数据并交回结果。若美术需要在 Material Instance 中调节边缘宽度,父材质先建立公开参数,再把参数送进函数。父材质决定哪些旋钮公开,函数只完成稳定计算。
这就是程序设计里的高内聚、低耦合:函数内部只围绕一个明确目标工作;调用者通过少量、清楚的输入输出依赖它。低耦合不是没有依赖,而是依赖一份稳定、可检查的约定。
我:用检查结果修正模型
共享也带来了新的危险。旧函数一直输出从零到一的黑白遮罩;后来小明希望它直接输出带颜色的最终边缘,于是在原函数上修改了输出含义。
五份材质同时出错:皮肤变得过亮,盔甲的反光范围改变,幽灵把颜色误当成透明关系,布料也收到自己不需要的彩色结果。函数没有编译失败,真正被破坏的是它与调用者之间的约定。
因此我把修改分成两类:若只是修正内部算法,输入、默认值、输出范围和含义都没改变,可以继续修改原版本,但要回归所有调用者;若输入含义、默认值、类型或输出范围改变,就建立 MF_Rim_v2,保留旧版,让材质逐个迁移。
迁移前先用 Reference Viewer 找出全部调用者。新版只接入一份测试材质,在固定灯光下比较画面、参数与编译结果;确认以后再逐批替换。最后一个旧材质完成迁移以前,旧函数不能删除。
Material Function 会成为调用材质的一部分。把节点抽成函数主要解决组织、复用和维护问题,不代表运行时只计算一次,也不会自动减少像素指令、采样器、绘制次数或静态变体。
真实经历、职责和结果必须由你自己补上,例如真实函数资产、受影响材质数量、迁移记录和本人负责的决定;这段故事不能冒充 Unreal 项目经验。
(右脑)画面修正:五个角色仍站在同一盏灯下,中间只有一张公共的黑白边缘图。正确修复落到这张图上时,五个角色同时变好;但新版若改变了这张图代表的意思,就不能偷偷把旧图换掉。新旧两张图应当并排存在,先让一个角色走到新版下面确认结果,再让其余角色逐个移动过去。共享让变化传播,版本边界决定这次传播是修复还是事故。
(左脑)严格对表:每个公开 Material Function 都应写清输入名称、数据类型、坐标空间、是否需要归一化、默认值、合法范围和输出含义。父材质负责建立实例可调参数并把值明确传入函数;函数负责稳定计算,具体艺术用途留在父材质。内部等价修复可以留在原版本,但必须检查全部调用者;契约变化时新建版本,通过 Reference Viewer 列出依赖,在固定关卡逐批迁移,并比较画面、指令、采样器和编译情况。完成全部迁移后,旧版才可以退休。
老师解惑
- Unity Sub Graph:在一个或多个 Shader Graph 之间复用相同节点计算,是理解 Material Function 最直接的桥梁。
- Unreal Material Function:通过 Function Input / Output 定义公共节点计算的接口。
- 父材质与 Material Instance:父材质决定完整渲染结构和公开参数;Material Instance 继承父材质并覆盖允许调整的参数。
- 不是完全等价:普通 Unity Material 主要引用 Shader 并保存属性;Unreal Material Instance 有明确的父子继承关系。Unity Material Variant 在继承方面更接近它。
- 复用不等于性能优化:函数减少的是重复维护,不会自动减少最终 Shader 的计算量。
- 版本冲突:真正危险的不是文件同名,而是同一个输入或输出在新旧版本里代表了不同意思。
工具分工
- Unity Shader Graph / Sub Graph:作为已经熟悉的理解桥梁。
- Unreal Material Function:保存稳定、可复用的材质计算。
- 父材质 / Material Instance:分别负责完整艺术用途和实例参数。
- Reference Viewer:查看一个函数修改后会影响哪些材质。
- 固定材质测试关卡:让所有调用者在相同灯光下接受版本回归。
记忆钩子
共享稳定的计算,保留各自的艺术用途;输入输出换了意思,就建立新版本。
这是教学场景,不是目标项目的真实捕获或个人经历。
下一步:验证这条因果
把输入、空间、版本和采样预算写成显式契约。
教学边界:它只验证这一条画面因果,不替代完整答案、项目经历或目标设备数据。