查看原文
其他

Shader Graph着色器视图自定义节点API:Code Function Node

Unity Unity官方平台 2018-11-15

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


明天晚上8点,新一期的Unity技术直播课程将详解使用Shader Graph着色器视图快速创建炫酷特效。今天我们将先介绍在Unity 2018.1 beta中一个扩展Shader Graph着色器视图的方法-使用Code Function Node,它是创建自定义节点最简单的方法。下面将介绍如何使用该方法创建一个新节点。

 

使用Code Function Node创建新节点

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

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这个名字。

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。 

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。

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;。我们创建好的方法如下所示: 

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文件夹中。

[Title("Custom", "My Custom Node")]

public class MyCustomNode : CodeFunctionNode

{


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



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

 

 

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

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时,我们必须在返回着色器函数前对其进行定义。

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/dotnet/csharp/programming-guide/concepts/reflection


  • CodeFunctionNode API

    https://github.com/Unity-Technologies/ShaderGraph/wiki/CodeFunctionNode

     

  • Port Binding

    https://github.com/Unity-Technologies/ShaderGraph/wiki/Port-Bindings


小结

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


明天晚上8点,Unity技术经理成亮将为开发者们带来更多Shader Graph着色器视图的功能讲解,对着色器开发感兴趣的开发或美术人员一定不能错过,带上你的问题和讲师互动吧! 


推荐阅读

官方活动

直播预告 | 使用Shader Graph着色器视图快速创建炫酷特效

直播时间:4月11日 20:00-21:00

活动网址:https://connect.unity.com/events/unitychina-shadergraph


赢取15万美元奖金|环球影业经典IP游戏开发大奖赛

活动信息:截至至4月20日 16:00

活动网址:https://connect.unity.com/challenges/universal


Unite Beijing 2018 及 Training Day

活动信息:5月11-13日 北京国家会议中心

售票官网: http://unite2018.csdn.net/  或者直接扫描下图二维码进行购票!



点击“阅读原文”访问Unity中文官方论坛!

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存