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

テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

【Unity】uGUIのシェーダーを改造してシェーダーを練習…のススメ

2D GUI(uGUI/NGUI/旧UI) Unity おもしろ演出、演出の強化 Shader Sprite

目次

シェーダーの用意

まずはuGUI用のシェーダーを用意します。

UnityのビルトインシェーダーはUnityのダウンロードページから入手することが出来ます。

Unity - Update

 

このシェーダーはUnityエディタのバージョンによって機能が追加されている事があるので、出来るだけ使用しているUnityエディタと一致するものを使用するのが良さそうです。

f:id:tsubaki_t1:20151029191323j:plain

 

シェーダーをダウンロードしたら、解凍して中にあるUI-Default.shader builtin_shaders-***\DefaultResourcesExtra\UI)をUnityのプロジェクトにインポート(ProjectビューへD&D)します。

f:id:tsubaki_t1:20151029191412p:plain

これからuGUIのシェーダーをカスタマイズして色々してみます。

シェーダーのカスタマイズの準備

シェーダーをカスタマイズする前に、シェーダーの変更前に正常に動作する事の確認およびシェーダーを変更した際にどうなるか即確認できる環境を用意します。

f:id:tsubaki_t1:20151029191724j:plain

まず改造したいシェーダーを複製します。今回の場合、UI-Default.shaderを改造するのでUI-Default.shaderが対象です。

今回は色反転(ネガ)を行うので複製したシェーダーの名前を「UI-Nega」に変更します。

ついでにシェーダーのコードを開き、"UI/Default"を"UI/Nega"に変更します。

f:id:tsubaki_t1:20151029192928j:plain

あとはUI-Negaを設定したマテリアルを用意します。マテリアルを作成してシェーダーを設定すれば良いのですが、UI-Negaを右クリックしてCreate>Materialを選択すればシェーダーが適応されたマテリアルが作成されます。

f:id:tsubaki_t1:20151029192600j:plain

最後にuGUIのImageを作成し、ImageのMaterialに先ほど作成したマテリアルを設定すれば準備完了です。

シェーダーの改造(色を変える)

まずはシェーダーを利用してスプライトの色を反転させます。

シェーダーは長々とありますが、とりあえず色を変更するだけなら見るべきはfragの中身、更に言えば下の図で言う所の96行目付近だけです。

f:id:tsubaki_t1:20151029193513j:plain

まずfragの中身はピクセル(解像度)毎に何度も呼ばれる処理の認識でOKです。
で96行目ですが、テクスチャから色を取り出してUIに色を塗っています。

正確には「tex2D(_MainTex, IN.texcoord)」で_MainTex(uGUIのImageに指定したスプライト)、ピクセルの色(RGBA)を取得し、最後に戻り値として塗る色を返しています。これをピクセル毎に繰り返して全体の色を塗る訳です。GPU氏が。*1

f:id:tsubaki_t1:20151029195351p:plain

なので、fragが戻す色を書き換えたり、色を取り出す場所を少し変えればuGUIで設定するUIにエフェクトを付与する事が出来る訳です。

例えば色を反転させてみます。

half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
color.rgb = float3(1, 1, 1) - color.rgb;

f:id:tsubaki_t1:20151029195855j:plain

ブラーを付けてみます。
ブラーは、ピクセルの上下左右から色を取って薄く塗る感じです。

half4 color = float4(0, 0, 0, 0);
color += tex2D(_MainTex, IN.texcoord) * 0.5f;
color += tex2D(_MainTex, IN.texcoord + float2( 0.008,  0.008)) * 0.125f;
color += tex2D(_MainTex, IN.texcoord + float2( 0.008, -0.008)) * 0.125f;
color += tex2D(_MainTex, IN.texcoord + float2(-0.008, -0.008)) * 0.125f;
color += tex2D(_MainTex, IN.texcoord + float2(-0.008,  0.008)) * 0.125f;
color *= IN.color;

f:id:tsubaki_t1:20151029200739j:plain

無駄にピクセルアートっぽくします。

ピクセルがテクスチャから取り出す座標を「小さくして丸めて戻す」事で、より大雑把にします。

float _Range = 256;
half4 color =  (tex2D(_MainTex, floor(IN.texcoord * _Range) / _Range) + _TextureSampleAdd) * IN.color;

f:id:tsubaki_t1:20151029201904j:plain

これにブラー的な感じで解像度の異なる丸め方をしたところ、何となく味わい深い感じになりました。

f:id:tsubaki_t1:20151029202253j:plain

ちなみにコードにエラーがある場合はピンクになります。またシェーダーやコンソールには何処にエラーがあるのか出るので、そこを治すと良さそうです。

f:id:tsubaki_t1:20151029202641j:plain

パラメータをシェーダーの外から設定する

パラメータをシェーダー内で記述するだけだと、動作のバリエーションを作る際にシェーダーを量産する必要が出ます。それはメモリ的にもファイルサイズ的にもちょっとアレなので、パラメータを外だししてメタデータで設定出来るようにします。

今回はピクセルアートっぽい奴を何とかしてみます。

f:id:tsubaki_t1:20151029203650g:plain

まずは_Rangeをfrag()の変数ではなくシェーダーの変数に変更します。
新しく記述する場所は「CGPROGRAM」から「ENDCG」の間です。

f:id:tsubaki_t1:20151029204242j:plain

次に変数と同じ名前のプロパティを露出させます。

プロパティはシェーダー上の方に書いてあるPropertyスコープの中に書きます

表記のフォーマットは「変数名(ラベル, 型) = 初期値」といった感じです。この変数名が一致してれば、シェーダー内の変数と紐づけられます。
今回は変数名は_Rangeなので一番左は_Range、次に表示名は”Range”で型が小数点なのでFloatとした…そんな感じです。

docs.unity3d.com

f:id:tsubaki_t1:20151029204404j:plain

これでマテリアルを確認すると、Rangeの項目が追加されていますし、このマテリアルの内容を書き換えれば絵の荒さが動的に書き変わります。

f:id:tsubaki_t1:20151029204734j:plain

f:id:tsubaki_t1:20151029204858g:plain

あとはスクリプトからマテリアルの値を変更できるようにすれば、スクリプトからでもエフェクトを動かせたり出来ます。

using UnityEngine;
using System.Collections;

public class BlockRange : MonoBehaviour {

	[SerializeField]
	Material mat;

	[Range(30, 256)]
	public int range;

	void Update()
	{
		mat.SetFloat ("_Range", range);
	}
}

 ちなみにuGUIやSpriteでは無ければCustomEditorを使ってマテリアルの表示内容をもっとスッキリさせたり出来ます。

docs.unity3d.com

その他

シェーダーは本格的にやろうと思ったら3Dに関するそれなりの知識が要求され、かつ色々な単語が飛び交い結構混乱します。

ただ正直な処、その辺り全部を知らなくとも最低限「ピクセルを動かして色々出来る」事を知っていれば結構色々な事が出来たりします。

例えばこんな感じの事とか。(マスクでアルファを減算してるだけ)*2

f:id:tsubaki_t1:20151029211515g:plain

トランジション的な感じ · GitHub

最初から全部覚えるより、まずは絵にエフェクトを付ける所から初めても良いかなーと。色変更ってのは分かりやすいですし、即反映されるのは楽しい(重要)。

 

ちなみに上はImageにしか適応できませんが、もう少し頑張るとこんな感じのエフェクトも出来ちゃったりします。

関連

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

*1:だから昨今のモバイルみたいに高解像度の癖にGPUが貧弱だったり画面全体を埋めるような描画範囲が広い奴で複雑なシェーダーを書くと、パフォーマンスが死ぬ

*2:テクスチャはAlpha From GrayscaleかつAlpha いsTransparencyを設定して、白黒画像をアルファ画像に変換する。白黒画像判定とか面倒くさい