ShaderGraphで自作ノードが作れるAPIが公開されました。
ノードはC#を使って書いていきます。
作ってみる
- MyCustomShaderGraph.csという名前でスクリプトを生成します。
- namespaceにUnityEditor.ShaderGraphを追加します。
- CodeFunctionNodeを継承します。
using UnityEngine; //UnityEditor.ShaderGraphが必要 using UnityEditor.ShaderGraph; //CodeFunctionNodeを継承する public class MyCustomNode : CodeFunctionNode { }
ここまでで、GetFunctionToConvertを実装していませんとエラーがでるので実装します。GetFunctionToConvertはノードで実際に処理するメソッドを渡す部分です。この処理をするためにはRefrectionが必要です。
namespaceにSystem.Reflectionを追加します。
GetFunctionToConvertをoverrideして実装します。Methodの情報を返してやります。
using UnityEngine; using UnityEditor.ShaderGraph; //MethodInfoを返すために必要 using System.Reflection; public class MyCustomNode : CodeFunctionNode { //MethodInfoを渡す関数 protected override MethodInfo GetFunctionToConvert() { //文字列部分: 最終的に処理する関数名 return GetType().GetMethod("MyCustomFunction", BindingFlags.Static | BindingFlags.NonPublic); } }
関数名を文字列で渡したくないという人は、.Net4.6に設定を切り替えるとnameof()というオブジェクト名をStringで返す関数が使えるのでそれでもいいかもしれません。
次にノード名をつけてやります。これはShaderGraphでノードを生成する際に表示される名前です。
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); } }
実際に行う処理部分のMyCustomFunctionを作っていきます。関数はstringで返す必要があります。引数にはNodeで表示されるInとOutのポートにあたり、[Slot] Attributeが必要になります。例ではDynamicDimensionVector 型のAとBという入力、Outという出力にします。
参考
Portで使える他のType
CodeFunctionNode · Unity-Technologies/ShaderGraph Wiki · GitHub
//GetFunctionToConvertから呼び出す関数名 //各引数には[Slot]属性をつける static string MyCustomFunction( [Slot(0, Binding.None)] DynamicDimensionVector A, [Slot(1, Binding.None)] DynamicDimensionVector B, [Slot(2, Binding.None)] out DynamicDimensionVector Out) { }
ここでは例として簡単にOut=A+Bを実行するNodeを作ります。
これは実際に使われているAddノードと同じ中身です。
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; } "; } }
最後にCreate Node Menu から呼び出せるようにTitle Attributeをつけます。Custom->My Custom Node という順でノードが呼び出せるようになります。
[Title("Custom", "My Custom Node")] public class MyCustomNode : CodeFunctionNode {
コードの全体です。
using UnityEngine; using UnityEditor.ShaderGraph; using System.Reflection; [Title("Custom", "My Custom Node")] public class MyCustomNode : CodeFunctionNode { public MyCustomNode() { //ノード名 name = "My Custom Node"; } protected override MethodInfo GetFunctionToConvert() { return GetType().GetMethod("MyCustomFunction", BindingFlags.Static | BindingFlags.NonPublic); } 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; } "; } } }
他にも例では、 3つの入力力最小値を出力するNode
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); } "; } }
与えられたBooleanに基づいて法線ベクトルを反転するNode
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;; } "; } }
自分でも一つ作ってみました。GrayScaleに変換するNodeです。
参考
www.tattichan.work
using UnityEngine; using UnityEditor.ShaderGraph; using System.Reflection; [Title("Custom", "GrayScaleNode")] public class GrayScaleNode : CodeFunctionNode { public GrayScaleNode() { name = "GrayScaleNode"; } protected override MethodInfo GetFunctionToConvert() { return GetType().GetMethod("ConvertToGrayScale", BindingFlags.Static | BindingFlags.NonPublic ); } static string ConvertToGrayScale( [Slot(0, Binding.None)] Color A, [Slot(1, Binding.None)] out Color Out) { Out = Color.white; return @" { Out = A.x * 0.3f + A.y * 0.6f + A.z * 0.1f; } "; } }
上がコードでつくったもの。下がNodeを組み合わせて作ったものです。
CodeFunctionNodeが使えるようになったことで、複雑でNodeだけでは実現できなかったような処理が作れるようになりました。