[Unity] Image Effectでフェードアウト


Unity5から、今まではPro版でしか使えなかった機能が使えるようになっています。
そしてこの「Image Effect」もそのひとつ。

今回はフェードアウトをやってみます。
といってもImage Effectが使えれば全然むずかしくなく、単純に時間に応じて黒色を画面に載せるだけ、という簡単なお仕事です。

ちなみにそれを適用するとこんな感じ↓


今個人で開発中のVRゲームのシーンw

カメラにスクリプトをアタッチする

まず、Image Effectを適用するために、カメラに対してスクリプトをアタッチします。

アタッチするのはこんな感じのシンプルなスクリプトです。

fader.cs
using UnityEngine;
using System.Collections;

public class Fader : MonoBehaviour {

    [SerializeField]
    Material m_Material;

    void OnRenderImage(RenderTexture src, RenderTexture dest) {
        Graphics.Blit(src, dest, m_Material);
    }
}

m_Material はインスペクタからマテリアルを設定する想定です。
(当然、コードだけで取得したりしても構いません)

さて、これで準備が整いました。
最後はフェードエフェクトをかけるためのシェーダを記述します。

ちなみにUnity5から、シェーダの追加時にタイプを選ぶようになっていました。(テンプレートが違う?)
タイプは「Image Effect Shader」を選んで追加します。

そして追加されたシェーダのうち、frag 関数をちょっとだけ修正します。
frag 関数だけ抜粋します)

fixed4 frag (v2f i) : SV_Target
{
    fixed4 col   = tex2D(_MainTex, i.uv);        // 1)
    fixed4 black = 1 - fixed4(_Time.yyy / 2, 1);  // 2)
    col *= black;                               // 3)
    return col;                                 // 4)
}

やっていることは単純で、時間から色を算出しています。

1行ずつ説明すると、

1)
ここはデフォルトのままです。fixed4 col = tex2D(_MainTex, i.uv); の部分はエフェクトを掛けるために準備されたテクスチャ、つまりシーンのレンダリング結果からテクセルを取得しています。
これをそのままリターンすれば画面にはなにも変化が出ないことになります。

2)
_Time はビルトインの変数で時間経過を渡してくれる変数です。
(ちなみに定義済みの変数などは以前こちらにまとめているのでよかったら参考にしてください)

_Time.y は純粋な経過時間です。さらに xzw についても2倍、3倍などの時間経過が格納されています。
表現したい内容に応じて変えるといいでしょう。

そしてその半分の値を3成分に適用し、透明度は 1 としています。

1 - fixed4(_Time.yyy / 2, 1)

そして結果を 1 から引くことで徐々に暗くする、という演出を行っています。

3)
計算結果をテクスチャの色にかけています。
仮に、スタート直後だと 1 - fixed4(_Time.yyy / 2, 1) の部分は 1 - fixed4(0, 0, 0, 1) で結果は 1 と同等(つまり x1 してもなにも変化しない)となるため、結果としてなにもしていないことになります。(そして時間経過で 0 に近づくので、結果としてフェードアウトしているように見える)

4)
あとは計算結果を戻すだけでフェードアウトの完成です。

ほぼ生成時のままですが、シェーダを載せておきます。

Shader "Custom/Fade"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                o.uv = v.uv;
                return o;
            }

            uniform float _Timer;
            sampler2D _MainTex;

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                fixed4 black = 1 - fixed4(_Time.yyy / 2, 1);
                col *= black;
                return col;
            }
            ENDCG
        }
    }
}