GLSL
HLSL
UE4

GLSL Sandboxで拾ったシェーダーをUE4で画面エフェクトとして活用する

More than 1 year has passed since last update.


GLSL Sandbox

http://glslsandbox.com

GLSL Sandboxとは,インタラクティブなGLSLの実行環境,およびエディターである.

作成した作品は公開することができ,GLSLが分からない人でもその作品を自由に閲覧,編集して楽しむことができる.

ここには,およそ人間の脳みそでは実現不可能な闇のピクセルシェーダーコードが多数眠っており,驚くべき数式が魅せる規則性ともいうべき美しさを視覚化しているとともに,その無限の可能性に胸を熱くするばかりである.

せっかくなのでこんな貴重なリソースは活用しなければ勿体ない.


UE4で動かす

自分のローカル環境でもこのシェーダーコードを活用したい.

ここで,日本国民ならだれでもパソコンにインストールされているUnreal Engine4を使って,この作品たちを実行することにする.

まず,GLSL Sandboxの作品は,その名の示す通り,GLSLで書かれている.

しかし,UE4がサポートするシェーダー言語はHLSLなので,これらのコードはHLSLに書き直さなくてはならない.

とはいっても,それほど困難な作業ではない.

基本的なデータ構造の対応表はネットにいくらでも転がっている.

さて,実現方法はいろいろあるが,スクリーン全体に対し実行されるピクセルシェーダーと考えると,もっとも近いUE4上の実行環境は,やはりポストプロセスマテリアルであろう.

すると,とるべき戦略は単純で,最終的なマテリアルへの出力「エミシッブカラー」に,GLSLでいうgl_FragColorに代入している値を入力するだけである.

まずはマテリアルを新規作成し,マテリアル属性を「ポストプロセス」にする.

次に,もっとも大事な「Customノード」を追加する.これが任意のHLSLコードを実行するノードである.

引数は好きな数だけ設定することができる.

ue4.png

最も重要なのは,GLSL上で

gl_FragColor = XX;

となっているコードを見つけ出し,それを

return XX;

に書き換えることである.これで万事うまくいく.

ただし,GLSL Sandboxでは,Uniform変数として,「time」や「mousePosition」,「resolution」等が定義され使われている.timeは上記の画像のように,Timeノードで取得することができるが,mousePositionとresolutionは動的に取得しなければならない.となると,他のBPとの連携が不可欠なので,今回は説明は総略することにする.そのような動的マテリアルインスタンスについての解説はこの辺りが詳しい. http://monsho.blog63.fc2.com/blog-entry-129.html


コード置換におけるTips

注意すべき点として,gl_FragCoord変数は,値域(vec2(0, 0) ~ vec2(resolution.x, resolution.y))として定義される値なので,マテリアルグラフ上のTexCoordとは互換ではない.

TexCoordは値域(float2(0, 0) ~ float2(1.0, 1.0))なので,工夫する必要がある.

ただし,多くの場合GLSL Sandboxでは「gl_FragCoord.xy/resolution.xy」としてスクリーン座標をノーマライズしているコードが基本なので,あまりその辺りで困ることはないと思われる.

TexCoord[0]とgl_FragCoord.xy/resolution.xyは互換であると覚えておこう.

上記の画像でもTexCoordを使っている,(シェーダー内でParameters.ScreesPositionという,FPixelMaterialParametesインスタンスにアクセスすることでスクリーン座標を取得することも可能だが,わかりやすさのためグラフで外からデータを与えている)

また,カスタムノードはシェーダーの一つの関数を示すノードであるので,main以外に関数を定義しているGLSLコードを持ってくる場合は,シェーダー関数ハッキングテクを使う必要がある.その解説はこちら.http://monsho.blog63.fc2.com/blog-entry-194.html


実行例

さて,今回はこちらのGLSLコードを使わせていただいた.

http://glslsandbox.com/e#44680.0

glsl.png

すごい.水だ.綺麗な色してるだろ....? これ,プログラムだけで表現してるんだぜ.

こちらを以下のように置換する.


float2 sp = texcoord;
float2 p = sp * 15.0 - float2(20.0, 20.0);
float2 i = p;
float c = 1.0; // brightness; larger -> darker
float inten = 0.025; // brightness; larger -> brighter
float speed = 1.5; // larger -> slower
float speed2 = 3.0; // larger -> slower
float freq = 0.8; // ripples
float xflow = 1.5; // flow speed in x direction
float yflow = 0.0; // flow speed in y direction
int MAX_ITER = 9;

for (int n = 0; n < MAX_ITER; n++) {
float t = time * (1.0 - (3.0 / (float(n) + speed)));
i = p + float2(cos(t - i.x * freq) + sin(t + i.y * freq) + (time * xflow), sin(t - i.y * freq) + cos(t + i.x * freq) + (time * yflow));
c += 1.0 / length(float2(p.x / (sin(i.x + t * speed2) / inten), p.y / (cos(i.y + t * speed2) / inten)));
}

c /= float(MAX_ITER);
c = 1.5 - sqrt(c);
float cpow4 = c * c * c * c;
return float4(float3(cpow4, cpow4, cpow4), 0.0) + float4(0.0, 0.4, 0.55, 1.0);

これをカスタムノードのCodeに挿入する.

あとは,ポストプロセスマテリアルに適用するだけだ.

今回はこれを使って,水中表現をしてみよう.

ue4.png

3Dmodel: Racing Miku RSK version

池がある.

ここで,PhisicalVolume等を使って,池に入ったときにポストプロセスマテリアルを適用するようにすると...

ue4.png

水の中に入った感じになりました.(本当はもっと画面のエフェクトがゆらゆら揺れていて水っぽくなっている)

ただし,光の屈折等は一切ないので,実用レベルではないです.

まあ完全に遊び用ですね.ポストプロセスマテリアルは重いし,GLSL Sandboxはアート的な側面が強く,パフォーマンスはくそくらえみたいなコードばかりなので.

でも面白いのでいろいろ試したらいかがだろうか.