渋谷ほととぎす通信

完全趣味でやってるUnityメモ。説明できないところを説明できるようにするための個人ブログ。昨日の自分より少しでも大きくなれるように。。。 ※所属団体とは一切関係がありません

GLSL SandboxをUnityに移植する方法その1


f:id:esakun:20161214013319p:plain

GLSL Sandboxとは、言わずと知れた有名なWebサービスです。
GLSLで書かれたシェーダを即時実行して全世界にブラウザベースで共有できます。

ソースも公開されているためシェーダ教材としても非常に重宝します。

実際にUnityで試してみたい表現が見つかったりするので移植することもありますが、そもそもUnityはShaderLabという独自のシェーダ言語なので単純にコピペという訳にはいきません。

本記事では移植方法について説明していきます。

シェーダ言語が違う

先ほど説明したとおり、Unityのシェーダは全てShaderLabで記述されていて、記述文法としてはCg/HLSLです。
※厳密にはGLSLでも記述可能ですが、「クロスコンパイルの観点からGLSLはテスト版のみで、プロダクトとしてはCg/HLSLで書いてね」と公式リファレンスにも書かれています。

ということで、GLSL SandboxのGLSLコードをCg/HLSLに変換します。

GLSL Sandbox初期ソースをCg/HLSLに変換

GLSL Sandboxで新規作成したときのGLSLコード

こちらのGLSLをUnityに以下の手順で移植してみます。

  1. 不要ソース削除
  2. フラグメントシェーダ処理をコピペする
  3. Cg/HLSLの文法に書き換える

f:id:esakun:20170503025720g:plain
※これから作る成果物はこんな感じのエフェクトです

1.不要ソース削除

こちらのソースは使用しないので削除します。

#ifdef GL_ES
precision mediump float;
#endif

#extension GL_OES_standard_derivatives : enable

uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;

uniform と付くものは外部(ここではJavaScript)から渡される変数で、timemouseresolutionはそのまま、時間マウス座標解像度が渡ってきます。マウス座標以外はShaderLab内で取得可能なので、ここでは削除します。

2. フラグメントシェーダ処理をコピペする

void main( void ) {
// fragment shader content
}

11行目のmain関数がフラグメントシェーダ処理です。
この中身をUnityのフラグメントシェーダにコピペします。

それ以外の変数や関数、構造体などのコードをフラグメントシェーダより手前にコピペします。このサンプルにはmain関数しか無いので、該当する変数・関数はありませんが、大抵この作業は発生します。

※使用する関数・変数・定数・構造体は、C言語のように使用するコードより先に書いてないとコンパイルエラー

3.Cg/HLSLの文法に書き換える

ただコピペするだけだとコンパイルエラーになるので、ここからCg/HLSL文法に変換していきます。

フラグメントシェーダおさらい

フラグメントシェーダはピクセル毎に処理していくユニットです。最終的にそのピクセルのカラー値を算出することで処理を終えます。GLSLとCg/HLSLでは少し文法が違うのでおさらいです。

■GLSLの場合

GLSLのフラグメントシェーダはgl_FragColor変数にカラー(vec4型)を代入します。

gl_FragColor = vec4( vec3( color, color * 0.5, sin( color + time / 3.0 ) * 0.75 ), 1.0 );

■Cg/HLSLの場合

一方Cg/HLSLのフラグメントシェーダでは、returnステートメントでカラー(fixed4型、float4型、half4)を返却します。

return fixed4( fixed3( color, color * 0.5, sin( color + time / 3.0 ) * 0.75 ), 1.0 );

また適宜以下のようにキーワードを置き換えてコンパイルが通るように修正していきます。

GLSL Cg/HLSL 備考
vec half, fixed, float
_time _Time
mix lerp 線形補間
fract frac 引数の小数部分を返す
mat2 half2x2, fixed2x2, float2x2 2☓2行列
mat3 half3x3, fixed3x3, float3x3 3☓3行列
mat4 half4x4, fixed4x4, float4x4 4☓4行列
resolution _ScreenParams 解像度
fragCoord i.uv*_ScreenParams 頂点シェーダ定義済み関数vert_imgを定義した場合
mouse - C#からシェーダに値を引き渡して実装する

更に詳しくはコチラのサイトをどうぞ

opengl:glsl_hlsl [HYPERでんち]

頂点シェーダ関数をvert_imgに設定する

これはやってもやらなくても良いのですが、UnityCG.cgincに頂点を変換するだけのミニマムな定義済み頂点シェーダ関数が定義されています。

#pragma vertex vert_img

vert_imgを使用することで、頂点シェーダ関数の記述を省くことが出来ます。

fixed4 frag (v2f_img i) : SV_Target

以下のようにvert_img関数がv2f_imgを返却しているため、上記のようにフラグメントシェーダの引数はv2f_img構造体にする必要があります。

UnityCG.cginc内vert_img関数のソースコード

v2f_img vert_img( appdata_img v )
{
    v2f_img o;
    o.pos = UnityObjectToClipPos (v.vertex);
    o.uv = v.texcoord;
    return o;
}

変換終了

最終的にこのようなソースコードになります。

gist.github.com

書いたシェーダの反映方法の例

f:id:esakun:20161213235306p:plain

Quad(板ポリ)を1枚3D上に配置します。

f:id:esakun:20161213235141p:plain

先程のシェーダをMaterialにアタッチして、このQuadにアタッチします。

f:id:esakun:20161213234937p:plain

すると、このようにシェーダを反映できます。

※マウスの値を反映していないので表現に差異が発生していると思います。あくまでプロセスとして参考にしてください。

次回は初期コードではない、もう少しコード量の多いシェーダを移植してみます。

余談:float4, half4, fixed4とは何か?

さっきからよく出てきてますが、どれも同じ4つの要素を持った値ですが、違いは精度です。

精度 備考
float 高精度の32bit浮動小数点 通常プログラミングのfloatと同じような32bit
half 中精度16bit浮動小数点 –60000 から +60000 の範囲で、小数点以下約 3 桁
fixed 低精度11bit固定小数点 –2.0 から +2.0 の範囲で、1/256の精度

int型も使えるようですが、プラットフォームによってはサポートされていないGPUがあるようで注意です。

参考 :
Unity - マニュアル: シェーダーのデータタイプと精度
Unity - マニュアル: シェーダーを書く場合のパフォーマンスのヒント

環境

  • Unity5.5.0p1

あわせてどうぞ