《Unity着色器圣经》9.0.7 | 用户自定义函数

目录索引

译文

要使用自定义函数节点,我们必须了解计算机图形学的基本概念以及如何用 HLSL 编写函数。自定义函数允许我们创建自己的函数,并在 Shader Graph 中以节点的形式使用它们。我们可以创建自定义光照、复杂操作或优化流程。

我们可以通过两种方式使用自定义函数节点:通过以“.hlsl”为拓展名的文件创建或直接在节点里输入函数。不过,笔者更建议大家用第一种方法,因为这样我们就可以在其他项目中进行复用。

图片[1]-《Unity着色器圣经》9.0.7 | 用户自定义函数-软件开发学习笔记
Fig. 9.0.7a

默认情况下,自定义函数节点没有输入与输出。我们需要在图检查器(Graph Inspector)界面中为函数配置必要的输入和输出。这一步骤可能因 Unity 的版本而异,比如 2019.3 版本没有图检查器,我们需要点击节点右上方的配置按钮(齿轮符号的按钮)进行配置。

我们将分析一个简单函数的实现来理解这一概念:

void Add_float(in float A, in float B, out float Out)
{
    Out = A + B;
}

在上面的代码中,“Add”函数返回两个输入数字的和(Out,即输出)。第一个输入对应于 float 类型的变量“A”,第二个是“B”。因此,在自定义函数节点中,我们应添加两个输入和一个输出。

图片[2]-《Unity着色器圣经》9.0.7 | 用户自定义函数-软件开发学习笔记
Fig. 9.0.7b

同样的类比适用于向量和其他数据类型,也就是说,如果“A”是一个三维向量,那么我们就在节点配置中添加一个名为 A 的三维向量作为输入。

接下来,我们将通过自定义函数实现 _WorldSpaceLightPos 这一变量的行为,我们最初在第 7.0.3 小节学习漫反射时曾提到过该变量。让我们创建一个 Unlit Shader Graph 类型的新着色器,并将其命名为 USB_custom_function

我们要执行的第一项任务是将自定义函数节点引入节点编辑区域。如上所述,我们需要一个扩展名为“.hlsl”的脚本,为此我们可以简单地创建一个 txt 文本文件,然后将后缀名改为“.hlsl”,或者直接在使用 Unity 的编辑器中创建脚本。

我们将“.hlsl”文件命名为“CustomLight”,在其中加入如下代码:

// CustomLight.hlsl
void CustomLight_float(out half3 direction)
{
    #ifdef SHADERGRAPH_PREVIEW
        direction = half3(0, 1, 0);
    #else
        #if defined(UNIVERSAL_LIGHTING_INCLUDED)
            Light mainLight = GetMainLight();
            direction = mainLight.direction;
        #endif
    #endif
}

在上面的代码中,我们可以推断出,如果在 Shader Graph 中开启了 SHADERGRAPH_PREVIEW,我们将在空间的 Y 轴上以九十度投射灯光。如果未开启且定义了 URP,那么输出的“方向(direction)”将等于主光源的方向,也就是场景中的平行光的方向。

由于 CustomLight 函数没有输入,因此只需在节点配置中添加一个类型为“half3”的三维向量作为输出即可,“half3”代表节点的精度等于 16 位。默认情况下,精度会被配置为“继承(inherit)”。

接下来,我们需要将“.hlsl”文件拖入或选中位于图形检查器节点设置(Graph Inspector – Node Settings)窗口中源(Source)框内的“.hlsl”文件。在名称(Name)选框中,我们必须使用函数的准确名称;换句话说,在本例中就是 CustomLight,否则可能会产生编译错误。

图片[3]-《Unity着色器圣经》9.0.7 | 用户自定义函数-软件开发学习笔记
Fig. 9.0.7c. 自定义函数是绿色的是由于direction向量的值

由于内部变量 _WorldSpaceLightPos 可以获取光源的位置,因此我们可以使用我们的节点复现相同的行为;实际上,mainLight.direction 等同于内部变量 _MainLightPosition,我们可以查看项目的“Lighting.hlsl”文件来验证这一点。

图片[4]-《Unity着色器圣经》9.0.7 | 用户自定义函数-软件开发学习笔记
Fig. 9.0.7d. D = max(0, n · l)

图 9.0.7d 所示,我们可以通过 Shader Graph 中的节点实现我们在第 7.0.3 小节中所学习的漫反射公式。


原文对照

It is essential to know basic concepts about Computer Graphics and how to write functions in HLSL to work with this node. As its name mentions, Custom Functions allows us to create our own functions and work with them in Shader Graph. We can use it to create custom lighting, complex operations, or optimize long processes.

Given its structure, we can work with this node in two ways: from files with “.hlsl” extension (type file) or by writing functions directly in its body (type string), however, it is advisable to use the first option because we can reuse the files later in other projects.

图片[1]-《Unity着色器圣经》9.0.7 | 用户自定义函数-软件开发学习笔记
Fig. 9.0.7a

By default, Custom Functions has no input or output. Initially, we must go to the Graph Inspector and add the necessary properties for the function. This process may be different depending on the Unity version, e.g., since the 2019.3 version of the software does not have a Graph Inspector, we must press the configuration button (gear button) at the top right of the node to perform the same action.

We will analyze the implementation of a simple function to understand the concept:

void Add_float(in float A, in float B, out float Out)
{
    Out = A + B;
}

In the inner exercise, the “Add” function simply returns a scalar value (Out) equal to the sum of two numbers. The first input we can find corresponds to the floating type variable “A,” and the second is “B.” Therefore, in our Custom Function node, we should add two inputs and an output corresponding to scalar values.

图片[2]-《Unity着色器圣经》9.0.7 | 用户自定义函数-软件开发学习笔记
Fig. 9.0.7b

The same analogy holds true for vectors and other data types, i.e., if “A” were a threedimensional vector, then such a vector would have to be added as input in the node configuration.

Next, we will recreate the behavior of the _WorldSpaceLightPos variable that we initially mentioned in section 7.0.3 when we talked about diffuse reflection, which we will use for the same purpose. Let’s start by creating a new shader of type “Unlit Shader Graph,” which we will call USB_custom_function.

The first task we will perform is to bring a Custom Function node to the node area. As mentioned above, we will need a script with the “.hlsl” extension. To generate it, we can simply create a file with the “.txt” extension and replace it with “.hlsl,” or create a script directly from the editor we are working with Unity.

We will call the “.hlsl” file “CustomLight” and start adding our function as follows:

// CustomLight.hlsl
void CustomLight_float(out half3 direction)
{
    #ifdef SHADERGRAPH_PREVIEW
        direction = half3(0, 1, 0);
    #else
        #if defined(UNIVERSAL_LIGHTING_INCLUDED)
            Light mainLight = GetMainLight();
            direction = mainLight.direction;
        #endif
    #endif
}

In the previous code block, we can deduce that if the preview in Shader Graph is enabled (SHADERGRAPH_PREVIEW), we will project lighting in ninety degrees on the Y-axis of the space. Otherwise, if Universal RP has been defined, then the output “direction” will be equal to the direction of the main light, that is, to the directional light we have in the scene.

Unlike the previous case, the CustomLight function has no inputs; therefore, only a threedimensional vector will have to be added as output later in the node configuration. On the other hand, it’s worth noting that such output is of type “half3.” Consequently, the accuracy of the node will be equal to a 16-bit value because, by default, it is configured as “inherit.”

Next, we must make sure to drag or select the “.hlsl” file in the “Source” box located in the Graph Inspector Node Settings window. In the “Name” box, we must use the exact name of the function; in other words, CustomLight, otherwise it could generate compilation errors.

图片[3]-《Unity着色器圣经》9.0.7 | 用户自定义函数-软件开发学习笔记
Fig. 9.0.7c. The Custom Function node is green due to the values of the direction vector

Since the enclosed variable _WorldSpaceLightPos owns the light position, we can use our node to replicate the same behavior; in fact, the operation “mainLight.direction” is equal to the enclosed variable _MainLightPosition. We can check this by going to our project’s “Lighting.hlsl” file.

图片[4]-《Unity着色器圣经》9.0.7 | 用户自定义函数-软件开发学习笔记
Fig. 9.0.7d. D = max(0, n · l)

As shown in Figure 9.0.7d, following the diffusion scheme we saw in section 7.0.3, we can generate the same behavior through nodes in the Shader Graph.

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容