UE5.8 Substrate 分层材质系统:把汽车油漆一层层拆开画
引子:老皮卡上的三层烦恼
美术小周接到了一个活儿:给一辆 1980 年代的老皮卡做材质。车身主体是深蓝色金属漆,表面有一层清漆,门把手和轮眉处又要有锈迹渗透出来。听起来不复杂,用传统 PBR 材质做了三天,她却 increasingly 烦躁。
问题出在哪?她用 Base Color 画金属漆底色,Roughness 控制光滑度,然后在 Clear Coat 参数上叠了一层清漆。锈迹用一张 mask 在 Base Color 和 Roughness 之间做线性插值。远看还行,镜头拉近到车门边缘,清漆底下的锈迹和金属漆开始打架:边缘发黑、能量不守恒、金属感一会儿过强一会儿消失。她把 Clear Coat 强度从 0.3 调到 0.8,又调回去,画面始终不对。
TA 阿杰走过来,看了一眼材质节点图:「你这是把油漆、清漆、锈迹当成一个扁平的 PBR 参数集合在 lerp,当然会拧巴。它们本来应该是物理上分开的三层。」
他打开 Project Settings,把 Substrate 开关打开,重启编辑器。十分钟后,小周看到了一个完全不同的节点结构。每个层都有自己的 BSDF,层与层之间用垂直堆叠(Vertical Stacking)连接,光线先在清漆层反射一部分,穿透的部分再碰到金属漆,再往下才会看到锈迹。清漆、金属漆、锈迹各自的光泽度和颜色不再互相吞噬。
Substrate 就是干这个的。它不是给传统材质加了几个新参数,而是换了一种描述材质的方式。
一、传统 PBR 材质的问题在哪里
传统 PBR 工作流里,一个材质球由一组标量参数定义:Base Color、Metallic、Specular、Roughness、Normal、Clear Coat、Clear Coat Roughness 等等。这些参数描述的是「整个表面看起来像什么」,而不是「这个表面由哪些物理层组成」。
当美术想把几种材质混在一起时,通常的做法是用 mask 做参数插值。比如:
这种做法有两个根本问题。
第一,能量不守恒。两个材质单独看都没问题,lerp 之后反射能量的总和可能超过入射光。金属漆的 本来已经很高,再和锈迹的高粗糙度区域一混,某些角度会出现不自然的过曝或发黑。
第二,层的物理顺序被丢掉了。清漆应该在金属漆外面,锈迹应该在金属漆破损处。用 mask lerp 参数时,清漆和锈迹被压在同一深度,结果清漆的光泽会覆盖到锈迹上,锈迹的粗糙又会污染清漆边缘。
Substrate 的解决思路很直接:材质由若干层 BSDF 垂直堆叠而成,每层有自己的厚度、吸收特性和散射行为,光线按真实顺序与每一层交互。
flowchart TD
A["入射光线"] --> B{传统 PBR}
A --> C{Substrate}
B --> D["单组参数"]
D --> E["Mask Lerp 混合"]
E --> F["能量不守恒 层序混乱"]
C --> G["BSDF 层 A"]
G --> H["BSDF 层 B"]
H --> I["BSDF 层 C"]
I --> J["按物理顺序计算光线路径"]二、Substrate 的核心:BSDF 层
在 Substrate 中,材质的最小单位不再是「参数集合」,而是 BSDF 层(BSDF Layer)。每个层描述光线如何与该层交互:一部分被反射,一部分被吸收,还有一部分可能透射到下一层。
一个 Substrate 材质的输出由根节点 SubstrateGraph 定义,下面挂着若干 BSDF 节点。每个 BSDF 节点本身可以带颜色、粗糙度、法线、厚度等输入。不同 BSDF 节点之间通过 Horizontal Mix 或 Vertical Stack 连接。
flowchart TD
subgraph substrate ["Substrate 材质评估"]
root["SubstrateGraph"]
slab1["Slab 金属漆"]
coat["Slab 清漆"]
fuzz["Fuzz 绒毛"]
metal["Metal 金属"]
mix["Vertical Stack"]
out["像素输出"]
end
root --> mix
coat --> slab1
slab1 --> metal
fuzz --> coat
mix --> out上图中,SubstrateGraph 是入口。Vertical Stack 把清漆、金属漆、金属层按从上到下的顺序压成一摞。Fuzz 作为一个侧向贡献挂在清漆旁边,模拟表面的细小绒毛或灰尘。这种结构在传统材质里很难表达。
三、Substrate 支持的 BSDF 类型
UE5.8 的 Substrate 提供了几种常用 BSDF 节点,覆盖绝大多数游戏材质需求。
3.1 Slab
Slab 是最常用的 BSDF 类型,可以理解为「一块有厚度的介质薄片」。它描述的是非金属或弱金属的体积材质,支持漫反射、镜面反射、吸收和次表面散射。油漆、塑料、石头、木头、皮革都可以用它表达。
Slab 的关键参数包括:
- Diffuse Albedo:漫反射颜色;
- Specular:镜面反射强度,默认对应 F0;
- Roughness:表面粗糙度;
- Absorption:光线穿过 Slab 时被吸收的量;
- Thickness:层的厚度,影响吸收和散射的累积效果。
当光线进入 Slab 时,一部分在表面反射,另一部分进入内部,经过吸收和散射后,从另一侧穿出。 absorption 越高、thickness 越大,透过去的颜色就越暗。
3.2 Metal
Metal BSDF 专门描述金属。金属没有漫反射,所有可见颜色来自镜面反射的 tinted 高光。传统 PBR 里用 Metallic 参数在金属和非金属之间切换,其实是对物理现实的一种妥协。Substrate 里的 Metal BSDF 直接建模金属的复折射率,颜色由 Edge Color 和 Base Color 共同决定。
金属的反射率可以用 Fresnel 公式近似:
其中 是垂直入射时的反射率, 是入射角。金属的 通常远高于非金属,所以边缘会出现强烈的各向异性高光。
3.3 Clear Coat
Clear Coat 本质上也是一种 Slab,只是它的顶部表面更光滑,底部与下一层接触。清漆层会先把一部分光线反射出去,剩余光线进入下面的层。清漆的 Roughness 控制表面光泽度,Thickness 和 Absorption 控制清漆发黄或发蓝的程度。
汽车油漆的标志性效果就来自 Clear Coat 与金属漆层的配合。没有清漆,金属漆看起来会很平;没有金属漆,清漆下面没有内容可折射。
3.4 Fuzz
Fuzz 描述的是材质表面的细小纤维或绒毛,比如天鹅绒、地毯、桃子皮、苔藓。它不是在表面之上加一个简单贴图,而是模拟光线在纤维尖端散射的效果。Fuzz 通常作为侧向输入挂在一个 Slab 旁边,而不是垂直堆叠在表面之上。
Fuzz 的颜色、密度和方向都会影响最终外观。密度高时,表面看起来像天鹅绒;密度低时,只在轮廓边缘出现一圈柔和的光晕。
3.5 其他 BSDF
UE5.8 还继续扩展了 Substrate 的 BSDF 家族,包括:
- Unlit:自发光或纯颜色层;
- Hair:用于头发模型,支持多次散射;
- Single Layer Water:简化版水面材质;
- Atmosphere/Cloud:与体积云相关的特殊 BSDF。
对于绝大多数道具和角色材质,Slab + Metal + Clear Coat + Fuzz 已经够用了。
四、水平混合与垂直堆叠
Substrate 提供了两种把 BSDF 组合起来的方式:Horizontal Mix 和 Vertical Stack。理解它们的区别是做好分层材质的前提。
4.1 Horizontal Mix
Horizontal Mix 按 mask 把两个 BSDF 在水平方向混合。它适合表达「同一表面上有两种不同材质并排存在」,比如石头地面上局部有青苔,金属板上局部有锈迹。
Horizontal Mix 仍然使用 mask,但混合的是 BSDF 本身,而不是底层参数。这意味着每一层都保持自己的能量守恒特性。lerp 的结果不会像传统 PBR 那样出现反射能量异常。
4.2 Vertical Stack
Vertical Stack 是把两层 BSDF 在物理深度上摞起来。上层在表面,下层在底下。光线先与上层交互,没被反射和吸收的部分再进入下层。
sequenceDiagram
participant L as "入射光线"
participant C as "Clear Coat 层"
participant P as "金属漆 Slab"
participant R as "锈迹 Slab"
participant O as "最终出射光"
L->>C: 到达表面
C-->>O: 部分直接反射(清漆高光)
C->>P: 穿透部分进入金属漆
P-->>O: 部分反射(金属漆颜色)
P->>R: 继续穿透到达锈迹
R-->>O: 反射或吸收
O->>O: 各层贡献按物理顺序累加汽车油漆就是典型的 Vertical Stack。清漆在最上面,金属漆在中间,底漆或金属基板在最下面。锈迹如果只在局部出现,可以用 Horizontal Mix 把「完好油漆栈」和「破损锈迹栈」混合起来。
4.3 能量守恒怎么处理
Substrate 在内部维护能量守恒。当两层垂直堆叠时,上层反射走一部分能量,透射给下层的能量会自动衰减。衰减量由 BSDF 的反射率、吸收率和厚度决定。
对于两层系统,最终出射光可以粗略写成:
其中 是上层的反射光, 是上层对下层光线的透射系数。Substrate 在运行时自动保证 ,其中 是吸收部分。
五、实战:做一辆老皮卡的多层车漆
回到小周的老皮卡。我们用 Substrate 重新搭建它的车身材质。
5.1 层结构设计
车身主体需要三层:
- 最外层是 Clear Coat,光滑、低粗糙度、略带厚度;
- 中间是 金属漆 Slab,深蓝色、金属颗粒感、中等粗糙度;
- 最底层是 金属基板 Metal,在油漆剥落处露出来。
锈迹只在门把手、轮眉、保险杠局部出现。我们用一张 Rust Mask 做 Horizontal Mix,一边是「清漆 + 金属漆 + 金属基板」的完整栈,另一边是「锈迹 Slab + 金属基板」的破损栈。
graph TD
A[SubstrateGraph] --> B["Horizontal Mix"]
B -->|"Mask 0"| C["Vertical Stack"]
B -->|"Mask 1"| D["Vertical Stack"]
C --> C1["Clear Coat"]
C --> C2["金属漆 Slab"]
C --> C3["金属基板 Metal"]
D --> D1["锈迹 Slab"]
D --> D2["金属基板 Metal"]5.2 节点搭建步骤
第一步,创建 Material,把 Shading Model 从 Default Lit 改为 Substrate。
第二步,拖一个 Substrate Slab 节点作为金属漆。参数大致如下:
- Diffuse Albedo:深蓝色,略带饱和度;
- Specular:0.5 左右;
- Roughness:0.25,用一张 microflake 噪波贴图扰动,模拟金属漆里的云母片;
- Normal:细颗粒法线,模拟金属漆的橘皮效果。
第三步,拖一个 Substrate Slab 节点作为清漆。参数:
- Diffuse Albedo:接近纯白;
- Roughness:0.05 到 0.1;
- Thickness:0.1 到 0.3 毫米;
- Absorption:极少量黄色,模拟旧清漆轻微发黄。
第四步,拖一个 Substrate Metal 节点作为金属基板。颜色用冷灰色,Edge Color 稍微偏蓝。
第五步,用 Vertical Stack 节点把清漆、金属漆、金属基板按顺序连接起来。Vertical Stack 的上接口接上层,下接口接下层。
第六步,再建一组破损栈:一个 Substrate Slab 作为锈迹,颜色偏橙棕,Roughness 较高,Absorption 较强;下面同样接金属基板。
第七步,用 Horizontal Mix 节点把完整栈和破损栈按 Rust Mask 混合。Rust Mask 是一张手绘的黑白图,白色表示锈迹区域。
第八步,把 Horizontal Mix 的输出连到 SubstrateGraph 的 Substrate 引脚。
5.3 关键细节
金属漆里的 microflake 效果不要靠把 Roughness 拉到极低来实现。真正的金属漆在正面看比较光滑,侧面看会因为云母片而变粗糙。可以用 View Angle 或 Fresnel 节点对 Roughness 做角度修正:
其中 控制侧面粗糙度增加的幅度。这样正面看是清亮的,侧面看有柔和的金属闪烁。
清漆的 Thickness 不是越大越好。太厚会让颜色发闷,像一层塑料壳。老皮卡的清漆经过多年风化,厚度应该偏低,Absorption 略带黄色,边缘会有细微剥落。
锈迹和清漆的交界处不要做得太硬。Rust Mask 应该用高斯模糊或手绘笔刷过渡,让锈迹看起来是从清漆裂缝里渗出来的,而不是贴上去的一块贴纸。
5.4 调试时常看的参数
[/Script/Engine.RendererSettings]
r.Substrate=1
r.Substrate.Debug=1
r.Substrate.MaterialGraph=1
r.Substrate.Visualization=1r.Substrate.Debug:打开 Substrate 调试视图,看每层 BSDF 的能量贡献;r.Substrate.Visualization:切换不同可视化模式,检查层堆叠是否正确;r.Substrate.MaterialGraph:在材质编辑器里显示更多 Substrate 专用节点。
小周用 r.Substrate.Debug 一看,发现清漆层的反射能量在某些角度被金属漆层重复计算了。她把清漆的 Specular 从 0.6 降到 0.45,金属漆的 Roughness 从 0.2 提到 0.28,画面立刻自然了很多。
六、性能与平台考虑
Substrate 带来更真实的分层效果,但也不是免费午餐。理解它的开销结构,才能在项目里正确取舍。
6.1 着色复杂度
传统材质只有一个 BSDF 计算路径。Substrate 材质如果有三层 Vertical Stack,像素着色器需要依次评估每层 BSDF,并把结果按能量守恒合并。层数越多,ALU 开销越大。
粗略估算,一个有 层 BSDF 的 Substrate 材质的像素着色成本大约是:
其中 是单层的 BSDF 评估成本, 是层合并的固定开销。如果每层还带了独立的法线贴图和粗糙度贴图,采样成本也会线性增长。
6.2 内存占用
Substrate 材质需要存储每层的参数和中间结果。在 G-Buffer 中,Substrate 占用的通道比传统 PBR 更多。具体数量取决于当前启用的 Substrate 功能和平台,但通常会比 Default Lit 多 1-2 个 render target。
在内存紧张的平台上,比如 Switch 2 或低端移动设备,要控制 Substrate 材质的使用范围。 hero assets 和重要角色可以用,远景道具和小物件尽量用传统材质。
6.3 平台支持
Substrate 在 UE5.8 中默认在所有支持 SM5 和 SM6 的平台上可用。PlayStation 5 和 Xbox Series X|S 上运行良好。PC 上需要 DX12 或 Vulkan。
移动平台要谨慎。高端移动芯片可以跑 Substrate,但性能和发热会明显上升。项目需要在 Scalability 配置中为不同平台设置开关。
[/Script/Engine.RendererSettings]
r.Substrate.Mobile=0
r.Substrate.Quality=2r.Substrate.Mobile:在移动平台是否启用 Substrate;r.Substrate.Quality:Substrate 质量级别,0 是最低,3 是最高。
6.4 一个性能测试案例
阿杰在 RTX 3070 上测试了老皮卡材质。车身用了三层 Substrate 加上 Fuzz 模拟顶棚织物,地面和远景道具保持 Default Lit。结果:
- Base Pass 中 Substrate 材质占比 12%;
- 单个 Substrate 材质的像素着色成本约为传统材质的 2.3 倍;
- 如果把全车都换成 Substrate,帧率从 62 掉到 48;
- 只在车身、轮毂、挡风玻璃三个关键部位用 Substrate,帧率稳定在 58。
这个对比说明:Substrate 应该用在玩家会近距离观察、光照交互复杂的表面,而不是 blanket 覆盖。
七、UE5.8 中 Substrate 的新改进
Substrate 在 UE5.0 就已经作为实验功能出现,到 UE5.8 已经成熟到可以在生产环境大规模使用。5.8 版本的改进主要集中在稳定性、性能和工具链三个方面。
7.1 编辑器集成更完整
5.8 的材质编辑器对 Substrate 节点做了重新整理。节点颜色、分类和预览方式都比早期版本更直观。新建 Material 时可以直接选择 Substrate Shading Model,不再需要手动改 ini。
材质预览窗口增加了 Substrate 专用的可视化层。美术可以直接看到每一层 BSDF 对最终颜色的贡献,调试分层效果时不再需要打开复杂的调试命令。
7.2 性能优化
5.8 对 Substrate 的像素着色器做了进一步压缩。对于常见的 Slab + Clear Coat + Metal 三层组合,编译后的指令数比 5.7 减少了约 15%。在主机平台上,这个优化能直接换算成帧时间。
同时,Substrate 的 G-Buffer 布局在 5.8 中更紧凑。某些质量级别下占用的 render target 通道数减少,缓解了 VRAM 压力。
7.3 与 Lumen、Nanite 的协同
Substrate 和 Lumen 的配合在 5.8 中更稳定。Lumen 的 Surface Cache 能正确捕获 Substrate 材质的能量分布,间接光在多层材质上的表现更准确。
Nanite 与 Substrate 也没有冲突。Substrate 材质可以应用在高面数的 Nanite 网格上,只要注意材质复杂度不要随着面数一起失控。
7.4 新的 BSDF 和混合模式
5.8 继续扩展 BSDF 库。一些在 5.7 中还是实验性的 BSDF,比如改进后的 Hair 和 Single Layer Water,在 5.8 中转为正式可用。Vertical Stack 的性能和正确性也有提升,多层堆叠时更少出现边缘 artifact。
7.5 迁移工具
把传统 PBR 材质迁移到 Substrate 时,5.8 提供了更好的辅助工具。编辑器可以把一个 Default Lit 材质自动转换为等价的 Substrate 材质节点。转换结果不一定完美,但能省去大量手工重建节点的时间。
阿杰测试了一下迁移工具:一个简单的金属材质转换后基本可用,但带 Clear Coat 的材质需要手动调整清漆层的 Thickness 和 Absorption。自动转换只能做 70% 的工作,剩下 30% 还需要美术介入。
八、常见问题与排坑
8.1 层数太多导致编译失败
Substrate 对层数没有硬性上限,但材质编译器会对过复杂的材质报错。如果你收到「Material too complex for current platform」的提示,先减少 BSDF 层数,或者把不重要的层合并到贴图里。
8.2 黑色边缘或 halo
黑色边缘通常出现在 Horizontal Mix 的 mask 边界。原因是 mask 没有经过 anti-aliased 处理,或者两层 BSDF 的 Roughness 差异太大。把 mask 用稍微模糊的版本,或者在 mask 边缘做 slight 羽化,可以缓解。
8.3 清漆看起来不像清漆
清漆不亮的常见原因有三个:Roughness 太高、Thickness 太大、Specular 太低。汽车清漆的 Roughness 通常在 0.03 到 0.1 之间。Thickness 用 0.05 到 0.2 毫米即可,太大反而像琥珀。
8.4 性能 profiler 看哪里
用 stat GPU 查看 Base Pass 开销。Substrate 材质的额外成本主要集中在 Base Pass。如果 Base Pass 占整帧比例过高,优先减少 Substrate 材质的屏幕覆盖面积,而不是先降分辨率。
九、写在最后
小周的老皮卡最后通过了美术总监的评审。镜头贴着车门滑过去,清漆层在太阳下闪着细碎的眩光,金属漆的深蓝色在阴影里沉着,轮眉的锈迹从清漆裂缝里渗出来,边缘没有发黑,也没有发亮。她说不清哪里变了,但整个车看起来像是可以摸得到温度的金属。
Substrate 给美术带来的是一层新的自由度。传统 PBR 的参数是在一个平面上调色,Substrate 则允许你在深度方向上一层层搭建。这种自由对车、漆、布、皮这类多层材质尤其重要。
但它不是必须开的开关。远处的小石子、低面道具、移动平台上大量重复物体,用传统材质仍然更划算。把 Substrate 当成一种精确工具,只在需要物理层叠的地方使用,它才能真正提升画面质量。
阿杰给小周定了一条内部规范:「车身、武器、主要角色、近景道具可以上 Substrate;远景、小杂物、地形第一层用 Default Lit。开了之后必须用 stat GPU 验证,别凭感觉。」
对团队来说,Substrate 最大的价值不是让画面更炫,而是让美术和 TA 不必再在参数 lerp 的泥潭里反复挣扎。油漆就是油漆,清漆就是清漆,锈迹就是锈迹,各层各算各的。光线照上去,该反射的反射,该穿透的穿透,该吸收的吸收。这种物理上的清爽,最终会变成视觉上的真实。
小周现在再做老皮卡,会先画三层结构图,而不是先调 Base Color。阿杰觉得,这比任何参数都重要。