デコシノニッキ

ホロレンジャーの戦いの記録

ShaderGraphのCodeFunctionNodeで自作Node

ShaderGraphで自作ノードが作れるAPIが公開されました。

blogs.unity3d.com

ノードはC#を使って書いていきます。

作ってみる

  1. MyCustomShaderGraph.csという名前でスクリプトを生成します。
  2. namespaceにUnityEditor.ShaderGraphを追加します。
  3. CodeFunctionNodeを継承します。
using UnityEngine;
//UnityEditor.ShaderGraphが必要
using UnityEditor.ShaderGraph;
//CodeFunctionNodeを継承する
public class MyCustomNode : CodeFunctionNode
{
 
}

ここまでで、GetFunctionToConvertを実装していませんとエラーがでるので実装します。GetFunctionToConvertはノードで実際に処理するメソッドを渡す部分です。この処理をするためにはRefrectionが必要です。

  1. namespaceにSystem.Reflectionを追加します。

  2. 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を組み合わせて作ったものです。
f:id:haikage1755:20180330120458p:plain:w450

CodeFunctionNodeが使えるようになったことで、複雑でNodeだけでは実現できなかったような処理が作れるようになりました。

[デコシノニッキ]は、Amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、Amazonアソシエイト・プログラムの参加者です。」