Shader "UI/HeatHaze"
{
    Properties
    {
        [HideInInspector]_MainTex("-",2D)="white"{} 
        _NoiseTex("NoiseTex", 2D) = "white"{} 
        
        [Header(LensFlare)]
        [Space(10)]
        _Degree("Degree", Range(0, 359)) = 0 //光線の角度を指定
        _FlareLength("FlareLength", Range(0, 1)) = 0.8 //光線の長さを指定   
        _FlareConverge("FlareConverge", Range(0, 1)) = 1 //大きいほど光が端に寄り小さいほど中央に寄る

        [Space(10)]
        _FlareSize("FlareSize", Range(0.1, 2)) = 0.9 //レンズフレアの大きさ
        _FlareHuePlus("FlareHuePlus", Range(-1, 1)) = 0 //レンズフレアの虹色を変化させる
        _FlareLuminance("FlareLuminance", Range(0.1, 2)) = 0.8 //レンズフレアの光の強さ
        [Space(10)]
        [Toggle] _UseFlareAutoAnim("UseFlareAutoAnim", float) = 0 //経過時間によって自動で_FlareLuminanceを変化させるか

        [Header(Flactuation)]
        [Space(10)]
        _FluctPow("FluctPow", Range(0.01, 0.05)) = 0.02 //歪みの強さ
        _AnimSpeed("AnimSpeed", Range(0.1, 1)) = 0.4 //歪みが変化するスピード
        _FluctScroll("FluctScroll", float) = 0 //参照するノイズ画像の座標をずらす、この値を変化させると画面がゆらぐように見える
        [Space(10)]
        [Toggle] _UseFluctAutoAnim("UseFluctAutoAnim", float) = 0 //経過時間によって自動で_FluctScrollを変化させるか

        [Header(Other)]
        [Space(10)]
        _Brightness("Brightness", Range(1, 2)) = 1.3 //画面全体の輝き
        _OverrideCol("OverrideCol", Color) = (1, 0, 0, 1) //画面全体を単一色で上書きする際の色
        _OverrideRate("OverrideRate", Range(0, 1)) = 0.15 //画面全体を単一色で上書きする配合率
    }

    SubShader
    {
        Tags 
        { 
            "Queue" = "Transparent"
            "RenderType"="Transparent"
        }
        Cull Off
        ZWrite Off
        Blend SrcAlpha OneMinusSrcAlpha

        CGINCLUDE

        #pragma vertex vert
        #pragma fragment frag
        #include "UnityCG.cginc"

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

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

        sampler2D _MainTex;
        sampler2D _NoiseTex;        
        sampler2D _GrabTexture;

        //LensFlare
        fixed _Degree;
        fixed _FlareLength;             
        fixed _FlareConverge;

        fixed _FlareSize;
        fixed _FlareLuminance;
        fixed _FlareHuePlus;

        float _UseFlareAutoAnim;

        //Flactuation
        float _FluctPow;
        float _AnimSpeed;
        float _FluctScroll;

        float _UseFluctAutoAnim;      

        //Other
        fixed _Brightness;
        fixed4 _OverrideCol;
        fixed _OverrideRate;

        v2f vert(appdata v)
        {
            v2f o;
            o.vertex = UnityObjectToClipPos(v.vertex);
            o.uv = ComputeGrabScreenPos(o.vertex);
            return o;
        }

        //光線の長さを修正
        fixed2 reflectLength(fixed baseX, fixed baseY)
        {
            fixed remainX = saturate(0.5 - baseX) * (1 - _FlareLength);
            fixed remainY = saturate(0.5 - baseY) * (1 - _FlareLength);
            return fixed2(baseX + remainX, baseY + remainY);
        }

        //光線を適切に中央に寄せる
        fixed2 reflectConverge(fixed baseX, fixed baseY)
        {
            return fixed2(baseX * _FlareConverge, baseY * _FlareConverge);
        }

        //_Degreeの入力値に合わせて座標を適切に変換
        //baseX = 右上にレンズフレアがある時のx軸のベース座標
        //baseY = 右上にレンズフレアがある時のy軸のベース座標
        fixed2 reflectDegree(fixed baseX, fixed baseY)
        {
            //_Degree=0が右上になってるので、少し計算が特殊になってる
            //画面を左45度傾けたとして、_Degree値が中心から右に寄ってるか。180で割るとバグるので179.9で代替え
            fixed isRight = step(_Degree / 179.9, 1);
            //画面を左45度傾けたとして、_Degree値が中心から上に寄ってるか。90で割るとバグるので89.9で代替え
            fixed isUp = isRight * step(_Degree % 179.9 / 89.9, 1) + (1 - isRight) * (1 - step(_Degree % 179.9 / 89.9, 1));

            //画面を4つの区間に分割
            fixed isRightUp = isRight * isUp;
            fixed isRightDown = isRight * (1 - isUp);
            fixed isLeftDown = (1 - isRight) * (1 - isUp);
            fixed isLeftUp = (1 - isRight) * isUp;

            //0~1の値を返す。_Degreeが45, 135, 225, 315の時transは(ほぼ)ゼロになる
            fixed trans = (45 - _Degree % 89.9) / 45;

            //x軸の座標を修正
            fixed fixX = isRightUp * baseX +
                         isRightDown * baseX * trans +
                         isLeftDown * -baseX +
                         isLeftUp * -baseX * trans;
            //y軸の座標を修正
            fixed fixY = isRightUp * baseY * trans +
                         isRightDown * -baseY +
                         isLeftDown * -baseY * trans +
                         isLeftUp * baseY;

            return fixed2(fixX, fixY);
        }

        //レンズフレアの座標修正
        fixed2 fixFlarePos(fixed x, fixed y)
        {
            fixed2 fixUv = reflectConverge(x, y);
            fixUv = reflectLength(fixUv.x, fixUv.y);
            fixUv = reflectDegree(fixUv.x, fixUv.y);
            return fixUv;
        }

        //レンズフレアの大きさを修正
        fixed2 reflectSize(fixed size)
        {
            return size * _FlareSize ;
        }

        //floatをRGBに変換
        fixed3 HUEtoRGB(in float H)
        {
            float R = abs(H * 6 - 3) - 1;
            float G = 2 - abs(H * 6 - 2);
            float B = 2 - abs(H * 6 - 4);
            return saturate(float3(R, G, B));
        }
        
        //レンズフレアの円を作成する
        //shiftX = 画面中央からどれだけズレるか、-0.5~0.5の値を受け入れる
        //shiftY = 画面中央からどれだけズレるか、-0.5~0.5の値を受け入れる
        fixed4 createFlare(v2f IN, fixed size, fixed shiftX, fixed shiftY, 
            fixed hueBlendRate, fixed overrideRate, fixed hueOffset,
            fixed inStartAlpha, fixed inRangeAlpha, fixed outStartAlpha, fixed outRangeAlpha)
        {
            size = reflectSize(size);

            fixed4 color = (tex2D(_MainTex, IN.uv));
            fixed offset = max(0, hueOffset + _FlareHuePlus);
            fixed3 hueColor;
        
            fixed outAlpha = 1;
            fixed inAlpha = 1;

            //渡されたuv座標を作成する円の座標に改変。座標は画面中央を0とし、xもyも最小値が-1、最大値が1になる
            fixed fixX = abs(IN.uv.x - 0.5 - shiftX) * 2;
            fixed fixY = abs(IN.uv.y - 0.5 + shiftY) * 2;
            //解像度の縦横比に関わらず円を正円にするための処理と、円の大きさの修正
            fixed minScreen = min(_ScreenParams.x, _ScreenParams.y);
            fixX = fixX * (minScreen / _ScreenParams.y) / size;
            fixY = fixY * (minScreen / _ScreenParams.x) / size;
            //画面中央からの直線距離を取得
            fixed atan = sqrt(fixX * fixX + fixY * fixY);

            //距離からHUE値を取得
            fixed fixHue = abs(atan + offset);
            //HUEをRGBに変換しを虹色として円に反映
            hueColor= (HUEtoRGB((abs(fixHue)) % 1) / hueBlendRate);
            color.xyz *= 1 / hueColor;

            //円の中心から外に向かうアルファ値を取得
            fixed fixInStartAlpha = inStartAlpha - hueBlendRate;
            inAlpha = saturate(atan - fixInStartAlpha);
            inAlpha = saturate(inAlpha / inRangeAlpha);
            //円の輪郭付近のアルファ値を取得
            fixed fixOutStartAlpha = outStartAlpha - (hueBlendRate / 2) ;
            outAlpha = saturate(atan - fixOutStartAlpha);
            outAlpha = 1 - saturate(outAlpha / outRangeAlpha);

            //変数を4つかけて最終的なアルファ値を決定
            fixed luminance = _UseFlareAutoAnim * (_FlareLuminance + sin(_Time.y) * 0.2) + 
                              (1 - _UseFlareAutoAnim) * _FlareLuminance;
            color.a = outAlpha * inAlpha * overrideRate * luminance;

            return color;
        }

        ENDCG

        //画面全体のゆらぎを実行
        GrabPass{}            
        Pass
        {
            CGPROGRAM
            fixed4 frag (v2f i) : SV_Target
            {
                float2 nUv = i.uv;

                //UseFlareAutoAnimがOnの時は時間経過によってノイズの位置をずらす、Offの時はFluctScrollを参照する
                fixed shift = ((_UseFluctAutoAnim * _Time.y) + ((1 - _UseFluctAutoAnim) * _FluctScroll)) * _AnimSpeed;
                //ノイズ画像をループさせる時に途切れ感を失くすために、サイン波を利用する
                //まずsin関数にuv座標のy軸とshift値を合計したものを渡し、数値を-1~1の範囲に収める
                //それをuv座標用に0~1の範囲に圧縮し、y軸に上書き
                nUv.y = (sin(nUv.y + shift) + 1) / 2;

                //tex2Dを使うことで、指定テクスチャの指定uv座標のカラー値を取得
                fixed4 uvNoise= tex2D(_NoiseTex, nUv);
                //カラーのRGB値を座標の修正値として無理やり変形させる処理
                //ノイズ画像はモノクロのため、黒=(0, 0, 0)、白=(1, 1, 1)のようにRGB値は全て一緒になる
                //どこから取得してもいいがここではrから値を取得、更に値の範囲を0~1から-1~1に拡大し座標の修正値とする
                fixed noise = uvNoise.r * 2 - 1;

                //画面の上下端では歪みを実行させないためのnoise配合率を算出
                fixed isUvBotom = step(i.uv.y, 0.1);
                fixed rate1 = isUvBotom * (i.uv.y / 0.1) + (1 - isUvBotom) * 1;
                fixed isUvUp = 1 - step(i.uv.y, 0.9);
                fixed rate2 = isUvUp * ((0.1 - (i.uv.y - 0.9)) / 0.1) + (1 - isUvUp) * 1;
                fixed noiseRate = rate1 * rate2;

                //noiseの値は-1~1の範囲に収まっている
                //これに_FluctPowを掛けると、最小で-0.01~0.01、最大で-0.1~0.1の範囲に収まる
                //その修正された値で、対象画像のuv座標のy軸だけ移動させ、描画するピクセルをズラす
                i.uv.y += noiseRate * (noise * _FluctPow);

                //Grabした画像に変形させたuv座標を適用させ歪ませる
                fixed4 col = tex2D(_GrabTexture, i.uv);
                return col;
            }
            ENDCG
        }        

        //虹リング大の描写
        Pass
        {
            CGPROGRAM
            fixed4 frag(v2f IN) : SV_Target
            {
                fixed2 fixPos = fixFlarePos(0.35, 0.35);
                fixed4 color = createFlare(IN, 0.4, fixPos.x, fixPos.y, 0.1, 0.25, 12.8, 0, 0, 0.95, 0.1);
                return color;
            }
            ENDCG
        } 

        //虹リング小の描写
        Pass
        {
            CGPROGRAM
            fixed4 frag(v2f IN) : SV_Target
            {
                fixed2 fixPos = fixFlarePos(0.17, 0.17);
                fixed4 color = createFlare(IN, 0.23, fixPos.x, fixPos.y, 0.1, 0.2, 14.9, 0, 1, 0.8, 0.2);
                return color;
            }
            ENDCG
        }

        //虹リング中の描写
        Pass
        {
            CGPROGRAM
            fixed4 frag(v2f IN) : SV_Target
            {
                fixed2 fixPos = fixFlarePos(0.23, 0.23);
                fixed4 color = createFlare(IN, 0.38, fixPos.x, fixPos.y, 0.55, 0.35, 4.65, 0.2, 0.8, 0.9, 0.1);
                return color;
            }
            ENDCG
        }          
        
        //白色発光大の描写
        Pass
        {
            CGPROGRAM
            fixed4 frag(v2f IN) : SV_Target
            {
                fixed2 fixPos = fixFlarePos(0.47, 0.47);
                fixed4 color = createFlare(IN, 0.6, fixPos.x, fixPos.y, 1, 0.9, 0.3, 0, 0, 0.8, 0.7);
                return color;
            }
            ENDCG
        }

        //白色発光中の描写
        Pass
        {
            CGPROGRAM
            fixed4 frag(v2f IN) : SV_Target
            {
                fixed2 fixPos = fixFlarePos(0.26, 0.26);
                fixed4 color = createFlare(IN, 0.3, fixPos.x, fixPos.y, 1, 0.5, 1.5, 0, 0, 0.9, 0.15);
                return color;
            }
            ENDCG
        }

        //白色発光小の描写   
        Pass
        {
            CGPROGRAM
            fixed4 frag(v2f IN) : SV_Target
            {
                fixed2 fixPos = fixFlarePos(0.12, 0.12);
                fixed4 color = createFlare(IN, 0.15, fixPos.x, fixPos.y, 0.9, 0.3, 2, 0, 0, 0.9, 0.1);
                return color;
            }
            ENDCG
        }           
        
        //画面全体を発光させる処理 
        GrabPass{}     
        Pass
        {
            CGPROGRAM
            fixed4 frag(v2f IN) : SV_Target
            {
                fixed4 color = (tex2D(_GrabTexture, IN.uv));
                color = color * _Brightness;
                color = (1 - _OverrideRate) * color + (_OverrideRate) * _OverrideCol;
                return color;
            }
            ENDCG
        }        
    }
}