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

#こんなやつ
3つのCubeが並んだ画面にUGUIのImageで色を上乗せしています。
カラーフィルムっぽい表現にはシェーダを利用しており、このImageにシェーダを適用したマテリアルを追加しています。
<img width="508" alt="スクリーンショット 2018-02-21 12.06.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/193550/2c810a07-192e-d509-1a1c-a8bb1ba738d2.png">
<img width="502" alt="スクリーンショット 2018-02-21 12.24.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/193550/3f92bf10-e9d0-e27d-022d-065bf16ac878.png">


Imageにスプライトを適用してあげるとこんな感じ
<img width="498" alt="スクリーンショット 2018-02-21 12.22.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/193550/f9a21083-c3b1-09fc-84d7-74ceb2004b03.png">

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

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

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

```glsl
SubShader
{
  GrabPass {}

  Pass
  {
    CGPROGRAM
    ...
    sampler2D _GrabTexture;
    ...
    ENDCG
  }
}
```
この様な感じで使うのですが、TsumikiTechTimes様の[こちら](http://tsumikiseisaku.com/blog/shader-tutorial-refraction/)の記事で詳しく書かれていますので詳細は割愛します。

##コントラストを調整する
GrabPassで取得した背景の画にコントラスト調整の計算を加えてあげるだけです。
おもちゃラボ様の[こちら](http://nn-hokuson.hatenablog.com/entry/2017/08/01/194341)の記事を参考にさせて頂きました。

```glsl
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シェーダに書き加えてあげるだけで完成です。

```glsl
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を用いたアプリを現在開発しており、本記事のカラーフィルムっぽい表現を随所で使用しています。
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ついに…AppStoreで公開されました！！！<br>ARを使用した、3Dらくがきアプリです！<br><br>現在アーリーアクセス価格となっております！是非とも遊んでみてください！<a href="https://twitter.com/hashtag/ARKit?src=hash&amp;ref_src=twsrc%5Etfw">#ARKit</a> <a href="https://twitter.com/hashtag/AppStore?src=hash&amp;ref_src=twsrc%5Etfw">#AppStore</a> <a href="https://twitter.com/hashtag/iOS?src=hash&amp;ref_src=twsrc%5Etfw">#iOS</a> <a href="https://twitter.com/hashtag/madewithunity?src=hash&amp;ref_src=twsrc%5Etfw">#madewithunity</a> <a href="https://t.co/vKZwm7SWHa">https://t.co/vKZwm7SWHa</a></p>&mdash; はちゃめちゃARらくがきらんど開発 (@rakugakiland) <a href="https://twitter.com/rakugakiland/status/972095395093389313?ref_src=twsrc%5Etfw">2018年3月9日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>