UE4 / Material / カスタムノード

カスタムノードを使うとマテリアルノードでもHLSLコードを書くことができる。複雑な計算を書いたり、既存のコードを利用するときに便利。ノードで利用するため入出力形式の制限がある。
HLSLでreturnした値が出力ピンにでてくるのでそれを他のノードやマテリアルノードにつなげて利用する。

f:id:hat0xAA:20190519152643j:plainf:id:hat0xAA:20190519152705j:plain

基本

マテリアルグラフで、右クリック > カスタム > Custom
でカスタムノードを作成できる。

f:id:hat0xAA:20190519152828j:plain
Code欄にHLSLコードを書く。
Code欄ではShift + Returnで改行できる。

Descriptionの内容がノード名になる。

OutputTypeはfloat1,float2,float3,float4のみ。

Inputsは任意の個数が作れる。それぞれに名前をつけ、その名前でCode中で利用する。作った入力はカスタムノードに入力ピンとして表示される。入力のデータ形式はつないだノードから自動で判別される。


HLSLコードの確認方法

f:id:hat0xAA:20190519154308j:plainf:id:hat0xAA:20190519154318j:plain
ウィンドウ > HLSLコード
でHLSLコード窓がひらき、そこでノードグラフから自動作成したHLSLコードを見ることができる。

f:id:hat0xAA:20190519154627j:plain
ライブビューが有効になっていればノードを変更したらすぐにHLSLコードが更新される。
更新されても見ている場所は保持されるのでコードの変化を確認しやすい。

HLSLコード窓自体は検索機能などはないので、コピーボタンでHLSLコードをコピーして外部のテキストエディタなどで検索したりするほうがよさそう。


カスタムノードから作成されるHLSLコード

f:id:hat0xAA:20190519155610j:plain
f:id:hat0xAA:20190519155632j:plain
グラフ全体はCalcPixelMaterials()関数の中に書かれ、各ノードはこの関数内のLocal0などの変数として記述される。

カスタムノードはCustomExpression0()のような関数として記述される。

たとえば、
f:id:hat0xAA:20190519155758j:plain
このようなカスタムノードだと
f:id:hat0xAA:20190519160057j:plain
このようにHLSLコードが作成される。


外部のHLSLコードの利用 : #include

#include でファイルからHLSLコードを読み込むことができる。
使えるファイルは拡張子が.usfか.ushのテキストファイル。
includeするファイルを更新した場合は、マテリアルの適用か保存を実行すると更新されたファイルの中身が使用される。

特殊なパスとして、"/Engine"はUE4エンジンフォルダの"Engine/Shaders"、"/Project"はプロジェクトフォルダの"Shaders"フォルダを意味する。

#include "/Project/abc.usf"
は、プロジェクトフォルダのShaders/abc.usfファイルの中身がその場所に読み込まれる。という意味になる。


例として、
プロジェクトフォルダのShadersフォルダの中に
f:id:hat0xAA:20190519161533j:plain
この内容のmyshader0.usfファイルを用意して、
f:id:hat0xAA:20190519161500j:plain
カスタムノードでincludeしてポストプロセスのエミッシブに接続してみると
f:id:hat0xAA:20190519161709j:plain
return float3(1,1,0);の結果として画面が黄色になる。


テクスチャオブジェクトの利用 : Texture2DSample()

Texture2DSample()関数とテクスチャオブジェクトを使うと任意のuvでテクスチャにアクセスする処理を書くことができる。
f:id:hat0xAA:20190519162852j:plain
f:id:hat0xAA:20190519162908j:plain
f:id:hat0xAA:20190519163743j:plain
入力にTextureObjectノードを接続するとその変数名+Samplerという名前で自動でSampler変数が作成される。(この画像の場合だとtexobjからtexobjSamplerが自動で追加されている)
Texture2DSample(texobj,texobjSampler,uv);のように2番目の引数にSampler変数を利用する。


関数の定義

書いたHLSLコードは次のように専用関数として自動作成される。
MaterialFloat3 CustomExpression0(FMaterialPixelParameters Parameters)
{
[カスタムノードに書いたHLSL]
}
なのでカスタムノードのコードを下記のように書いておくとHLSLの関数が書ける。

return 0; 
} 

void fn0() {
    //なにかコード

関数などを定義するためのカスタムノードを作り、それを別のカスタムノードで利用する。ということができる。

定義用のカスタムノードはグラフで利用されていないとHLSLコードが作成されない。どこか影響のない場所につなげておく必要がある。
無名の入力ピンに接続しても無効なのでカスタムノードのダミー入力ピンに接続する場合はちゃんと名前をつけておく。


例として、
f:id:hat0xAA:20190519170242j:plain
関数定義用のカスタムノード

f:id:hat0xAA:20190519170252j:plain
関数を呼び出すカスタムノード

f:id:hat0xAA:20190519170313j:plain
HLSLコードを見ると関数が定義され、それを呼び出すコードになっている。