はじめに
以下の公式エントリで Unityの Shader Graph における Custom Function Node の存在を知ったので試してみた記事です。
環境
- Unity 2019.3.1f1
- Shader Graph 7.1.8
デモ
従来の問題点
Shader Graph はグラフィカルにシェーダを構築出来てとても便利ですが、「あ、これコードで書いたら簡単なのにノードで表現すると複雑...」みたいなケースに当たることが良くあると思います。たとえばアルファ付きのテクスチャやスプライトのアウトラインが欲しいケースを考えてみます。テクスチャにアウトラインを付ける場合、UV を上下左右にちょっとずつずらして足し合わせて上げるとつけることが出来ます。ノードで表現すると次のような感じです:
前半の囲った部分がアウトラインの生成部です。コードで書いた場合は UV をずらして足し合わせるのは数行で事足りますが、ノードにすると複雑になってしまいますね。
この要求に対して、C# で自作ノードを生成する方法が以下の記事で紹介されています。
ただ、毎回いちいちスクリプトを作るのは面倒です。そこで、Custom Function Node の出番です。
Custom Function Node
このノードではノード内にコードを記述することが出来ます。出力と入力変数を定義するとノードの入出力にそのスロットが追加され、入力変数を使って出力変数に値を詰めるようにすると動作します。では具体的に先程のアウトラインの例がどうなるか見てみましょう。
ロジックはほとんど Custom Function Node に入ります。ノードの設定は右上の歯車アイコンから行うことが出来、次のように設定しました:
Alpha = 0.0; float r = Outline; Alpha += SAMPLE_TEXTURE2D(Texture, Sampler, UV + float2(+r, 0)).a; Alpha += SAMPLE_TEXTURE2D(Texture, Sampler, UV + float2(-r, 0)).a; Alpha += SAMPLE_TEXTURE2D(Texture, Sampler, UV + float2( 0, +r)).a; Alpha += SAMPLE_TEXTURE2D(Texture, Sampler, UV + float2( 0, -r)).a; Alpha = min(Alpha, 1.0); Alpha -= SAMPLE_TEXTURE2D(Texture, Sampler, UV).a; Alpha = max(Alpha, 0.0);
今回は一つの関数内で簡単に記述できるロジックでしたが、uniform 変数が欲しいときや、複数の関数呼び出しを使って何かするようなより複雑なロジックが必要なときは、Type
を String
から File
に変更すれば、Body
が関数展開されずにより柔軟な記述が可能です。コードは TextAsset
で与えられ、.hlsl で保持する形になります。ドキュメントにも書いてあるように幾つかのルールを守る必要があるのでちょっと大変になります。以下、上記コードをファイルで書いた例です:
#ifndef SAMPLE_OUTLINE_INCLUDED #define SAMPLE_OUTLINE_INCLUDED void SampleOutline_float( Texture2D Texture, float2 UV, SamplerState Sampler, float Outline, out float Alpha) { Alpha = 0.0; float r = Outline; Alpha += SAMPLE_TEXTURE2D(Texture, Sampler, UV + float2(+r, 0)).a; Alpha += SAMPLE_TEXTURE2D(Texture, Sampler, UV + float2(-r, 0)).a; Alpha += SAMPLE_TEXTURE2D(Texture, Sampler, UV + float2( 0, +r)).a; Alpha += SAMPLE_TEXTURE2D(Texture, Sampler, UV + float2( 0, -r)).a; Alpha = min(Alpha, 1.0); Alpha -= SAMPLE_TEXTURE2D(Texture, Sampler, UV).a; Alpha = max(Alpha, 0.0); } #endif
precision をサフィックスにつけたり、返す変数は out
で指定したりといった Shader Graph のルールを守る必要があります。そしてこのファイルを次のように Source
に設定します。
なお、指定できるファイルは TextAsset
ですが .hlsl か .cginc だけのようです。そうしない場合は、variable '_CustomFunction_A36DA959_Alpha_1' used without having been completely initialized
といった文言でエラーが表示されます(おそらく Shader Graph のバグで、本来は .hlsl か .cginc だけだよ、といった文言が出るのが設計っぽいです)。
コードの再利用
複数のグラフにまたがって使う場合は Sub Graph 化をします。不要な変数も省けてシンプルになるのでそういった用途にも使えると思います。
ただ、Sub Graph にするとプレビューが Quad でなく Sphere になってしまうのが不便で...、どなたか修正方法ご存じの方いらっしゃいましたら教えて下さい。
発展
その他の用途としては、冒頭の公式ブログにもかかれていたように、シェーダグラフでは公開されていない変数にアクセスするためのノードを作ったりも出来ます。
より進んだテクニックについては Youtube に幾つかあるので見てみてください:
おわりに
あとは Tags や Blend、デプス出力などが拡充されれば uRaymarching もシェーダグラフ対応ができるかも。。