Unity: ImageEffectのスクリプトの書き方

Unity5 からフリー版でもImageEffectが使用できるようなりましたね. というわけで,エフェクトを自分で作成する際の基本をメモしておきます.

ImageEffect による画像処理

ImageEffect ととは,cameraから出力される画像(映像)に対して,shaderを当てて処理するものです.
ImageEffect 使用するには,下記のようなスクリプトを用意し,cameraに追加します.
すると,Inspector 上の当該スクリプトの箇所にshader の設定項目がでてきますので,そこに用いたいshader を指定します.

using UnityEngine;

[ExecuteInEditMode]
public class test : ImageEffect
{
    void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        Graphics.Blit(source, destination, material);
    }
}

ここで,source: 元のテクスチャ,destination: 出力されるテクスチャです.
上記のスクリプトは,単にカメラから得たテクスチャを shader に渡し,その結果を出力・もしくは後段の処理に引き渡すものです.

出力されるテクスチャを保持する場合

例えば,最初のImageEffectで,ラプラシアンを取得し,後段のImageEffectでそのラプラシアンを使用したい場合,予めAssetにRenderTexture を用意し,そのRenderTextureに書き込むようにします.

public class test : ImageEffect
{
    public RenderTexture rt;

    void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        Graphics.Blit(source, rt, material);
    }
}

後段の処理でそのRenderTextureを使用する場合は,そいつを

material.SetTexture("shader上のtexture名", rt);

でshaderに渡してあげます.

シェーダに複数のパスを書いて,特定のパスを実行する

シェーダ内の複数のパスから特定のパスを選んで実行する場合,下記のように第四引数にパスのインデックスを指定します.

material.SetTexture(source, rt, material, 0);

ImageEffectの例

3x3 Gaussian Blur

3x3 ガウシアンブラーの例です.
シェーダ側が完全にべた書きですが,3x3 ならそんなに変わらないだろうということで・・・
考慮する近傍画素数が増えると,x 軸, y 軸を順にブラーかけた方が早いです.
まずは,C#の例から

using UnityEngine;

[ExecuteInEditMode]
public class GaussianBlur : ImageEffectBase
{

    public int _Width = 1920;
    public int _Height = 1080;

    void Start()
    {
        material.SetFloat("_OneWidth", 1.0f / _Width);
        material.SetFloat("_OneHeight", 1.0f / _Height);
    }

    void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        Graphics.Blit(source, destination, material);
    }
}

つづいて,対応するシェーダです.

Shader "Custom/GaussianBulr" 
{
    Properties 
    {
        _MainTex ("", any) = "" {} 
        _OneWidth("Width of one pixel", Float) = 0.00052083333
        _OneHeight("Height of one pixel", Float) = 0.00092592592
    }

    CGINCLUDE
    #include "UnityCG.cginc"
    
    struct v2f {
        float4 pos : SV_POSITION;
        half2 uv : TEXCOORD0;
    };
    
    sampler2D _MainTex;
    float _OneWidth;
    float _OneHeight;
    
    
    v2f vert( appdata_img v ) 
    {
        v2f o; 
        o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
        o.uv = v.texcoord;      
        return o;
    }
    
    half4 frag(v2f i) : SV_Target 
    {
        half4 color = tex2D(_MainTex, i.uv) / 4;
        color += tex2D(_MainTex, half2(i.uv.x, i.uv.y + _OneHeight)) / 8;
        color += tex2D(_MainTex, half2(i.uv.x, i.uv.y - _OneHeight)) / 8;
        color += tex2D(_MainTex, half2(i.uv.x + _OneWidth, i.uv.y)) / 8;
        color += tex2D(_MainTex, half2(i.uv.x - _OneWidth, i.uv.y)) / 8;
        color += tex2D(_MainTex, half2(i.uv.x + _OneWidth, i.uv.y + _OneHeight)) / 16;
        color += tex2D(_MainTex, half2(i.uv.x + _OneWidth, i.uv.y - _OneHeight)) / 16;
        color += tex2D(_MainTex, half2(i.uv.x - _OneWidth, i.uv.y + _OneHeight)) / 16;
        color += tex2D(_MainTex, half2(i.uv.x - _OneWidth, i.uv.y - _OneHeight)) / 16;

        return color;
    }

    
    
    ENDCG
    SubShader 
    {
         Pass {
              ZTest Always Cull Off ZWrite Off

              CGPROGRAM
              #pragma vertex vert
              #pragma fragment frag
              ENDCG
          }
    }
    Fallback off
}