随着在Unity 2018.1中加入Shader Graph着色器视图,如今在Unity中创建自定义着色器变得更为简单。然而,尽管我们默认提供了不少各式各样的节点Node,这些节点仍不可能满足开发者的所有需求。因此我们开发了一个自定义节点API,提供开发者在使用C#创建新节点时使用。这个API还能帮助开发者根据需求扩展Shader Graph着色器视图。

今天我们将先介绍在Unity 2018.1 beta中一个扩展Shader Graph着色器视图的方法-使用Code Function Node,它是创建自定义节点最简单的方法。下面将介绍如何使用该方法创建一个新节点。

使用Code Function Node创建新节点

首先新创建一个C#脚本,命名为MyCustomNode。为了使用Code Function Node API,我们需要在代码中包含或是添加该类到命名空间UnityEditor.ShaderGraph,然后从基类CodeFunctionNode进行继承。

[C#] 纯文本查看 复制代码using UnityEngine; using UnityEditor.ShaderGraph; public class MyCustomNode : CodeFunctionNode { }

你可能已经注意到,MyCustomNode被高亮为一个错误。如果我们把光标移到该错误信息上,会看到提示我们需要实现一个名为GetFunctionToConvert的继承成员。基类CodeFunctionNode会告诉Shader Graph着色器视图如何处理这个节点,但我们仍需要告诉它结果函数应该是什么。

GetFunctionToConvert方法使用Reflection反射来将一个方法转换为MethodInfo的实例,从而使CodeFunctionNode能在Shader Graph着色器视图中转换和使用。并且让我们能更为直观地写出着色器函数。

如下面代码所示,添加命名空间System.Reflection和重写函数GetFunctionToConvert。要注意MyCustomFunction,它将作为函数名写入着色器中。你还能根据自己正在编写的函数为其命名,只要不以数字开头即可。在本示例中我们将使用MyCustomFunction这个名字。

[C#] 纯文本查看 复制代码using UnityEngine; using UnityEditor.ShaderGraph; using System.Reflection; public class MyCustomNode : CodeFunctionNode { protected override MethodInfo GetFunctionToConvert() { return GetType().GetMethod(MyCustomFunction, BindingFlags.Static BindingFlags.NonPublic); } }

现在我们脚本中的错误都已经解决,我们可以开始编写节点的功能了!

首先我们应进行命名,在类中加入一个没有参数的公有构造函数。在该函数中,将变量名设为包含节点标题的字符串。当该节点在图中出现时,它将显示在节点的标题栏上。本示例中我们将节点命名为My Custom Node。

[C#] 纯文本查看 复制代码using UnityEngine; using UnityEditor.ShaderGraph; using System.Reflection; public class MyCustomNode : CodeFunctionNode { public MyCustomNode() { name = My Custom Node; } protected override MethodInfo GetFunctionToConvert() { return GetType().GetMethod(MyCustomFunction, BindingFlags.Static BindingFlags.NonPublic); } }

接下来,我们将定义节点的函数。如果你熟悉反射,你会注意到方法GetFunctionToConvert会尝试访问这个类中的方法MyCustomFunction。MyCustomFunction将定义着色器函数。

现在让我们创建一个静态方法,返回类型为string,它的名字与方法GetFunctionToConvert中的字符串一致。本示例中,这个名字为MyCustomFunction。在该方法的参数中,我们可以定义节点将拥有哪些端口。这些参数将直接映射到最后着色器函数的参数中。我们需要添加Shader Graph着色器视图中支持类型的参数并对其设定Slot属性。

现在我们要加入二个类型为DynamicDimensionVector的参数,分别命名为A和B,以及一个out参数,类型为DynamicDimensionVector,命名为Out。然后我们为这些参数加入默认Slot属性。每个Slot属性需要一个特别的索引和绑定,我们把它们设为None。
[C#] 纯文本查看 复制代码static string MyCustomFunction( [Slot(0, Binding.None)] DynamicDimensionVector A, [Slot(1, Binding.None)] DynamicDimensionVector B, [Slot(2, Binding.None)] out DynamicDimensionVector Out) { }

在下面方法中,我们会在返回字符串中定义着色器函数的内容。这些内容包含着色器函数和HLSL代码。本示例中定义为Out = A + B;。我们创建好的方法如下所示:

[C#] 纯文本查看 复制代码static string MyCustomFunction( [Slot(0, Binding.None)] DynamicDimensionVector A, [Slot(1, Binding.None)] DynamicDimensionVector B, [Slot(2, Binding.None)] out DynamicDimensionVector Out) { return @ { Out = A + B; } ; } }

这就是Shader Graph着色器视图中加法节点的C#代码。

在完成可用节点前,我们还要做一件事:指定它在Create Node Menu创建节点菜单的出现位置。这一步通过在类上添加Title属性来完成。这会定义一个string数组,它会描述节点在菜单层级列表中出现的位置。这个数组中最后一个字符串定义了节点在Create Node Menu中的名字。

本示例中,我们会把节点称为My Custom Node,并把它放在Custom文件夹中。

[C#] 纯文本查看 复制代码[Title(Custom, My Custom Node)] public class MyCustomNode : CodeFunctionNode {

现在我们的节点就完成了!如果我们返回Unity,编译脚本然后打开Shader Graph着色器视图,我们会看到在Create Node Menu中出现了新节点。

092308fbmmsgcu7dbiev7i.png

然后在Shader Graph着色器视图中创建节点实例。你会看到它拥有和我们在MyCustomFunction类中所定义的相同名字和类型。

092307ibrrbw7soa3xa2lw.png

现在你便可以通过使用不同的接口类型和绑定来创建各种节点。这个方法返回的字符串可以包含任意在Unity常规着色器中有效的HLSL代码。下面的节点会返回三个输入值中的最小值。

[C#] 纯文本查看 复制代码static string Min3( [Slot(0, Binding.None)] DynamicDimensionVector A, [Slot(1, Binding.None)] DynamicDimensionVector B, [Slot(2, Binding.None)] DynamicDimensionVector C, [Slot(3, Binding.None)] out DynamicDimensionVector Out) { return @ { Out = min(min(A, B), C); } ; } }

下面这个节点能根据输入的布尔值反转法线。要注意在本示例中,接口Normal是如何拥有WorldSpaceNormal的绑定的。当没有任何边线(Edge)连接这个接口时,它会默认使用网格的世界空间法线向量。

注意:当使用一个具体输出类型,例如Vector 3时,我们必须在返回着色器函数前对其进行定义。
[C#] 纯文本查看 复制代码static string FlipNormal( [Slot(0, Binding.WorldSpaceNormal)] Vector3 Normal, [Slot(1, Binding.None)] Boolean Predicate, [Slot(2, Binding.None)] out Vector 3 Out) { Out = Vector3.zero; return @ { Out = Predicate == 1 ? -1 * Normal : Normal;; } ; } }

参考文档
Reflection:https://docs.microsoft.com/en-us ... concepts/reflection
CodeFunctionNode API:https://github.com/Unity-Technol ... ki/CodeFunctionNode
Port Binding:https://github.com/Unity-Technol ... /wiki/Port-Bindings

小结
现在你已经准备好在Shader Graph着色器视图中使用Code Function Node来创建节点了吗?当然这只是开始。要想自定义该系统,你还可以在Shader Graph着色器视图中去做更多的事情。
着色器视图锐亚教育

锐亚教育 锐亚科技 unity unity教程