読者です 読者をやめる 読者になる 読者になる

ぼっちプログラマのメモ

UE4とかVRとかについて書いたり書かなかったり。

UE4のCustomノード(カスタムHLSLシェーダ)を使ってみた

はじめに

UE4のマテリアルで使用できる、Customノードを使ってみたときの話。
今回はポストプロセスマテリアル上でのCustomノードなので、
通常のマテリアルで使用する場合は少し挙動が違う可能性があります。ご了承下さい。

Customは、任意量の入力を実行するカスタムHLSLシェーダーコードを書いて、実行結果を出力します。

Unreal Engine | Custom 表現式


今回は以前ノードで実装した、ラプラシアンフィルタを用いたアウトライン描画を
Customノードで実装しました。

Customノードの利点・欠点

利点

  • マテリアルBPでは使えない、ローカル変数やループ処理を使うことができる
  • ネット上に転がっているHLSLシェーダのサンプルを「ある程度」参考にできる

欠点

  • マテリアルBPで実装する時と比べて、処理効率が悪くなる
  • 不安定
    • 特定の操作で100%エディタがフリーズ。対処方法は後述

きっかけ


↑のドット絵フィルターを改良するために、
サイズが可変の平滑化フィルターをかけたい
平滑化(移動平均、ガウシアン)フィルタ 画像処理ソリューション

UE4のマテリアルBPにはローカル変数やループ処理がない。
つまり、フィルタサイズ分のノードを組む必要がある…/(^o^)\
UE4のポストプロセスマテリアルで色々してみた 基本編 - Unreal Engine 4 (UE4) Advent Calendar 2014 - ぼっちプログラマのメモ
(↑で載せた3x3のフィルタでは、9個分の処理をノードで書く必要がある)

\  __  /
_ (m) _  ピコーン
   |ミ|
/ `´  \    Customノードを使って、
  ( ゚∀゚)    シェーダコードを書いちゃえばいいんじゃね!?
 ノヽノ |
  < <

Customノードの使い方

f:id:pafuhana1213:20150215135741j:plain
いつものように、マテリアルBPエディタ上で「右クリック→customと入力」で
「Custom」ノードが出てきます。

f:id:pafuhana1213:20150215140307j:plain
Code   :HLSLシェーダコード
OutputType:返り値(返り値は1つだけです。追加は不可能です)
Inputs  :入力値(関数の引数)


シェーダコードはUE4エディタ上で書くのは辛すぎるので、
hlslに対応したエディターを使って書きます。
(私は、テキストファイルの拡張子を.hlslに変更し、
そのファイルをVisualStudioCommunity2013で開いて、コード書いてました)


試しに、↓こんな感じのコードをCodeに入れてみます。

return float4(uv.x, uv.y, 0, 0);

f:id:pafuhana1213:20150215141545j:plain
ポストプロセスマテリアルだと、結果はこんな感じ
f:id:pafuhana1213:20150215141820j:plain


Customノードにおける注意点

Customノードはhlslシェーダにおける関数を定義する所!

HLSLでググると色々とサンプルコードが見つかります。が、丸っとそのまま使うことは出来ません…
何故なら、Customノードはあくまで関数だからです…
つまり、#include使えませんし、関数を定義することが出来ません…

ただ頑張れば、別のCustomノードを呼び出す事が出来るようです。非推奨ですが

結論
・ Customノードは関数、頑張れば別のCustomノードで呼び出して使える
・ MaterialFunctionは展開される。Customノードからの呼び出しはできない

【UE4】 マテリアルエディタのシェーダーコード生成についてちょっとね - 適当にグラフィックス関係のプログラミングで遊ぶ


CustomノードのCodeを更新する際は、リセット推奨!

別エディタでシェーダコードを書いて、そのコードをCustomノードに設定する場合、
「コードをコピー」→「Codeの入力エリアをクリック」→「全選択」→「ペースト」
としたくなる所ですが、
かなりの確率でエディタがフリーズします…
※UE4のバージョンは、 4.7 Preview7

コードを更新する際は、
Code入力エリア横の黄色矢印をクリックしてリセットしてから
ペーストするようにしましょう

f:id:pafuhana1213:20150215143444j:plain


コメントNG //によるコメントNG

// おまじない
// 何故かこの行を消すと動かない
// TODO:あとで直す(2012/12/24)
return float(0, 0, 0, 0);

通常コードを書く際は、処理内容・目的などを第三者(未来の自分を含む)向けに
伝えるようにコメントを書くのですが…
コメントが含まれたシェーダコードはエラーが出ます!
つらい

追記


/(^o^)\

HLSLシェーダコード内で、UE4の関数を使ってみる

f:id:pafuhana1213:20150215144655j:plain
マテリアルエディタのメニューから見れるHLSLコード内で
使用されている関数に関しては、CustomノードのHLSLシェーダコード上で使える
…と思います。

例えば、通常「SceneTexture:SceneColor」ノードで使用可能な
レンダリング画像中の指定したUV座標におけるRGB値を取得する処理を
シェーダコード上で使用したい、とします。


Customノードの引数にレンダリング画像(Sampler2D)とUV座標(float2)を
追加したい所ですが、「SceneTexture:SceneColor」のColorピンから得られる値は
RGB値なので、残念ながら不可能です。


そこで、まず↑のメニューからHLSLコードを開いて
「SceneTexture」で検索してみます…「SceneTextureLookup」関数が
引っかかると思います。さらに関数内のコードを見ていると、
その中で使用されている「CalcSceneColor(UV)」が
目的の品だという事が分かると思います。流れとしてはこんな感じです。

注意としては、「HLSLコード」で見れるシェーダコード内で
定義されている関数は、Customノード内のシェーダコードでは使用できません。

追記



/(^o^)\

今回実装した処理のコード

f:id:pafuhana1213:20150215151245j:plain

float4 output;
float2 offsetUV;
float1 filter[9] = { -1.0f, -1.0f, -1.0f,-1.0f, 8.0f, -1.0f,- 1.0f, -1.0f, -1.0f };

for (int i = -1; i < 2; i++)
{
	for (int j = -1; j < 2; j++)
	{
		offsetUV = float2(i, j) * invSize;
		
		FScreenSpaceData ScreenSpaceData = GetScreenSpaceData(offsetUV + centerUV, false);
		output += smoothstep(0, depthMax, ScreenSpaceData.GBuffer.Depth) * filter[(i + 1) + (j + 1) * 3];
	}
}

output = clamp(output, float4(0, 0, 0, 0), float4(1, 1, 1, 0));

return float4(CalcSceneColor(centerUV), 0) * (1 - output.x);

さいごに

国内外含めて、Customノードを触っている人があまりいないので、
叩き台的な意味も含めて、記事を書いてみました。
まだ不安定ではありますが、ノードベースでは作り辛い処理を
サクッと書くことが出来て便利だと思います。

追記

らりほま@rarihomaさん、本当にありがとうございました!
…私ひどすぎ

さらに追記


ナンダッテー!