材质系统重写 2026:从着色枚举到物质分层
把「选一个着色模型」换成「组合若干物质薄片」,着色模型的组合爆炸问题就变成了一个闭包预算问题——本文拆解 Substrate 的 Slab / Operator / Tree 三元模型、coverage 与 transmittance 的统计语义、闭包简化算法,以及 G-Buffer 字节契约如何反过来约束材质创作。
约 49 分钟阅读14,425 字1 次阅读博主

把「选一个着色模型」换成「组合若干物质薄片」,着色模型的组合爆炸问题就变成了一个闭包预算问题——本文拆解 Substrate 的 Slab / Operator / Tree 三元模型、coverage 与 transmittance 的统计语义、闭包简化算法,以及 G-Buffer 字节契约如何反过来约束材质创作。

一句话摘要:把「选一个着色模型」换成「组合若干物质薄片」,着色模型的组合爆炸问题就变成了一个闭包预算问题——本文拆解 Substrate 的 Slab / Operator / Tree 三元模型、coverage 与 transmittance 的统计语义、闭包简化算法,以及 G-Buffer 字节契约如何反过来约束材质创作。
几乎所有商用引擎的材质系统,都建立在一个朴素的假设之上:每个像素只能属于一种着色模型。Unreal Engine 的传统路径提供 Default Lit、Clear Coat、Subsurface Scattering、Cloth、Anisotropy 等一整套并列的 Shading Model,美术在材质图根节点上选一个,着色器按这个选择走一条分支。
这个设计在项目早期极其高效:分支少、寄存器压力可控、每种模型都能被单独优化到极致。但它有一个无法回避的结构性缺陷——你无法混合两种外观。
考虑三个真实场景。第一,美术想让一块 SSS 材质表面再覆一层塑料护甲,于是需要同时具备次表面散射与清漆高光这两个特性。第二,美术想做一块高级布料:经纬两组丝线各带各的各向异性高光,上面还盖一层塑料薄膜。第三,美术想在金属与塑料之间做渐变过渡。
在着色模型枚举的体系里,这三个需求都落到同一个结论上:不够用,那就再加一个着色模型。但每加一个着色模型,工程成本是实打实的:要实现新的 BRDF、要为它做 GPU 验证、要加渲染路径、要扩 shader 排列组合、要维护各平台降级逻辑。更糟的是分支与寄存器压力会随着着色模型总数一起增长,最后连最简单、最常用的那个模型也会被拖慢。
更隐蔽的一层代价在物理正确性上。传统 PBR 用一个标量 metallic 在介电质与导体之间切换,这个做法在两者之间过渡时会产生一圈不受欢迎的光晕(halo)。这是 PBR 本身的已知局限——用单一 metalness 标量去近似一个本质上是连续的材质空间,必然在边界处产生赝影。而老式着色模型体系根本没有腾挪空间去修它。
还有一条约束容易被忽略:延迟渲染架构下,可存储的输入数量是有限的。G-Buffer 的字节预算是一个硬天花板,每多一个着色模型,就得多占一个 RT 的槽位或者多压一个字节。
所以问题不是「怎么让着色模型更好」,而是**「能不能不要着色模型这个概念」**。
Unreal Engine 5 引入的 Substrate,给出的答案是一次概念替换:不再枚举外观,而是描述物质。
Substrate 的核心主张是把材质建模为若干片「物质」(Slab)的组合,每片物质是一个有原则的 BSDF 表征(principled BSDF representation),参数使用有明确物理单位的量。两个 Slab 之间通过算子(Operator)连接,算子有三个:水平混合、垂直分层、覆盖度加权。所有 Slab 与 Operator 组成的结构是一棵树(Tree),树的拓扑就代表了这个材质的物质组织方式。
这个设计最关键的一点是:Slab 是物理上有意义的最小单元。它不是一堆 BSDF 的加权和,而是「一片物质」,由两部分组成——界面(Interface)与介质(Medium)。界面描述光与表面交互的那一层,介质描述光进入物质内部后如何散射与吸收。
这个抽象带来的直接收益是可组合性。艺术家想要「SSS 表面 + 塑料护甲」,正确的表达不是「造一个新的着色模型」,而是「两个 Slab + 一个垂直分层算子」。想要「金属渐变到塑料」,则是「两个 Slab + 一个水平混合算子」。组合爆炸没有被消除,但它从着色模型维度的爆炸转移到了材质树维度的爆炸,而后者是可以被算法化管理的。
树的叶子是 Slab,内部节点是算子。遍历这棵树会输出一组闭包(Closure)。闭包是「送去做光照求值的一袋参数」,它已经消化了拓扑结构、观察方向与光路的影响,每个闭包可以独立求值,输出的亮度色相互相加就得到最终像素。
这个「树 → 闭包列表」的转换,是整个系统里最核心的一步抽象。后面所有关于字节预算、闭包计数、材质简化的机制,都建立在它之上。
值得对照的是行业里其他材质标准的选择。Substrate 团队评估过 Autodesk Standard Surface、Adobe Surface、MaterialX、RenderMan Lama 与 NVIDIA MDL,结论分成两派:一派是固定拓扑的规范(Standard Surface、OpenPBR),问题是拓扑被钉死,无法表达更复杂的表面;另一派是BSDF 汤(soup of BSDFs,MDL 与 MaterialX 属于此列),把一堆 BSDF 混在一起,代价是难以做可扩展性、难以做有效的求值、也难以做参数压缩与共享。
Substrate 选了第三条路:用有原则的 Slab 表征替代 BSDF 汤。这个选择的分量在本文第六节会变得非常明显——参数混合之所以可能,正是因为 Slab 是有原则的。一个 Specular BSDF 和一个 Diffuse BSDF 在 BSDF 汤的框架里根本无法合并,但两片 Slab 可以。
一个 Slab 的界面部分使用微表面场(microfacet field)建模,行业标准足够好,没必要重新发明轮子。
反射率参数化上,Substrate 用 F0 表示法向入射下的反射率。选择 F0 而不是复折射率(complex IOR)的理由很实际:F0 是归一化的,跨材质迁移时语义清晰,而复折射率带着一个不好处理的虚部。
F90 是一个更特殊的选择,它只影响色度而不影响强度,在掠射角上不削减能量。Substrate 演讲里坦承这是整个参数化里最不物理的一个,团队说可能后续会转向 Adobe 的 F82。这是个 opt-in 特性——不用它就不付代价。
粗糙度与各向异性上,主镜面反射使用 GGX 分布,各向异性沿主轴与副轴定义,取值范围 [-1, 1],同样是 opt-in。各向异性的参数化采用 Kulla 与 Conty 的方案。切线基(tangent basis)的存储是自适应的:如果表面各向同性,只存一个法线;如果各向异性,则存完整的切线基,用两个八面体编码(octahedral encoding)并量化到 32 bit。
还有一个容易被忽略的耦合:主镜面粗糙度与漫反射粗糙度默认是耦合的。这与传统 PBR 实践中「金属不关心漫反射粗糙度、介电质的漫反射和镜面粗糙度应当一致」的直觉是矛盾的,但耦合更省参数。
第二镜面波瓣(secondary GGX lobe)通过一个自由权重与主波瓣混合。演讲中提到还提供了一个按感知方式混合两个波瓣的辅助节点。
Fuzz(微观起伏层)是另一类界面特征,可以表达 asperity 层的水平散射,它始终位于镜面波瓣之上,拥有完整参数化:粗糙度、反照率、覆盖度。
Glint 同样是 opt-in 特性,用于车漆、积雪这类闪烁效果。它基于 Chermain 等人在 i3D 2021 的工作,用预计算的 LUT 定义不同 LOD 下的 glint 行为。原理是控制 glint 在 UV 参数化上的密度,当观察距离拉远、像素足迹逐渐覆盖多个 glint 时,材质会收敛到单个 GGX 波瓣。
SpecularLUT 则是最后一块拼图,它允许接入一张基于视角与光照角度参数化的查找表,为整个 Slab 提供统一的着色。用于蛋白石效应、复杂车漆这类 Slab 模型本身表达不了的视角相关现象。
介质部分采用体积化表述,用平均自由程(Mean Free Path)与反照率来描述——它们是散射系数与吸收系数的对偶。这个表述的价值在于它能连续地覆盖从纯漫反射到完全透明表面的整个区间,而不需要为每种外观单开一个模型。次表面散射的相关参数包括 SSS MFP、SSS MFP Scale 与 SSS Phase Anisotropy。
厚度的处理需要分情况。单个 Slab 或最底层的 Slab,如果表面被视为「厚」的,厚度由几何边界定义——光栅路径下可以用常量值、SDF 或阴影贴图来估计,路径追踪路径下则直接取射线交点距离。如果表面是「薄」的(比如树叶、布料这类平面),厚度需要在材质层面定义,Substrate 为此在根节点提供了 Surface Thickness 输入。当一个 Slab 覆在另一个之上时,上层 Slab 的厚度由垂直分层算子提供。
这里有个容易忽略的细节:粗糙透射的成因是界面粗糙度非零,而不是介质散射。为了模拟它,需要从界面的反射率反推 IOR,再用一张按粗糙度与深度/厚度参数化的预计算 LUT 计算「光扩散」,最后由一个后处理 pass 按此做模糊。在光线追踪或路径追踪路径下,这一步可以直接依赖射线求交完成,不需要 LUT。
还有一层能量守恒的问题:当光线打到微表面并被弹到另一个微表面时,如果没有模拟这种多次反弹,高粗糙度(尤其是导体)会明显丢能量。Substrate 依赖 Turquin 2019 年的多次散射补偿方法,同时用预计算的方向反照率表来保证透射到介质的能量正确。
三个算子对应三种不同的物理操作,但它们的语义都要落到同一套统计量上。这里先把三个术语钉死,因为后面所有公式都建立在这个区分上。
Coverage(覆盖度) 定义为「命中这一层存在的概率」。Transmittance(透射率) 定义为「这一层的衰减量」。两者组合得到 throughput:
用官方演讲里的例子来校准:一个粗糙金属球,覆一层绿色介电质。当介电层足够厚时,光只能从介电层反射回来,透射率为 0;减小厚度,光能部分穿过介电层到达下面的金属,透射率开始大于 0;继续减小,透射率继续上升。而在后三种情况中,介电层始终挡在光路上,覆盖度是 1——只有当介电层薄到光"直接"打在金属上时,覆盖度才降到 0。
覆盖度是 slab 存在的概率,透射率是 slab 的衰减量,这是理解整个系统的钥匙。
水平混合算子在两份材质之间插值。它会同时求值两份材质,然后混合求值结果,混合因子定义了各自 slab 的覆盖度——第一个 slab 的覆盖度是 mix,第二个是 1-mix。
垂直分层算子把一个 slab 叠在另一个之上。关键性质是:分层会改变下层材质的外观,因为上层介电质的透射率会给下层的各向异性材质染上偏红色调。分层操作可以叠加,比如再叠一层灰尘。
覆盖度加权算子改变的是一个 slab 的「存在感」。这里有个必须分清的区分:变厚度与变覆盖度不是一回事。
用垂直分层算子改变上层厚度时,上层的透射率与吞吐量都会变,但始终能看到它的镜面反射。用覆盖度加权算子改变上层覆盖度时,上层的透射率保持不变,只有吞吐量随覆盖度变化——但当覆盖度降到 0,镜面反射也就随之消失。
这三组公式可以在数学上精确写出。以一个子树为例,若上层覆盖度为 、透射率为 ,垂直加权给出新覆盖度 ,透射率 原样向下传播。
水平混合的情形:左子树覆盖度 、透射率 ,右子树 、,混合权重为 ,则新覆盖度是加权求和
而透射率是加权求和后再按覆盖度重新归一:
当上下两层叠加时,官方给的是一套概率树推导。设上层覆盖度 、透射率 ,下层 、,则:
需要注意的是,这套统计表示假设各个 Slab 的覆盖度之间没有相关性。这是一个明确的近似,也是后面简化算法能够成立的前提。
树的遍历分三步:第一步初始化每个 Slab 的 coverage/transmittance 统计量;第二步深度优先处理算子,由算子收集子节点的代表性统计量;第三步让每个 Slab 沿树向上走到根节点,沿途计算 ToViewThroughput 与 TopTransmittance。
TopTransmittance 表示上方所有表面对某个 Slab 的总透射率,计算基于指数衰减:
而沿某个具体光照方向 观察时,需要把法线方向的透射率重映射到该方向:
根节点上的 coverage 与 transmittance 合起来,就是整个材质表面的总 coverage 与总透射率,可以直接算出吞吐量,用于把表面与场景色做 alpha 混合。
到这里为止,一切都还优雅。麻烦出现在树变复杂的时候。
考虑一个拓扑:4 个 BSDF 分布在 3 层里,其中第 3 层覆盖着一个由两层组成、各自覆盖度不同的复杂子结构。问题来了:光在材质内部可以走很多条不同的路径。它可能穿过 S0 再穿过 S2 到达 S3,也可能穿过 S0 再穿过空气到达 S3,还可能穿过 S1 再穿过 S2 到达 S3。
演讲中给出的完整交互集合是 、、、、、——6 条路径。每条路径会导致不同的吞吐量和不同的感知粗糙度,因此严格按定义来算,BSDF 3 需要输出 6 个闭包。这显然太贵了。
解法是统计聚合:把任意多个 slab 组合成一个虚拟 slab,用固定的一组属性来代表它——transmittance、coverage、roughness,以及一个折射波瓣。在这个统计表示下,上例中 BSDF 3 只需要输出 1 个闭包。
这里的关键洞察是:这个聚合之所以合法,正是因为 Slab 是有原则的物质表征。各个算子对聚合统计量的影响是可以推导出来的:覆盖度算子只降低覆盖度,不影响透射率、粗糙度与折射波瓣;水平混合与垂直分层各有对应的组合规则。
渐进简化(progressive tree simplification)的思路是:按从最深的子树开始的顺序,逐个用参数混合(parameter blending)折叠算子,折叠到预算之内为止。
举个具体例子。默认材质有 3 个波瓣,存成 3 个闭包共 72 字节。先对最深的子树用参数混合处理水平混合算子,2 个 slab 合并成 1 个,结果是 2 个闭包 48 字节。再对垂直分层算子做同样的事,1 个闭包 12 字节。
三个档位:72 → 48 → 12 字节。这里的「12 字节」这个数字很值得记住,它暗示了一个闭包的最小打包尺寸。
这套流程里有两个工程细节值得强调。
第一,编译时必须按最坏情况的拓扑生成代码。Substrate 的翻译器知道静态拓扑、共享切线基、以及启用的特性,于是生成静态树的访问代码。之所以要展平成静态代码,是因为编译器处理动态树遍历的 for 循环会出现奇怪的行为甚至崩溃。
第二,共享切线基要合并存储。如果多个 slab 用到同一个切线基,只存一份,以节省 G-Buffer 内存。同时系统会追踪每个 slab 启用了哪些特性,据此估算最坏情况的复杂度与每像素字节数,这样才能按项目设置里的最大 G-Buffer 字节预算去简化。
这里必须重复一遍前文的论断:这套算法在 BSDF 汤的框架里做不到。MDL 与 MaterialX 那种「一堆 BSDF 混合」的结构,无法做参数混合简化——一个 Specular BSDF 和一个 Diffuse BSDF 根本无从合并。而 Slab 是有原则的物质表征,参数本身带物理含义,因此可以按经验规则做混合。
现在把前面所有机制落到实际约束上。
延迟渲染下,材质数据必须装进 G-Buffer;前向渲染下,材质直接求值,不受此约束,但移动端项目可能仍然不希望每像素求值超过一个闭包。Substrate 演讲里给的约束表非常清楚:前向路径在「必须装进内存」这一项上打叉,延迟路径三项全打勾。
于是有了两套 G-Buffer 格式的选择。
Blendable GBuffer 把固定内存占用与可预测的速度放在首位。它的功能集有限,接近非 Substrate 路径;性能与非 Substrate 路径持平;目标是 60Hz 档项目;跨平台视觉一致性有保障;没有烹饪(cooking)开销;并且不强制启用 DBuffer 贴花技术。
Adaptive GBuffer 目标是视觉保真度,释放 Substrate 的全部潜力,允许更丰富的材质复杂度。它的性能开销更高,且取决于屏幕上材质复杂度;视觉保真度依赖具体平台;烹饪时间相比 Blendable 增加约 15%,材质本身着色行为复杂的话还会额外增加;它强制启用 DBuffer 贴花技术;并提供 Closure Count 与 Bytes Per Pixel 两项额外控制。
平台矩阵是硬约束:Adaptive GBuffer 只在当代主机 Xbox Series X/S、PlayStation 5 与 PS5 Pro、Windows PC(SM6)、macOS(SM6)、Linux(SM6)上受支持;其他平台(包括 SM5 世代)一律降级为 Blendable。
默认策略上,新建项目默认使用 Blendable GBuffer(优先速度而非视觉保真度),而 Automotive 与 Architectural 这类模板项目默认使用 Adaptive GBuffer(优先视觉保真度)。
存量项目的迁移路径也有明确设计:升级到 UE 5.7 及以后的现有项目默认仍走非 Substrate 路径,除非在 Project Settings > Rendering 里显式开启 Substrate materials 并重启项目。
更精细的是编译期兼容:现有非 Substrate 材质开箱即用,但不会被自动转换成 Substrate 节点——它们在编译期被转换到 Substrate 表示。这个设计避免了资产修改与重存,简化了项目迁移,也降低了烹饪成本。真正的显式转换是右键根节点选 Convert to Substrate,它会自动创建一个 Slab 接到根节点的 Front Material 上。
但这里有两条不可逆的硬边界,值得单独标注:显式转换过的材质不兼容非 Substrate 项目,转换是永久的、无法回退;以及在关闭了 Substrate 的项目里,Substrate 材质会渲染成黑色。
项目设置层面有三项关键旋钮。Substrate GBuffer Format (Project) 决定项目用哪种格式;若选 Adaptive,只有兼容平台会用它,不兼容的自动落到 Blendable。Substrate Closure Per Pixel (Project) 定义每像素最多求值多少个闭包,若材质闭包数超出,会迭代简化直到塞进预算。Substrate Closure-Per-Pixel Override 决定每平台的闭包上限如何计算:默认取平台上限与项目上限的较小值,也可以强制以项目设置为准。
还有一项贴花相关的耦合关系。Blendable 格式同时支持可混合贴花(blendable decal)与 DBuffer 贴花,DBuffer 默认开启但可以关掉。Adaptive 格式只支持 DBuffer 贴花——如果项目没有使用 DBuffer 贴花,系统会自动为支持 Adaptive 的平台启用它。
Substrate 还支持在 DBuffer 求值期间读取材质法线,作为一条实验路径(由 r.Substrate.DBufferPass 控制)。它的意义在于让 DBuffer 材质能使用材质自身的法线,而不需要时域重投影或基于深度的法线重建。
从 UE 5.8 的发布说明里还能看到这条机制在持续演进:一批针对 Substrate 的着色器排列组合优化正在进行,其中一项是当只使用 Substrate-Blendable 时,不再编译 Substrate-Adaptive 专用的 ComplexSpecial 着色 tile 排列组合。另一项则是在 IndirectLighting 合成阶段减少着色器排列组合,并在间接光照 pass 中启用 Substrate 的 classification stencil 写入以提升性能。
材质系统的可调试性往往被当作次要问题,但在一个闭包数量、字节预算、树拓扑三重耦合的系统里,没有工具就是没有工程。
Substrate 的编辑器工具围绕三个问题设计。
第一个问题是「我到底搭了什么」。 编辑器提供材质拓扑预览(material topology preview),把树的结构画出来,并显示复杂度分级(Slab count、memory、properties、classifications)。这里的关键在于一个设计选择:拓扑预览展示的是 BSDF 树与 BSDF(Am) 两种视图——前者是按波瓣组织的旧视角,后者是按物质组织的 Substrate 视角,两者并列正是为了让美术理解「我原来那套写法在新系统里对应什么」。
第二个问题是「它花了多少字节」。 编辑器提供材质简化预览(material simplification preview),可以手工覆盖每像素字节预算(官方示例里是 80 字节),并直接应用这个覆盖值看简化后的结果。Substrate Stats 面板则给出详细输出:字节预算、被请求的简化前字节数与简化后字节数(示例中 48 字节 = 12 个 UINT32)、总 BSDF 数量、共享局部基数量、材质复杂度分级、根节点是否为薄面、材质类型(Multiple Slabs),以及每个 slab 的 SharedLocalBasisIndexMacro 之类的编译期宏。
第三个问题是「这个像素到底算成了什么」。 编辑器支持指针指向任意像素做调试,把该像素的详细材质信息打出来。
这套工具的可视化视图族在 UE 5.8 里被进一步扩充——视口工具栏新增了 View Modes 子菜单,可视化子菜单覆盖 Buffer、Nanite、Lumen、Substrate、Groom、Virtual Shadow Map 与 Virtual Texture。
编译期的正确性同样依赖工具链。在 UE 5.8 的发布说明里能看到针对 Substrate 表达式图的实质性重构:涉及 UMaterialExpressionRerouteBase 的四个方法(IsResultMaterialAttributes、IsResultSubstrateMaterial、GatherSubstrateMaterialInfo、SubstrateGenerateMaterialTopologyTree)都增加了 ContainsInputLoop() 守卫。这个细节看着不起眼,但它揭示了材质图的一个真实风险——表达式图里可以出现环路,而在材质图编译期,一个环路会导致无限递归或栈溢出。
Substrate 的设计目标之一,是让同一个材质在光栅化路径、光线追踪路径、路径追踪路径之间视觉一致。这听起来是显然的要求,但实现起来处处是坑。
粗透射就是个典型例子。光栅路径需要预计算 LUT 加后处理模糊,而光线追踪与路径追踪路径直接依赖射线求交。两条路径的近似方式不同,一致性只能做到近似。
多次散射补偿在光栅路径上依赖预计算的方向反照率表,而路径追踪路径上是天然精确的——这意味着光栅路径在某些粗糙度下会系统性偏离参考实现。
Lumen 的兼容性问题更具体。UE 5.8 的发布说明里记录了几条与 Substrate 相关的修复:修复了 Substrate-Adaptive 下 Lumen card 的漫反射颜色累积问题;修复了 Substrate-GBuffer 在使用 Lumen hit-lighting 时的次表面着色模型伪影,根因是射线命中载荷里的次表面颜色不正确;在 MegaLights 中修复了 Substrate Format=1 的光晕伪影,原因是邻居像素复用了属于不同拓扑材质的 history。
最后这条特别值得玩味:当材质拓扑不同时,共享 history 会产生错误。这是时域滤波与材质系统耦合产生的典型问题——时域重建假定相邻像素的历史可以复用,而这个假定在材质拓扑突变的边界上不成立。同一批修复里还有一条相关的:MegaLights 的时域滤波在只使用 center sample 时会丢失细节,修复方式是增大 denoiser 时域滤波邻域。
还有一条互斥关系值得记住:r.SSS.SeparatedDiffuse.Enable 引入的次表面漫反射分离特性,与 Substrate 的不透明粗糙折射互斥——因为后者已经在自己的 buffer 里分离了漫反射。这个 CVar 还需要分配专用的 R16F 纹理,官方明确标注「低端系统不应开启」。
至于着色模型的细节演进,UE 5.8 里也有几条:Substrate 现在始终使用 Sheen LTC,原有的 SheenQuality 设置被移除;粗糙漫反射 BSDF 从 Chan 模型改为 EON 模型;为 Blendable GBuffer 格式补充了 F90 近似;为导体材质增加了 Substrate IOR 到 F0 的变体;在启用 Substrate 时毛发改用 Substrate 光照。
而 **Substrate NPR Shading(卡通着色)**在 UE 5.8 里作为实验特性引入,它构建在 **Substrate Blendable GBuffer(旧版模式)**之上,支持包括局部光源、天光与 Lumen 全局光照在内的所有光源类型,通过新的 Substrate Toon BSDF 与 Toon Profile 资产精确控制漫反射与镜面响应。它提供的能力包括带抖动的 ramp-based 漫反射与镜面控制、带排线图案的自阴影消光、各向异性镜面高光,以及全局光照强度缩放控制。
注意「构建在 Blendable GBuffer(旧版模式)之上」这个措辞——一个在 2026 年才加入的实验特性,选择了较旧、较受限的 G-Buffer 格式作为基座。这说明 Blendable 路径的稳定性价值在高风险新特性上反而更高。
同期还有一条与 Substrate 相关的导入能力:支持将 X-Rite 的 AxF 格式实测材质导入引擎,通过 Substrate 材质系统实现,面向汽车与工业可视化团队,把从 X-Rite Pantora 导出的高保真真实扫描材质直接带进引擎。
至于 MegaLights 与 Substrate 的交互,UE 5.8 里也有一条性能相关的记录:启用 Substrate 后为 MegaLights 增加了各向异性支持,并且不再向同一光源追踪多条射线(当目标不在半影区内时),在主机上根据场景不同可带来 0.3 到 1 毫秒的性能提升。
最后把视角拉回自研引擎。如果现在要设计一套材质系统,Substrate 提供了几个可直接抄的决策点。
第一,材质表征应该是「物质」而不是「外观」。 这不是措辞游戏。物质表征带有物理量纲,因此参数可以按物理意义混合、按预算简化、按平台降级;外观枚举没有这些性质,只能靠穷举分支。用 BSDF 汤(MDL、MaterialX)虽然解决了互操作性,但把可扩展性问题原样保留了下来。
第二,组合爆炸要正面处理,不能回避。 闭包组合爆炸是材质组合化必然的代价,Substrate 的做法是:统计聚合把多条光路压成单个虚拟 slab,渐进简化按深度优先顺序折叠算子,参数混合作为折叠手段,特性禁用作为最后的兜底。这四级手段构成一条完整的降级链。
第三,字节预算必须是编译期的一等公民。 材质树编译时就按最坏情况拓扑生成静态代码、合并共享切线基、追踪每 slab 的特性开关、估算每像素字节数。运行时不做任何动态决策。这直接决定了 G-Buffer 格式选择、平台矩阵、以及「哪些平台用 Adaptive、哪些用 Blendable」这类工程决策。
第四,统计量要选对。 coverage 与 transmittance 的区分是整个系统的骨架。它们的组合给出 throughput,而 throughput 才是真正用于混合的量。很多材质系统在 alpha 混合上出鬼片,根因就是没能正确维护这两者的分离关系。
第五,跨路径一致性要作为一等目标设计。 光栅、RT、PT 三条路径对同一个材质的近似程度天然不同:粗透射、多次散射、体积介质这三处的差异最明显。材质系统应该显式记录哪些近似是路径特有的,并在比对时把它们排除掉,而不是让美术去猜「为什么光追版看起来不一样」。
第六,工具与调试视图不是可选项。 拓扑预览、字节预算面板、逐像素调试在组合化材质系统里是必需品。Substrate 官方示例中那个「48 字节 = 12 个 UINT32」的输出,就是这种工具链的直接产物。
几个需要谨慎采纳的点。 Adaptive GBuffer 的 15% 烹饪时间增长是实打实的成本,官方给的建议是新项目用 Blendable、只有汽车与建筑可视化这类以视觉保真度为第一诉求的模板才用 Adaptive。F90 参数化官方自己都说最不物理、可能后续换掉,采纳时应留好替换路径。BSDF 汤在跨厂商互操作场景里仍然是现实需求,Substrate 解决不了这个问题,它解决的是「自有引擎内的高保真材质创作」。
关于自研引擎的规模判断。 完整的 Substrate 实现需要:Slab 参数体系、树的编译与展平、统计聚合算法、渐进简化、两种 G-Buffer 格式与字节预算、跨路径一致性机制、编辑器全套拓扑与字节工具、以及跨平台降级矩阵。这是一个以年为单位的工作量,不是一个小特性。务实的路线是先落地混合算子 + 固定拓扑的最小版本——让美术能在一个材质里混两种外观,就已经能解决枚举体系最痛的「混合即鬼影」问题;闭包预算与自适应简化可以在有了真实项目反馈之后再上。
最后留一个观察。Substrate 从 5.2 的实验特性一路走到 5.7 默认对新项目启用、5.8 继续扩展卡通着色与实测材质导入,它的每一步都在同一个方向上使劲:把「材质有多复杂」这个问题,从美术的直觉判断,变成编译器的可计算约束。这大概是 2026 年引擎材质系统最值得学习的一点。
Conversation
0 条