はじめに
こんばんは、代表の堂前です!
前回に引き続き、UnityのUIシェーダについて取り上げ、その高速化(主にGPU)を測ってみます。
普段意識することがあまりないシェーダかと思いますが、一緒に挙動を理解していきましょう。
※検証したのはMacのUnity5.4.1系になります。
※本稿は推測を含むため、その点は留意下さい。
ビルトインシェーダの確認
Unityの内部シェーダ、俗に言うビルトインシェーダですが、ダウンロードが可能になっています。
今回は5.4.1系での確認ですので、該当のバージョンのビルトインシェーダをダウンロードしてみましょう。
uGUIですが、ImageやText等を配置して表示するかと思いますが、それぞれにおいてマテリアル(つまりはシェーダ)を設定できるようになっています。
が、ここが無指定(None)の場合は内部のuGUI向けの標準シェーダを使うようになっています。
そのシェーダがビルトインシェーダの「UI-Default.shader」となります。
(厳密に言えばTextは「UI-DefaultFont.shader」ですが、中身がほぼほぼ変わらないので説明を割愛します。)
uGUIではこのシェーダを元に改造し、独自のシェーダとしてImage等に利用することが可能です。
よって、高速化してそれを利用することが可能です。
今回は高速化の観点の記事になります。
ですので、その大きな鍵を握るフラグメントシェーダに着目していきます。
以下、転載します。
fixed4 frag(v2f IN) : SV_Target
{
half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
#ifdef UNITY_UI_ALPHACLIP
clip (color.a - 0.001);
#endif
return color;
}
パッと見で気になるのは以下の3点です。
・_TextureSampleAddはどういうことに利用されるか?
・UnityGet2DClippingは何を行っているか?
・UNITY_UI_ALPHACLIPでのclipは何のためにあるか?
以下、推測も多分に含みますが、それぞれについて見解を説明します。
_TextureSampleAddの用途
フラグメントシェーダでテクスチャカラーに毎回加算されている「_TextureSampleAdd」ですが、非常に気になるところです。
用途を調査しましたが、結論から言えばよく分かりませんでした。
ただFrame Debuggerで幾つか見る限り、その値は毎回(0,0,0,0)になっていました。
あと過去のビルトインシェーダも探ってみたところ、5.2.0からこの_TextureSampleAddが含まれていました。
しかし更新履歴ではそれっぽいものは見当たらず・・・。
5.2.0以前ではないことと、ほぼ確実に(0,0,0,0)が来るので削っても問題なさそうですが、用途不明で怖いのでそのままにしておくのが無難かもしれません。
(※2016/10/8 13:15追記)
_TextureSampleAddについてenu様より情報を頂きました。
_TextureSampleAddは確かAlpha8モードのテクスチャにすると1が入るんじゃなかったっけかな。おそらく黒いシルエットを白いシルエットにするための値だと思う。color指定は乗算で利くので黒より白のほうが都合が良い。 https://t.co/wEZEAvCeDK
— enu (@_enu) 2016年10月8日
Alpha8では1が入るということなので、軽く検証を行いました。
まず、以下のようなAlpha8テクスチャを用意します。
これをImageに貼り付け、_TextureSampleAddの利用の有無で比較しました。
そうしたところ下の様になりました。
通常状態 | _TextureSampleAddを使わない |
調査したところ、Alpha8ではシェーダではRGBは0とみなされる為、_TextureSampleAddを(1,1,1,0)として加算し、白色にしているという状態でした。
Alpha8を使わなければ_TextureSampleAddは削除しても〜と思われるかもですが、Textは内部でAlpha8テクスチャを参照している様なので、同じシェーダを利用するとTextも暗くなってしまいます!
よって_TextureSampleAddをどうにかしたい時は「Alpha8の利用の有無」「Textでは更に別シェーダを用意する」などの考慮が必要そうです。
UnityGet2DClippingの用途
次に「UnityGet2DClipping」関数です。こちらも5.2.0から含まれています。
関数の処理自体は以下の様になっています。
inline float UnityGet2DClipping (in float2 position, in float4 clipRect)
{
float2 inside = step(clipRect.xy, position.xy) * step(position.xy, clipRect.zw);
return inside.x * inside.y;
}
指定の矩形範囲(clipRect)の中なら1.0を、その範囲外なら0.0を返す関数です。
それがアルファに乗算してあります。
これは5.2.0から追加された「Rect Mask 2D」のためのものです。
(恐らくですが)Rect Mask 2D以外ではこの関数を通すことは無いと考えます。
同機能を使わない場合、削除することをオススメします。
UNITY_UI_ALPHACLIPの用途
フラグメントシェーダで「#if UNITY_UI_ALPHACLIP」で括られている箇所があります。
そしてそれが有効な時はclip処理が走っています。
#ifdef UNITY_UI_ALPHACLIP
clip (color.a - 0.001);
#endif
UNITY_UI_ALPHACLIPはシェーダキーワードなのですが、どんな時に有効になるかというと、前回も紹介したマスク処理でステンシルに書き込む時のみ有効になるようです。
(マスク処理で画像の形にくり抜きたい為に行っています。)
clip命令も(内部でifなどが動作することもあるので)なるべく削除したいのですが、逆に言えばマスク処理を行わなければUNITY_UI_ALPHACLIPは有効になりません!
そのことを理解して敢えて残すのも良いですし、uGUIのマスク処理を使わないと決めた上で削除しても良いでしょう。
現行のマスク処理は負荷がかかっていることを把握しておくと良いと思います。
※お知らせ
株式会社ロジカルビートでは、一緒に働いてくれる仲間を募集しています!
興味を持たれた方は採用ページを是非ご覧下さい!
【免責事項】
本サイトでの情報を利用することによる損害等に対し、株式会社ロジカルビートは一切の責任を負いません。