如何将着色器从游戏引擎转移到Substance Painter

我叫Taras Uleisky,我是Plarium Kharkiv的技术艺术家。为了在移动设备上优化Survival RPG的图形,我们使用了自定义着色器。它们涉及使用独特的纹理和贴图,与其他流行的着色方法中的纹理和贴图不同。因此,对于3D艺术家来说,如何为游戏中的资产创建这些纹理尚不完全清楚。为了立即了解3D模型在纹理化阶段在游戏引擎中的外观,我将着色器移到了Substance Painter。目前,Substance Painter中几乎没有API资料,我自己研究了此主题,因此我决定分享自己的想法。



Unity着色器


游戏使用matcap底纹。除了通常的diff纹理之外,还将两个预先创建的Matcap纹理转移到着色器中。它们分别使用两个蒙版进行插值和模糊处理。结果,Matcap纹理乘以漫反射和假眩光,并在材料上看到反射。



下面的示例显示了如何在着色器图中实现Matcap。在这种情况下,将两个Matcap纹理打包为一个并划分为通道。即,通道R和G中分别为金属和非金属。



通过检查器插入两个Matcap作为示例。



结果与PBR着色中的金属和非金属有一定的类比。

我们想增加材料的粗糙度和污垢,以在PBR着色中创建某种粗糙度类似物。为此,我们使用了纹理化方法。MIP映射一连串的纹理创建了所谓的MIP金字塔,其最大分辨率为1x1。例如:1×1,2×2,4×4,8×8,16×16,32×32,64×64,128×128。这些纹理中的每一个都称为MIP级别。要基于遮罩逐个像素地在着色器中实现磨损,您需要选择所需的MIP级别。结果是这样的:蒙版上的像素为黑色,在Matcap上选择了最大MIP级别,而像素颜色为白色,则MIP级别为0。





因此,着色器可以模拟反射和高光,增加光的粗糙度和磨损。而且,所有这些都无需使用Cubemap,复杂的照明计算和其他可显着降低移动设备性能的技术。



设置Substance Painter以创建着色器


Substance Painter中所有可用的着色器均以GLSL编写。
具体来说,要为Substance Painter编写着色器,我使用了免费的VS Code。对于语法突出显示,最好使用VS Code扩展的Shader语言支持。



Substance Painter中有关API的资料很少,因此在“ 帮助/文档/着色器” API中找到的标准文档是无价的。



有助于编写着色器的第二件事是Substance Painter中的标准着色器。要找到它们,请转至... / Allegorithmic / SubstancePainter /资源/架子/ allegorithmic /着色器。

让我们尝试编写最简单的不显示明暗器的着色器。首先,创建扩展名为.glsl的文本文件,并编写一个简单的着色器。也许,虽然还不清楚,但我将在Substance Painter中进一步详细介绍着色器的结构。



创建一个新项目,并将着色器拖动到您的外壳上。在“ 将资源导入到”下拉列表中,选择项目“ project_name”



这是必需的,以便可以更改所有更改。

现在转到“ 窗口/视图/着色器设置”,然后在出现的窗口中选择新的着色器。您可以使用搜索。



如果您看到整个模型是白色的,并且可以在上面绘制基础颜色,那么说明您所做的一切正确。现在,您可以保存项目并转到下一部分。



如果模型是粉红色的,则很可能是着色器中有错误-有关此情况的通知将出现在控制台中。

在Substance Painter中构建着色器


以前面描述的未照明着色器为例,考虑着色器的结构。



着色方法是着色器的基本部分;没有该方法将无法使用。内容将在3D模型中显示。所有最终的计算都通过diffuseShadingOutput()函数输出

第3行和第4行分别创建一个参数和一个变量。该参数将基础颜色通道与将在其中存储绘制的纹理的变量相关联。所有参数在帮助中都有详细说明,在使用基本色的情况下,应按照示例中的说明进行所有说明。第8行在3D模型的uv坐标中布置纹理。我注意到,对于具有基本色的纹理,将使用稀疏虚拟纹理系统,因为该库与第一行连接LIB-sparce.glsl

您可以找到许多Matcap的实现,但是其要点是模型的法线指向摄像机,并且纹理沿x和y轴旋转。为了使法线向摄像机旋转,我们需要视图矩阵或矩阵类型。您可以在上述证书找到一个



因此,这些是与基色相同的声明名称。现在我们需要获取3D模型的法线。


零是向量的第四个元素。

将视图矩阵与法线向量相乘将使法线向摄像机扩展。



不要忘记,在矩阵相乘时,因子的顺序很重要。如果更改乘法顺序,结果将有所不同。

现在,您可以从viewNormal创建uv坐标。



现在该挂上matcap纹理了。



在这种情况下,该参数将在着色器界面中创建一个纹理字段,并且如果项目具有名称为“ Matcap_mip”的纹理,则Substance Painter会自动将其拉起。



让我们检查发生了什么。



在此,Matcap的纹理将在新坐标中扩展,并在输出处与基本色相乘。我要注意一个事实,Matcap纹理是通过texture()函数扩展的,而Base color是通过textureSparse()函数扩展的。这是因为通过着色器接口指定的纹理不能为SamplerSparse类型

结果应如下所示:



现在添加一个将两个Matcap混合使用的面膜。为了方便起见,在一个纹理中添加两个Matcap'a,将它们分成多个通道。结果,两个Matcap纹理将分别位于通道R和G中。

结果将是这样的:



让我们开始向着色器添加蒙版。原理类似于添加基色。



用参数中的user0替换basecolor值就足够了。

现在,在像素着色器中获取遮罩值,并混合使用matcap纹理。



此处,在掩模中仅使用R通道,因为它将是黑白的。两个matcap通道使用mix()函数在Unity中类似于lerp 进行混合

让我们更新着色器并在界面中添加自定义通道。为此,请转到“ 窗口” /“视图” /“纹理集设置”,在Channels标题附近的窗口中,单击加号,然后从大列表中选择user0。



该频道可以叫任何您喜欢的名称。

现在,在此通道上绘制,您可以看到两种Matcap纹理是如何混合的。



Unity的着色器还使用了Matcap的法线贴图,该贴图是从高多边形模型中烘焙而来的。让我们尝试在Substance Painter中执行相同的操作。

要对法线使用所有操作,您需要连接适当的



现在,我们连接法线贴图。在Substance Painter中有两种:一种是通过烘烤获得的,另一种是可以绘制的。



根据参数,您可以猜测channel_normal是法线贴图,可以根据该贴图进行绘制,而texture_normal是-烤法线贴图。我还注意到,变量名称texture_normal嵌入在API中,您不能自行决定对其进行命名。

接下来,在像素着色器中解开卡的包装:



然后,我们混合法线贴图和模型顶点上的法线。为此,在上面连接的库中,有一个normalBlend()函数



首先,我们混合两个法线贴图,然后混合法线。尽管混合顺序并不重要。

法线朝相机注视方向的旋转将如下所示:



然后您将无法更改任何内容,一切将保持不变。应该是这样的:



如上所述,在这种情况下,需要进行贴图映射以模拟磨损,例如PBR着色中的粗糙度卡。但是主要的问题是,没有为纹理生成Mip卡中的金字塔,而是从着色器接口传输了纹理,因此glsl的textureLod()方法将不起作用。可以采取另一种方式,通过混合使用Matcap的方法,通过用户通道加载Matcap纹理。但是,纹理的质量将大大降低,并且会出现奇怪的伪像。

另一种解决方案是创建MIP卡金字塔在Adobe Photoshop或其他类似的编辑器中手动进行操作,然后选择MIP级别。金字塔的建造非常简单。有必要从原始纹理的大小开始-在我的情况下是256x256。我们创建一个大小为384x256(384,因为256 + 256/2)的文件,现在我们将原始纹理减小一半,直到其大小为一个像素为止。缩小纹理的所有版本均按升序放置在原始纹理的右侧。结果应该是这样的:



现在,您可以开始编写一个函数,该函数将根据蒙版上每个像素的颜色查找金字塔中每个纹理的坐标。

最简单的方法是将将为每个纹理计算的uv坐标存储在数组中。数组的大小将确定为log2(高度)。我们需要原始的uv,因此我们将它们添加到function参数。要确定要在特定像素上使用哪个数组元素,请向function参数添加级别



现在,为原始纹理计算uv,即裁剪那些多余的128像素的宽度。为此,将x坐标乘以⅔。



要使用金字塔中其余的纹理,您需要找到图案。当我们从纹理创建金字塔时,我们可以注意到,每次纹理都比以前的尺寸缩小一半。也就是说,纹理大小减小多少倍,您可以通过将MIP级别的幂提高2来确定



事实证明,如果选择级别(例如4),则纹理将减少16倍。如uv坐标从0到1确定,然后需要规格化,即1除以纹理减少的次数,例如1除以16。

使用获得的size变量值,可以计算特定MIP级别的坐标



的大小紫外线是在以相同的方式作为纹理的尺寸减小。在x坐标处,纹理始终偏移⅔。 y坐标偏移可以定义为每个级别大小变量的所有值的总和。也就是说,如果值是level = 1,则y坐标中的uv将偏移0个像素,而如果level = 2,则偏移将是纹理高度的一半-128个像素。如果等级= 3,那么移位将显示为128 + 64像素,依此类推。可以使用该循环获得所有移位的总和。



现在,每次迭代,偏移变量将累加起来,并沿y轴将纹理移位所需的像素数。分步算法如下所示:



最后一步是显示一个通道,该通道将在每个像素上选择所需的电平。我们已经做到了,没有什么新鲜的。





要将MIP级别选择为纹理,只需将数组的长度乘以纹理即可。现在,您可以通过刚刚编写的方法连接新的uv坐标。



不要忘记将纹理转换为int类型,因为这现在是数组的索引。
接下来,您需要像以前一样在Substance Painter中添加一个自定义通道。结果应该是这样的:





着色器唯一缺少的是光源和通过按shift键旋转光源的能力。首先,为此,我们需要一个参数参数将通过按shift键产生旋转角度,以及旋转矩阵





我们随机放置光源,并将位置乘以旋转矩阵。



现在,光源将通过按shift键绕y轴旋转,但是到目前为止,这仅仅是存储光源位置的向量。有好东西如何在着色器中实现定向光。我们将专注于他。确定模型的光的方向和照明仍然需要我们来决定。



阴影的颜色和光源的颜色将由参数设置:



根据上面计算的照度对颜色参数进行插值。



结果将是这样:



使用这些参数,您可以通过Substance Painter界面调整阴影的颜色和光源的颜色。



创建和配置预设


准备好着色器后,需要导入Matcap纹理和带有架子设置的着色器。



我们删除所有未使用的通道并添加用户通道:



导出纹理预设看起来与其他任何预设相同,不同之处在于它将使用我们的自定义通道。



我们将为所有设置创建一个模板,以便在创建项目时立即分配所需的着色器并配置所有纹理通道。为此,请转到File / SaveAsTemplate并保存模板。



现在,在创建新项目时,您无需进行任何配置-只需选择所需的模板即可。



你得到了什么


技术美术师可以创建特殊效果,自定义场景并优化渲染过程。我还希望《暴风雨:生存传奇》中的装甲和武器模型完全符合3D艺术家的意图。结果,Substance Painter中的3D模型看起来与游戏引擎中的相同。


Substance Painter中的3D模型具有自定义阴影。


Unity中的3D模型,带有自定义阴影。

希望本文对您有所帮助,并激发您获得新的成就!

All Articles