tetu式

ゲームと音楽・作曲の自己満足と悩みどころの多いプログラムのブログ。

Unity スプライト(SpriteRenderer)にカラーフィルターを掛けてみる

最近のゲームでほとんど見かけないことはないカラーフィルター。

カラーフィルターってなんぞ?という人はそもそもこの記事に辿り着かないとは思いますが一応説明。

実際のゲームを例にすると、キャラクターが何らかの効果で炎に包まれたりした時に全体的にキャラクターの色が赤みがかったりしますよね?これのことです。
他には回復した時に緑色になったり、感電した時に黄色や青白くなったりとか。

絵を描く時なんかにも使ったりしますね。
あまり専門ではないですが、キャラの顔に影をつけたりする時は新しいレイヤーを顔レイヤーの上につくって影にしたい部分を黒く塗り、アルファ値を落とすみたいなことをすると思います。


3Dゲームに限らず2Dゲームでもよくある手法です。




さて、UnityにはSpriteRendererというクラスがあり、画像ファイルを表示する時とかに使ったりするかと思います。
SpriteRendererを持つオブジェクトを選択するとInspectorのSprite Renderer項目内にColorというものがあります。

一度もこの部分を触ったことがない状態なら白いバーとスポイトが表示されてます。
この白いバーの部分をクリックするとペイントソフトで見かけるような色を選択するウィンドウが出てきますね。

この部分の色を変えるとカラーフィルターを掛けることができますが・・・何かイメージと違う。
Colorウィンドウの下の方にSlidersという項目があり、ここでRGBA値を1~255の間で調整することができます。
こっちの方で一色ずつ色を上げ下げしてみると分かると思いますが・・・

元のスプライトに対してR値をいくら、G値をいくら、B値をいくら、という感じの設定になっています。

なのでたとえばG255べた塗りの画像があった場合、元々R値とB値は全く使われてないのでこの部分を上げ下げしても元の絵は全く色が変わりません。
逆にG値を0にしてR値とB値を上げ下げしても赤くなったり青くなったりはしません。

まとめると初期状態では元の画像から色を減らす(黒に近づける)方向でしかフィルターを掛けることができません。

ゲームの無敵状態みたいな白く明滅するようなフィルターはこのままじゃできないのです・・・


で、調べた結果の解決策。Shaderを使う方法。
こちらこちらの記事を参考にしました。


この記事の内容に入る前にちょっとした下準備。
何も設定してないなら、Inspector>Sprite Renderer>Colorの下にあるMaterialは Sprites-Default というものになっています。

このMaterialは中身のShaderの編集ができないみたいなので、Shaderを変更するにあたって新たにMaterialを自作する必要があります。

ゲームプレイ途中でMaterialを変更する予定がありそうな人はAssetsフォルダのResources内、そうでない人は適当なところにMaterialフォルダを作り、その中で CustomMaterial という名前のMaterialを作成。名前は別になんでもいいです。
右クリック>Create>Materialで作成できます。


そしたら参考記事の内容へ。
こちらでbuilt-in shadersをダウンロードしてきます。
今使っているUnityのバージョンに合わせた物を落としましょう。
Unityのバージョンチェックは メニューバー>Help>About Unity... から。
そんなに容量も大きくないのでぱぱっと解凍。

解凍したら DefaultResources フォルダ内の一番下にある Sprites-Default.shader を先ほど作ったUnityのエディタ上で作ったMaterialフォルダにドラッグ&ドロップでコピー。
エクスプローラでもUnity上でやってもOKです。
コピーしたらファイル名を Sprites-Custom.shader と変えておきましょう。

改名したらUnityでこのファイルをダブルクリックし、 MonoDevelop 上で編集をします。
ソースコード拡張子じゃないからってテキストエディタとかで編集なんかしないように。

そしたらまず1行目。

Shader "Sprites/Default"
↓ 書き換え
Shader "Sprites/Custom"

この部分がMaterialのShaderを変更する時に使うパスみたいなものになります。
次に66行目。

fixed4 frag(v2f IN) : COLOR
{
	return tex2D(_MainTex, IN.texcoord) * IN.color;
}
↓ 書き換え
fixed4 frag(v2f IN) : COLOR
{
	fixed4 emi = tex2D (_MainTex, IN.texcoord) * _Color;
	emi *= IN.color;
	emi.rgb = emi.rgb*2+max(fixed3(0,0,0),IN.color.rgb-0.5)*2;
	return emi;
}

何やってるかはよくわかりませんが・・・Sprite RendererのColorに使う色の範囲をここで調整してるみたいです。


これで一通り編集は完了。
後はフィルターをかけたいオブジェクトの Inspector から Sprite Renderer の Material を CustomMaterial にし、その Material の Shader を sprites>Custom にします。

これを適用すると今までRGB値が255,255,255で元の絵をそのまま表示していた物が127,127,127になります。
0~255の間で色を減らすだけだったものを0~127にし、色を増やす方向を128~255に割り当てたような感じです。

先ほどのG255のべた塗り画像を例にした時・・・

赤にしたい場合:R255,G0,B0~127
黄にしたい場合:R255,G128~255,B0~127
白にしたい場合:R255,G128~255,B255

こんな感じ。元の絵の各色の値がもともと最大値、最低値だった場合はスライダーの半分から向こうは最大値、最高値と同じになります。


白明滅ループのカラーフィルターをかけたい時は

const float EARLINESS = 0.01f;
int direction = 1;
float col = 0.5f;

void Update()
{
    col += EARLINESS * direction;
    if(col >= 1.0f)
    {
        direction = -1;
    }
    if(col <= 0.5f)
    {
        direction = 1;
    }
    this.GetComponent<SpriteRenderer>().color = new Color(col,col,col,1);
}

こんな感じ。なんの捻りもない直球コード。
変数は0.5fと1の間を行ったり来たりするようにすればOKです。
ソース上だと0~255じゃなくて0~1になる点に注意。