SIGGRAPH 粗读丨看看虚幻5的复杂材质系统(上)


3楼猫 发布时间:2024-11-21 16:33:02 作者:Hakumen Language

前言

提到渲染材质
,很多人能想到的可能最常被提到的“PBR材质”或是“SSS材质”之类的缩写,或是“毛发”、“衣料”、“水体”等更接近现实中物体的概念。
其实仔细想想就会发现,前两者更接近一种经验模型,通过近似归纳的算法来提供尽量写实的效果;后两者的实现则是一套或多套不同复杂度的组合方案,基于不同引擎、不同开发者、不同平台都会有多元的方案,但几乎都无法通过一次简单的绘制来完成。
但不管是哪种方案,在目前大多数的情况下,在实时渲染的引擎中还是以提炼好的功能模块为主——这意味着一旦需要一个新的复合材质,就需要一定程度的自定义开发,而且往往有比较多的限制(后面文中也会介绍到)。
本次会介绍的是虚幻5引擎中2023年提出的一套被称为基底(Substrate)组合的方案,这是一套艺术家易用性为主的复杂材质方案。
本文还是以翻译原文PPT页及解说稿为主,打星号的部分则是我个人的补充。由于篇幅原因还是会拆成上下两篇,上篇介绍Introduction、Slab、Operators这几部分。

1 引入动机——Introduction / Motivations

The current material model in UE is based around the notion of Shading Models. A shading model target a specific set of visual features, proper to a given appearance. For instance, we have a shading mode for general dielectric/conductor, for ClearCoat, for Subsurface scattering, for Cloth, ..
当前UE中的材质模型是基于着色模型(Shading Models)的概念建立的。一个shading model是以一组视觉特性为目标,为实现一种特定的外观。例如对于绝缘体、导体、清漆、次表面散射、布料等。
每个shading model能覆盖一定范围的外观谱系(spectrum)。
多数现存的shading model使用的是艺术家的实现方案,同时保持了高性能。例如《堡垒之夜》最高能以120Hz运行。
另外,所有这些shading models在我们的不同渲染路径之间保持视觉一致(visually consistent),无论是光栅化路径,还是光线追踪方式的Lumen或path tracer路径。
然而,尽管内置了大量特定的shading model,这种方式还是阻止了艺术家自由的进行创作。(*原文用了silo一词,直译过来的一个意思是发射井)
例如,一个艺术家希望将SSS表面与金属表面混合。
在shading model的方案中,由于我们对一个像素只能提供一个shading model,这种混合就会导致硬边界或不连续。
在合适的混合后,过渡外观会变得更平滑。(使用了Substrate方式来演示)
另一个例子,如果一个艺术家想创造一个基于次表面的材质,但包裹在塑料壳或被汗水环绕呢。
同样的,仅通过shading model的方式,无法同时兼顾SSS模型和覆盖层(Coat)模型的特性。
另一个更进阶的例子。想象艺术家想构建一个进阶的布料材质。这种布料有着两种丝线的集合(例如,有着各向异性高光),相互垂直以模仿某种编织图案。并且布料还会被一层塑料层覆盖。
再次的,这种需求也无法通过shading model方式实现。
这个例子是我们现有shading model中的一个限制,更多是与我们的实现方式有关。
UE default shading model uses the notion of metalness to make the distinction between conductor and metallic. When transiting between the two types, an undesired halo can appear. This is a known limitation in PBR, but something we couldn’t address before dropping the notion of metalness in Substrate.
UE的默认shading model使用了基于金属性(metalness)这一概念来区分导体和金属。当在两种类型之间过渡时,会出现一个预期外的光晕。这是PBR中一个已知的局限,不过在引入Substrate之前我们无法解决。
所有这些例子都突出了这项事实——如果一个艺术家想得到特定的进阶外观效果,都需要对现有shading model的一些修改,甚至创建全新的shading model。
每次都为这种例子添加新shading model,可能会导致参数排列组合式的爆发。
同时,添加新的shading model也需要工程师花时间来实现并确保其如预期一样生效。
长期来看,这样做也增加了整体的着色开销。一个更复杂的shading model在实现时,通常也会增加分支逻辑和参数注册上的压力,最终拖慢整体着色的性能。
为了应对这种性能倒退,人们可以调整项目设置,按需启用这些进阶shading model。但这又带来维护和测试上的问题。
此外,UE是基于延迟渲染架构,这限制了可存储的输入的数量。(*下篇介绍数据存储中会提到)
在所有这些限制下,我们感觉很难引入并支持进阶材质。
这时Substrate方案出现在了视野里。
Substrate是一套复合框架,基于内建好的材料模块艺术家可以集成或创建复杂的外观。
然而,这套框架需要能保持和旧系统一样的性能,也要能保持和旧方案在不同渲染路径上的视觉一致性。
此外,这套框架需要是动态规模的(scalable)。对于一种复杂材质,我们不希望为高端设备和低端设备重新指定不同的变体。
在工业界存在很多材质的标准和规范。MaterialX似乎是在工业界有着最广泛应用的框架——然而,这些规范都有一些不符合我们需求的限制。
要么它们依赖具体的拓扑定义(Autodesk Std Surface, Adobe Surface, OpenPBR),使我们无法创建更多进阶的表面模型;要么它们依赖一堆BSDF(*原文是a soup of),这阻碍了高性能地进行实现(有着太多分支和循环),也很难基于简单的规则来动态控制材质复杂度。
*BSDF(Bidirectional Scattering Distribution Function) ,双向散射分布函数。和BRDF差一个字母,但这个复杂度往往高很多。
这就是我们开始开发Substrate的原因。如前所述,Substrate是一个框架,它基于以下3个概念构成:
  • Slab是我们描述具体材料matter 也可以理解成材质特性)的方式。
  • 操作符Operators是我们对其做组合操作的方式。
  • 树结构Tree是材质的拓扑结构描述方式。例如,不同的材料之间的相互关系和组织方式。
为了演示,让我们举一个例子。这里是一个由两种材质特性构成的材质:
一个基础层,由金属和各向异性纤维构成,这被表达为一个Slab
另一层由有一定吸收率的绝缘体构成,这被表达为另一个Slab
两个Slabs之间通过上下层方式来组织,通过一个垂直层操作符(vertical layering operator)连接。
*这里是演讲人的全部章节安排。这里我的上篇中覆盖了:Slab、Operators这两部分。

2 Slab定义材料——Slab Defining Matter

*Slab直译是厚板、 板坯等,后续以作者拆解的多层结构作为理解方式,本身保持英文不翻译了。
在这个架构中,slab被用来表达一类材料。这也是我们与大多数已有shading model统一化的方式。
你可以把一片材料想象成一个表面(interface)和有着一定厚度的一层介质(medium)组合而成。
所有属性都基于物理属性,这帮助我们不通过太多复杂的再参数化(convoluted reparametrization)就能描述一个材料。通过使用参数单元也能帮助我们保持一致性——它帮助我们思考如何控制光照与这些属性的交互(计算)方式。
一个反例就是gltf,或是其它有着漫反射透射(diffuse transmission)或是颜色吸收(color absorption)等等特性的规范,你控制的是完全不符合整体直觉的一些单一的影响项。
*gltf是一套3D模型的行业规范,包含了材质部分。
表面层如你能想象的一样简单——它定义了两层介质之间的分割层,以一个微表面场的方式被建模。这部分的工业标准是简单而实用的。
这层表面通过一些不同的属性进行参数化,下面我们会介绍其中的一部分。
*这里的参数有的描述起来可能很复杂,但只要理解最终影响的都是某一像素最终计算的光照值即可。
为描述反射率,我们使用了一个F0参数(这里指一个表面的法向入射的反射率)——它是便利且归一化了的,不像一些复杂的折射率(如IOR)参数。(*这里说反射率也包含了折射吸收,稍微了解的应该知道这本来就是光传播在介质同时发生的一种材质特性,如图)
为了描述更进阶的导体材料,我们使用了一个F90参数,它影响色度(chromaticity 对应HSV颜色空间)而不是强度,不减少透射时的能量。这个参数是其中最不“物理”的一个参数,后续我们可能用Adobe的F82来替代它。这个特性是一个可选项,意味着你只有当用到它时才会额外消耗性能。
对于微表面描述,我们使用一个简单的基于GGX的NDF。再次的,工业标准在这里是足够好的,我们不用自己从头造轮子。(NDF是Normal Distribution Function法线分布函数。GGX、NDF在Games101中有介绍,另外参数如图所示)
作为默认,我们的原始高光粗糙度是与漫反射粗糙度结合的。
对于各向异性(anisotropy),我们使用了Kulla&Conty参数标准。
We adapt our tangent basis representation based on usage. If a surface is isotropic we will store a simple normal, but if the surface is anisotropic we will store a full tangent basis with two octahedral encoding (quantized onto 32bits).
基于自身使用情况,我们调整了基于切线的表达方式。 如果表面是各向同性(isotropic)的,我们仅存储一个简单的法线,而对于各项异性表面我们需要存储基于两个八面体编码(two octahedral encoding 离散化装入32bits数据)的切线数据。
备注:后续我们会发现,一个材质可能由多个Slab构成,但都共享内存中同一份切线数据。更多的说明会在后续讲存储的章节介绍。
可能有点神秘的是(原文用了esoteric这个词),我们有一个第二级的GGX波瓣(lobe),它以较低的权重混合进了第一级的波瓣中。
这样做的主要意图是为表面描述添加更长的“高光尾段”(‘specular tail’ 参考图中分布函数)。一个基础的GGX NDF有其自身的尾段分布,不过对于特定的制成材料,它的高光波瓣尾段可能更长。通过混合两种特定波瓣,我们可以重现这一朦胧的外观效果,使我们同时可以看到锐利的反射和朦胧模糊的部分。
我们也开放出了一个辅助节点(材质蓝图中的)用于以特定方式混合两个波瓣。
一个Slab可以描述其表面层的粗糙。经过这一粗糙层光以水平方式散射(图中右侧的波瓣分布),而不像高光波瓣那样(垂直分布)。
这些粗糙层总是在高光波瓣的上层,并且有着完全的参数化表达(roughness, albedo, coverage *图中以Fuzz开头的部分)。
*这里举例的就是衣料表面的绒毛——Fuzz。
正如所提到的,我们使用一个微表面场来为表面建模。这些微表面有着特定的尺寸,因此当我们靠的足够近时,我们就会看到它们。这时就会出现“闪亮”(glints)的效果。
你可以有选择地接入glint特性,并通过一个密度控制器来控制它们的尺寸,在表面UV上参数化。为了达到实时渲染的要求,我们使用了Chermain的方案——使用预计算的LUT来定义不同LOD下的glint行为。(*LUT——LookUpTable)
对于给定的glint密度,当你的视野远离表面时,像素的覆盖范围内逐渐会包含越来越多的glint。在达到特定距离后,它就会通过一个单一的GGX波瓣来覆盖。
这主要用于车漆、雪和其它需要闪亮效果的地方。
作为前述的反射率参数的补充,一个Slab可以补充提供一个SpecularLUT。这个LUT定义了一个统一化的“染色”效果,参数化基于视线和光照角度。
这一LUT可以通过颜色坡度或纹理的方式来控制——参数化也是基于视线和光照角度。
*这里解释下,这是一个二维的LUT,即一个轴是编码好的视线角度,另一个轴是光照角度,坐标对应的唯一点是Tint结果值。
这在表现乳白色(opalescence)外观或一些复杂的车漆模拟时都会使用到。
当表面有粗糙度时,光线会被散射(折射)。这能创造出一个粗糙传播的外观效果。在这种情况下,粗糙的外观是由表面造成,而不是介质散射(例如次表面传播)造成。
为模拟这一效果,我们从表面的反射率参数中恢复出IOR折射率。
对于我们的光栅化路径,我们预计算了以粗糙度和深度(厚度)为维度的LUT,以计算光的分布。之后通过一个后处理pass来计算相应的模糊效果。
对于光追路径,我们可以简单地依赖一个射线检测来计算这个效果。
*这里的粗糙度散射和之前绒毛的粗糙度不一样,而且这里更多关注的是折射问题。
目前为止我们没有涉及的一个方面是管理不同slab之间的能量传播。
When an incident light ray hits the interface, it will hit a given microfacet. At that point, it might be reflected, transmitted, or hit another microfacet as it get reflected
当一个间接光照的射线命中表面时,它会命中给定的微表面。在特定点上,它可能被折射、穿过或命中另一个微表面而被反射。
在后一种情况下,我们需要模拟这里的多次“弹射”以避免损失能量。这在高粗糙度的表面会非常明显,尤其是对导体而言。为模拟这一能量传递过程,我们采用了Turquin的方案。(对比图见图中红框部分,用经验性的方式补充了一部分应该反射的能量)
第二个重要的方面是,我们需要确保传递了正确数量的能量到下层介质中。例如,所有未被表面层折射或吸收的光能,需要传递到介质中去。对于这一点我们使用了表面定向的预计算查找表——也如Turquin的方案中所述。
*篇幅原理这里就不展开解释Turquin的方案了,有兴趣可以去搜搜原文。文末附了论文链接,另外我记得在Games202中也有相关讲述。
结束了表面层的介绍,让我们深入介质层的部分。
Our medium is based on a volumetric formulation. The intent was to have a unique parameterization which can represent the continuum between diffuse to fully transparent surface and abstract any artistic parametrization.
我们的介质层是基于一个体积化表达式(volumetric formulation)。这样设计的意图是能有一种独特的参数化方式,以连续体(continuum)的方式描述从漫反射到全透明表面的材质,以及抽象出任何艺术表达用的参数。
因此,一个介质由以下部分来描述:
  • 无效区段(Mean free path),例如一个光子命中介质的粒子前自由通过的距离。后面缩写成MFP。
  • 反照率(Albedo 也翻译成漫反射系数或反射率),例如用以总括多次弹射后的介质反射率。它是散射和吸收两个系数的结合。(*Albedo are just the dual of scattering & absorption coefficients)
  • 各项异性的相函数(Phase function),用以描述命中介质粒子后的散射方向。
  • 厚度(Thickness),用以描述介质的深度。
通过这样的参数化方式我们可以覆盖完整的外观谱系——从全向漫反射到全透射。
简单来说,你也可以把它看成:
  • Diffuse albedo控制外观的“乳白色”程度
  • Mean free path控制表面的透射程度
从方案实现的视角来看,你也可以把这个外观空间划分为4部分:
  • 不透明漫反射opaque diffuse)部分,视线无法透过材质表面,光也无法从像素覆盖区 中漏出
  • 光学厚optical thick)的部分,视线无法透过材质表面,但像素覆盖区中有些光能漏出
  • 光学薄optical thin)的部分,视线能透过材质表面,同时仍有一些光散射发生
  • 透明translucent)部分,作为前者的一种特殊情况,没有任何内部散射
下面让我们看看如何实现这几部分。
对于漫反射的部分,我们使用了一个反光(retroreflective)模型——基于Chan的方案。
This approximates a diffuse microfacet model, who the roughness is coupled with the primary specular.
这种方案提出了一种漫反射微表面模型的近似,将粗糙度与基础高光相结合。
其中粗糙度控制反光的量,例如光在掠射角(几乎90度的夹角)方向反弹的量,这在高粗糙度时会尤其明显。
如图表中所示,这种“管理方式”(原文是regime,直译是体制)是在像素覆盖区相对MFP时足够大时采用的。
当像素覆盖区和MFP相比较小时,我们就进入了optical thick的模式。
此时我们应用常规的SSS技术:
  • 对于光栅化路径,我们使用基于Xie的方案的后处理pass
  • 对于光追路径,我们使用随机游走算法(random walk algo)
需要注意,在我们的实现中,如果一个材质由多个slab构成,这一计算只应用于最底层的可见slab。对于光栅化路径,我们可以退回到例如wrap lighting等方案以提升性能。
*wrap lighting是次表面散射近似方案中的一种简化方法,它的核心思想是做颜色移位来模拟吸收与散射。
Optically thin的情况是实时渲染中很不常见的,因此也很难处理。它拼合了一部分视线能透过的半透明,以及一些大范围的散射(朦胧)效果。
在我们的情况中,我们聚焦于它在叠在另一个slab上层的情况。
这个技术方案包含两部分:光线散射回弹,以及光线透射通过表面。
  • 对于透射的部分我们使用一个简单的Beer Lambert模型来计算吸收。
  • 对于散射的部分,我们依据预计算的LUT——参数化来自单次散射的反照率(从漫反射率估计),重新缩放后的MFP(相对于1M的厚slab),以及视线、光照角度。这一LUT通过离线计算这些条件组合的情况来得出(MFP/albedo/view/light)。
这一方案目前只解决了第一步,也就是能量的部分,但没有处理光的“扩散”以显示成朦胧表面的部分。(*原文这里提到以后再加这个特性)
最终这种特定情况下的optically-thin表面,虽然有了视觉“透过”的效果,但没有任何散射效果。
对于光栅化路径,我们使用双来源混合(dual source blending)的方式来处理透射的部分。没有任何神奇的部分,都是工业标准方式。(*这里就是常说的Alpha blend)
The important part here is that we decoupled the notion of transmission from the notion of coverage. This split the convoluted ‘opacity’ term into two explicit notions. More on that later.
重要的部分是我们将光传播(transmission)的概念从光覆盖(coverage)的概念中解耦出来了。通过这么做把晦涩难懂的“不透明度”(opacity)概念拆分成了两个确定的概念,后面我们会提到。
从中我们可以看出,表面材质元素在视觉上的规模(scale)决定了我们在材质用应用的策略。(*基于MFP和Pixel Footprint像素覆盖区的比较)
我需要强调,采用的单位是很重要的。使两者都采用合适的世界空间单位,能有助于如何渲染一个材质的外观。
最后,介质的厚度也需要列入考虑。它定义了光在其中传播的距离,这会影响“光的衰减“结果。
当它是单一层,或多层材质中的最底层介质,我们需要考虑这两种情况:
  • 如果表面被视为是“厚”的,它的厚度是被几何体的范围来定义的。在光栅化路径中,我们可以使用一个常量、一个SDF或一个shadow map来估计厚度值。对于光线追踪路径,它能够通过射线相交计算得出。
  • 如果表面是“薄”的,例如几何体是一个平面(植物、衣物等),它的厚度需要通过另外的方式定义。此时我们在材质层级来定义它的厚度,在根节点上以Surface Thickness参数输入。
For other layer, we also define the thickness through the graph. This is only necessary when a slab is ‘coating’/’layered on top of’ another slab. In such case, the thickness of the top slab is provided by the Vertical operator.
对于其它层,我们也能通过材质图来定义厚度。这只在slab是其它层上的覆盖层(coat)时是必须的。此时,上层slab的厚度是通过Vertical运算符来提供的。
*小结一下,这里位置slab方案主要提炼了目前为止的主要的透射材质方案,并为之设计了组合的可能性。其中在介质层有其独创的设计,而在表面层方面更多的是把现有的工业化方案进行组合。

3 运算符——Operators

我们有3种主要的运算符:
  • 水平混合(Horizontal mixing)——类似之前两种材质之间的过渡和混合
  • 垂直层叠(Vertical layering)——想象两个材质之前的覆盖层关系
  • 覆盖权重(Coverage weighting)——可以把它想象成alpha blending,使一种材质看起来更“透明”
但首先,我们需要适当地定义一些术语(terminology)来确保谈的是同一件事。
假设我们有一个粗糙金属球体作为基础。当光照命中表面时,它直接基于它的粗糙金属性反射。
现在,假设我们把它覆盖在一层绿色的绝缘体材质下。
在图中最左侧,材质的厚度很大,因此当光线命中表面时,它只通过绝缘体层反射。所有光传播都被绝缘体层吸收了。此时,绝缘体层的传播系数(transmittance)是0。
如果我们降低绝缘体层的厚度,仅有一部分光会被它吸收,光线就会达到下方的粗糙金属表面。此时,绝缘体层的传播系数就大于0。
如果我们进一步降低厚度,更少的光会被吸收,则传播系数会进一步增加。
在前三个例子中,绝缘体层始终存在——从任何角度的光都会命中它。因此绝缘体层的覆盖率(coverage)是1;另一方面,在最后一个例子中,绝缘体层不存在,它的覆盖率就是0。
依此推演,0.5的覆盖率意味着光有50%的几率命中绝缘体层和粗糙金属表面,以及另50%几率直接命中金属表面。
因此覆盖率定义了slab的存在率,而传播系数定义了一个slab的“1-衰减系数”。
组合在一起,两者共同定义了一个slab的通过率(throughput)。
水平混合运算混合两种材质slab。
假设有一个金属材质,我们将其“水平混合”至一个SSS材质。这意味着我们将分别计算两种材质,并混合两者的计算结果。混合系数源自slab覆盖率的定义。第一个slab将有着等于mix值的覆盖率,同时第二个有着“1-mix”值的覆盖率。
而垂直层叠操作,则允许一个材质slab覆盖在另一个之上。
假设我们有一个各向异性的纤维材质。我们想为它覆盖一层绝缘体材质,这样它的传播系数就会影响纤维材质的外观,产生一层微红的染色效果(如图)。
多层效果可以合并,例如我们再将另一层粉尘材质覆盖再其上,它又会影响传播系数以及底部层的通过率。
最后,则是覆盖权重运算。
我们从一层有着一定吸收率的绝缘体开始。光传播通过这层表面,被部分吸收,产生图中所示的微黄的传播系数。
通过覆盖率的权重调整,slab表面“存在”的像素数量被修改了:
  • 如果覆盖率是1,我们能看到完整的表面。
  • 如果覆盖率是0,则完全看不到。
  • 在两者之间的覆盖率参数,我们能看到表面的一部分。
*类似天气预报的降雨概率,也是一种覆盖率。
很重要的一点是,这种组合操作能明确区分出调整厚度和调整覆盖率的外观区别。
以图中的橙色绝缘体为例:
通过垂直层叠操作,我们可以调整顶层的厚度来影响底层的光传播系数及顶层的通过率,但始终能看到这一层的高光反射。
而通过覆盖率操作,当我们调整顶层的覆盖率,上层的光传播系数保持不变,但它的通过率改变了(由于覆盖率的变化)。(*以图中为例,覆盖的分布是一种离散的情况)

结语

以前常有“五彩斑斓的黑”这个调侃,确实从静态的绘制来说某种意义上是无法实现这种效果的。但实际上自然界人们确实能从昆虫的甲壳上观察到这种视觉现象,而实际上虽然“五彩斑斓”“一闪一闪”和“黑”在同一时间确实不是同时存在的——这和观察角度、光照等都有关系,但在一个动态时间的尺度上,这种材质确实是存在的。
通过这次介绍的复合材质系统,这种材质确实能做到了,而这是之前通过普通的shader model无法实现的。
下篇会介绍这篇分享的后面部分,覆盖一些更偏向架构和数据设计的部分。

最后是资料链接:
Authoring Materials That Matters - Substrate in Unreal Engine 5 的PPTX
Practical multiple scattering compensation for microfacet models这篇论文的一个下载地址
Real-time subsurface scattering with single pass variance-guided adaptive importance sampling 论文的一个下载地址
Material Advances in Call of Duty: WWII 在动视的文章地址

© 2022 3楼猫 下载APP 站点地图 广告合作:asmrly666@gmail.com