【Unity】【シェーダ】色収差のポストエフェクトを実装する

f:id:halya_11:20180619233827p:plain

色収差のポストエフェクトを実装します。

色収差

写真を撮るときには光がカメラのレンズを通過します。
このとき、プリズムに光を通した時のような効果が生まれると波長ごとに焦点距離が変わってきます。

f:id:halya_11:20180619233245p:plain
Wikipedia より

これが原因で起こる色ずれを色収差といいます。

実装の方針

色収差の実装方法はいくつかありそうですが、まずは簡単な実装を試してみます。
今回は下記のサイトを参考にさせていただきました。

【AE】色収差(RGB Split/chromatic aberration)の効果を作成する。その1-標準エフェクト編 | migmotiongraphics

AE用の記事ですが、下記のような手順でシェーダを書けば近しいものができそうです。

  1. 元テクスチャをサンプリング
  2. 1.のB値を元テクスチャを少し拡大してサンプリングした色のB値に置き換える
  3. 2.のR値を元テクスチャをさらに少し拡大してサンプリングした色のR値に置き換える

実装

というわけで実装してみます。

Shader "ChromaticAberration"
{
    Properties{
        _MainTex ("Texture", 2D) = "white" {}
        _Size ("Size", Range(0.0, 1.0)) = 0.025
    }
    SubShader
    {
        Cull Off
        ZTest Always
        ZWrite Off

        Tags { "RenderType"="Opaque" }

        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;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            half _Size;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                half4 col = tex2D(_MainTex, i.uv);

                half2 uvBase = i.uv - 0.5h;
                // R値を拡大したものに置き換える
                half2 uvR = uvBase * (1.0h - _Size * 2.0h) + 0.5h;
                col.r = tex2D(_MainTex, uvR).r;
                // G値を拡大したものに置き換える
                half2 uvG = uvBase * (1.0h - _Size) + 0.5h;
                col.g = tex2D(_MainTex, uvG).g;

                return col;
            }
            ENDCG
        }
    }
}

とてもシンプルです。
_Sizeプロパティにより、R値とG値をサンプリングする際の拡大率を変えられるようにしています。

スクリプトも非常にシンプルです。

using UnityEngine;

public class ChromaticAberration : MonoBehaviour
{
    [SerializeField]
    private Material _material;

    [Range(0.0f, 1.0f), SerializeField]
    private float _size = 0.025f;

    private void OnRenderImage(RenderTexture source, RenderTexture dest)
    {
        if (_material == null) {
            Graphics.Blit(source, dest);
            return;
        }
        _material.SetFloat("_Size", _size);
        Graphics.Blit(source, dest, _material);
    }
}

結果

描画結果はこのようになります。

f:id:halya_11:20180619233827p:plain

色ずれが発生して色収差のような効果がけかられました。
特性上、画面端やコントラストが強い部分で顕著に見られます。

色収差はカメラの物理現象の再現という本来の意味合いのほか、
画面効果に使われたりするので簡単にアニメーションさせてみました。

f:id:halya_11:20180619235328g:plain

参考

【AE】色収差(RGB Split/chromatic aberration)の効果を作成する。その1-標準エフェクト編 | migmotiongraphics

関連

light11.hatenadiary.com