此页就分层着色器的背景进行讨论。
分层通常用于在计算机图形中创建更加复杂、更加美观的材质。从混合材质到简单的涂层材质(例如:木材上的虫漆、湿石材等),再到更复杂的混合材质(泥点斑斑的车漆,其中泥是漫反射泥层上的水层分层,泼溅于包含透明涂层、颜料、油漆碎片等的多层车漆上)。
注意 我们所讨论的是物理角度上可信的材质分层(非“Photoshop 层面”的分层)及其多种混合模式,包括本身并不符合物理基础的分层。可以使用这些模式通过分层纹理来创建效果,但这些模式不适用于混合材质物理效果。
从生产要求角度看,分层着色器必须:
当使用 mib_color_mix 等简单的颜色合并着色器进行着色器分层时,会产生效率问题。当然,从视觉上来看可能会是我们想要看到的效果,但却会引起多个问题。
最重要的是,随着复杂性的增加,性能效率会呈几何级数下降。我们将举例说明这一点。
此图像使用简单的数学颜色合并进行渲染。 6 分钟完成渲染 |
这与使用分层着色器渲染的场景完全相同, 58 秒完成渲染 |
上面图像中显示的场景包含一个由多层组成的材质。
对于更具挑战性的场景,差别可能会更加明显 - 尤其是在封闭的空间中,无法控制的反射光线数以几何级数增长,甚至很容易就导致速度减慢 10 倍或 100 倍。
类似的测试场景使用大量透明度,在 22 秒(有分层)和 15 分钟(无分层)之间有区别。
分层着色器由一组旨在协同工作的 C++ mental ray 着色器组成,这是一个着色器工具包;此工具包包含专用组件合并着色器和一组提供各种着色组件模型的组件着色器。实施源于 mia_material 和由 NVIDIA ARC 渲染产品过程中开发的技术。尤其是,组件元素类似于 NVIDIA 材质定义语言 (MDL) 中可用的材料。由于没有预定义的层数,因此,您可以使用多个基本组件(例如额外的光泽度或散射组件)来提高任何着色模型。
接下来,我们一起来发现问题并考虑如何用分层库来解决这些问题:
想象一下将三个着色器混合在一起: 其中一个占 80%,另一个占 20%,第三个占 0%。如果这三个着色器插入到非智能数学节点,则所有三个着色器将完全执行。因此,如果所有这三个着色器使用 50 个采样的光泽反射光线,渲染会投射 150 束光线,因为数学模式中执行所有这三个。
此外,当这些光泽反射光线以类似混合的方式投射到次曲面,则次曲面上的数学模式将执行所有输入着色器。并且,即使插入到混合器的各个着色器和 mia_material 一样智能,在较大的跟踪深度下仅投射单束光线,但如果有三个着色器插入到混合器中,则仍将有三束光线投射。
这听起来可能不多,但在以几何级数增长的光线树中,很快便完全难以处理,因为在每一跟踪深度连接处光线树都会变为三倍。
这样做的代价可能很大,但视觉效果却并不理想。它还可能阻碍统一采样的使用(统一采样的目标正是提升视觉效果),具体取决于每个眼光线结果。理想情况是,在这种情况下,我们希望实现最小的次光线跟踪,使用统一采样自适应性来关注哪里需要改进质量。
问题就在于组件着色器不按重要性来执行。第一个着色器不知道它占 80%,第二个也不知道自己占 20%,第三个则完全不起作用,不需要调用。所有这三个着色器都是盲目地执行任务,就好像它们是唯一能代表材质的着色器。
为匹配视觉重要性,我们倾向于使用第一个着色器 80% 的光线,使用第二个着色器 20% 的光线,而第三个着色器根本不执行。
由于合并着色器的所有组件输入都是 shader 类型的,因此这些组件可以按需执行(对于 0% 的示例则完全不执行)。此外,状态 - > 重要性变量需要先调整,然后再调用,这样被调用的着色器可基于此变量做出选择,以减少光泽反射的光线数等内容。最后,如果我们在跟踪深度的点击数低于第一次的数目,则我们只依据由组合权重确定的概率来跟踪其中一个组件的次光线。此设计可利用统一采样的模型。
如上所述,使用统一采样后,渲染含复杂分层的场景的速度会加快 10 倍到 100 倍。这一方面是因为光线数以几何级数增长的属性,另一方面是因为统一采样只把时间用在需要突显视觉效果的地方。
与光线类似,由传统灯光循环提供的灯光采样可能会出现问题。
如果对三个着色器进行分层,让每个着色器包含一个灯光循环,则将会有三个灯光循环。每通过灯光循环一次,每个灯光采样都需要一次阴影光线跟踪,这样很快就变得非常耗时。毕竟,在大多数场景中,阴影光线数占主导地位,因为包括环境光线在内的各类型光线都会产生跟踪阴影光线。
此问题的解决方法是,使用特定于分层着色器的 API 来提供灯光采样。这将可以存储灯光采样,仅在定向时重新运行该灯光采样。否则,循环首次运行后,将会自动重用灯光采样。这样,每个组件可以重复灯光采样,重用其灯光采样值,而无需一次次地跟踪阴影光线。
在包含大量透明度的场景中可能会有很大的速度优势(2 倍至 20 倍),因为每条阴影光线都会分解成许多小的光线段,从而使性能下降的幅度更大。
尽管操作简单,但单个输出着色器组件的简单合并最有可能会忽略更复杂的多个输出组件着色器的输出。
虽然可以通过其他“数学节点”合并这些次级输出,但是不同的着色器具有不同的输出样式,最初只有某些特定种类的输出可以合法混合。对于实际项目,着色器网络很难维持其复杂性。
分层着色器特定的 API 可以保证从每个组件着色器输出到各种照明过程特定的帧缓冲区。使用规范源于 NVIDIA 材质定义语言 (MDL) 中的灯光路径表达式 (LPE)。从指定灯光路径聚集的灯光可能会累积到用户帧缓冲区。通过字符串选项,用户可以识别指定支持的灯光路径可能会写入到的用户帧缓冲区。有关支持 LPE 过程的更多细节,请登录此处。
经加权后,输出被写入,这样便可以相加。也就是说,所有的过程就是它们相加的总和。许多其他着色器(如 mia_material)具有“raw”和“level”样式的输出,而这些是适用于单个着色器的输出,它们不能被合并;您不能轻易延迟“raw”和“level”的乘积值来合成,这是因为将采样过滤到像素的操作行为会造成不可逆转的数学复杂性,难以在后期解决。
帧缓冲区的写入机制也需要按帧缓冲区中所述的要求处理透明度。
若要实现此目的,写入帧缓冲区的着色器需要混合调用 mi_trace_transparent() 时的值。然而,如果 mi_trace_transparent() 调用位于着色树的叶着色器中,则无法以正确的顺序控制这些调用(因为着色树可能包含 mi_trace_transparent() 的多个不同调用)!
现在,我们要按照帧缓冲区页面的说明写入帧缓冲区。对 mila_material 进行特别处理便可解决该问题,这将:
在 mental ray 中,光子和阴影着色器的行为与普通表面着色器的行为不同,仅凭连接两个表面着色器的输出并不能将其转换为合法甚或有效的光子着色器,而某些情况下(碰运气而定)可以充当阴影着色器使用,有时则不能。
分层着色器将光子和阴影着色器功能分开,并配有简化了的组件功能。