Unity
Shader
uGUI

UGUIとシェーダでカラーフィルムっぽい表現

こんなやつ

3つのCubeが並んだ画面にUGUIのImageで色を上乗せしています。
カラーフィルムっぽい表現にはシェーダを利用しており、このImageにシェーダを適用したマテリアルを追加しています。
スクリーンショット 2018-02-21 12.06.33.png
スクリーンショット 2018-02-21 12.24.42.png

Imageにスプライトを適用してあげるとこんな感じ
スクリーンショット 2018-02-21 12.22.42.png

環境

Unity2017.3.1f1
iOSでも動作確認しています。

やってること

Spriteの背景となっている画に対してコントラストの調整とSprite画像の乗算を行って表示しています。

背景の画を取ってくる

シェーダで背景の画を取ってくる場合は、GrabPassをSubShader内に記述し、CGPROGRAM~ENDCG内に_GrabTextureを宣言します。

SubShader
{
  GrabPass {}

  Pass
  {
    CGPROGRAM
    ...
    sampler2D _GrabTexture;
    ...
    ENDCG
  }
}

この様な感じで使うのですが、TsumikiTechTimes様のこちらの記事で詳しく書かれていますので詳細は割愛します。

コントラストを調整する

GrabPassで取得した背景の画にコントラスト調整の計算を加えてあげるだけです。
おもちゃラボ様のこちらの記事を参考にさせて頂きました。

Properties 
{
...
  _Contrast ("Contrast", Range(1, 20)) = 10
}

fixed4 frag(v2f i) : SV_Target
{
  ...
  float2 grabUV = float2(i.screenPos.xy / i.screenPos.w);
  grabUV.y = grabUV.y * -1 + 1;

  fixed4 grab = tex2D(_GrabTexture, grabUV);

  half4 cont = 1 / (1 + exp(-_Contrast * (grab - 0.5)));
  ...
}

UI-Defaultシェーダを改造する

あとはこれらの処理をUI-Defaultシェーダに書き加えてあげるだけで完成です。

Shader "UI/Film" 
{
    Properties 
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)
        _Contrast ("Contrast", Range(1, 20)) = 10

        _StencilComp ("Stencil Comparison", Float) = 8
        _Stencil ("Stencil ID", Float) = 0
        _StencilOp ("Stencil Operation", Float) = 0
        _StencilWriteMask ("Stencil Write Mask", Float) = 255
        _StencilReadMask ("Stencil Read Mask", Float) = 255

        _ColorMask ("Color Mask", Float) = 15

        [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
    }

    CGINCLUDE
    #include "UnityCG.cginc"
    #include "UnityUI.cginc"

    #pragma multi_compile __ UNITY_UI_CLIP_RECT
    #pragma multi_compile __ UNITY_UI_ALPHACLIP

    sampler2D _GrabTexture;

    sampler2D _MainTex;
    fixed4 _Color;
    fixed4 _TextureSampleAdd;
    float4 _ClipRect;

    float _Contrast;

    struct appdata_t
    {
        float4 vertex   : POSITION;
        float4 color    : COLOR;
        float2 texcoord : TEXCOORD0;
        UNITY_VERTEX_INPUT_INSTANCE_ID
    };

    struct v2f
    {
        float4 vertex   : SV_POSITION;
        fixed4 color    : COLOR;
        float2 texcoord  : TEXCOORD0;
        float4 worldPosition : TEXCOORD1;
        float4 screenPos : TEXCOORD2;
        UNITY_VERTEX_OUTPUT_STEREO
    };

    v2f vert(appdata_t v)
    {
        v2f OUT;
        UNITY_SETUP_INSTANCE_ID(v);
        UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
        OUT.worldPosition = v.vertex;
        OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);

        OUT.texcoord = v.texcoord;

        OUT.color = v.color * _Color;

        OUT.screenPos = ComputeScreenPos(OUT.vertex);
        return OUT;
    }

    fixed4 frag(v2f i) : SV_Target
    {
        half4 color = (tex2D(_MainTex, i.texcoord) + _TextureSampleAdd) * i.color;

        float2 grabUV = float2(i.screenPos.xy / i.screenPos.w);
        grabUV.y = grabUV.y * -1 + 1;

        fixed4 grab = tex2D(_GrabTexture, grabUV);

        #ifdef UNITY_UI_CLIP_RECT
        color.a *= UnityGet2DClipping(i.worldPosition.xy, _ClipRect);
        #endif

        #ifdef UNITY_UI_ALPHACLIP
        clip(color.a - 0.001);
        #endif

        half4 cont = 1 / (1 + exp(-_Contrast * (grab - 0.5)));
        cont = cont * color;
        cont.a = color.a; //AlphaはSpriteのものを適用する

        return cont;
    }

    ENDCG

    SubShader 
    {
        Tags
        {
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }

        Stencil
        {
            Ref [_Stencil]
            Comp [_StencilComp]
            Pass [_StencilOp]
            ReadMask [_StencilReadMask]
            WriteMask [_StencilWriteMask]
        }

        Cull Off
        Lighting Off
        ZWrite Off
        ZTest [unity_GUIZTestMode]
        Blend SrcAlpha OneMinusSrcAlpha
        ColorMask [_ColorMask]

        GrabPass {}

        Pass
        {
            CGPROGRA
            #pragma vertex vert
            #pragma fragment frag
            ENDCG
        }
    }
    FallBack "Diffuse"
}

参考にさせて頂いたサイト

Unity シェーダーチュートリアル - Tsumiki Tech Times
http://tsumikiseisaku.com/blog/shader-tutorial-refraction/

【Unityシェーダ入門】コントラストを調節できるポストエフェクトを作る - おもちゃラボ
http://tsumikiseisaku.com/blog/shader-tutorial-refraction/

ちょっと宣伝

「はちゃめちゃAR!らくがきらんど」というARKitを用いたアプリを現在開発しており、本記事のカラーフィルムっぽい表現を随所で使用しています。