場面遷移用のUI縞模様（ストライプ）シェーダ

場面遷移で使える縞模様（ストライプ）のシェーダを書いたので、コードを貼っておきます。スプライトではなくストライプ。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">せっかくなので、u1wからの新要素として「ストライプの角度指定」も入れておいた。使い処が増えたかな。<br>Qiitaは近々書きます。 <a href="https://t.co/E6xyTBgWoO">pic.twitter.com/E6xyTBgWoO</a></p>&mdash; MIYAKE (@ScreenPocket) <a href="https://twitter.com/ScreenPocket/status/1371148999328030727?ref_src=twsrc%5Etfw">March 14, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

今回も前回の放射シェーダと同じく、builtInシェーダのUI-Defaultから作成したUI用のシェーダとなっています。

https://qiita.com/ScreenPocket/items/852f4c893c27715a99e6

重ね合わせとかを考えると、ポストプロセスよりも多分UIの方が扱いやすいはず。
そして、色についても前回と同じく、「アタッチしたUI.Graphicのcolor」と「マテリアルの_Color」の2色で色付けが可能です。

#コード
```glsl:UI-Stripe.shader
Shader "ScreenPocket/UI/Stripe"
{
    Properties
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)
        _Step ("Step", Range(-1,1)) = 0
        _Angle ("Angle", Range(0,360)) = 0
        _LineCount ("Line Count", Float) = 32
        
        _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
    }

    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 One OneMinusSrcAlpha
        ColorMask [_ColorMask]

        Pass
        {
            Name "Default"
        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 2.0

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

            #pragma multi_compile_local _ UNITY_UI_CLIP_RECT
            #pragma multi_compile_local _ UNITY_UI_ALPHACLIP

            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;
                half4  mask : TEXCOORD2;
                UNITY_VERTEX_OUTPUT_STEREO
            };

            sampler2D _MainTex;
            half4 _Color;
            half4 _TextureSampleAdd;
            float4 _ClipRect;
            float4 _MainTex_ST;
            float _MaskSoftnessX;
            float _MaskSoftnessY;
            float _Angle;
            float _Step;
            float _LineCount;

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

                float2 pixelSize = vPosition.w;
                pixelSize /= float2(1, 1) * abs(mul((float2x2)UNITY_MATRIX_P, _ScreenParams.xy));

                float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
                float2 maskUV = (v.vertex.xy - clampedRect.xy) / (clampedRect.zw - clampedRect.xy);
                OUT.texcoord = float4(v.texcoord.x, v.texcoord.y, maskUV.x, maskUV.y);
                OUT.mask = half4(v.vertex.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_MaskSoftnessX, _MaskSoftnessY) + abs(pixelSize.xy)));

                OUT.color = v.color;
                return OUT;
            }

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

                half cosValue = cos(radians(_Angle));
                half sinValue = sin(radians(_Angle));
                half2x2 rotate = half2x2(cosValue, -sinValue, sinValue, cosValue);
                //中央にずらして(-0.5)、回転した後で元の位置に戻す(+0.5)
                IN.texcoord = mul(IN.texcoord - 0.5, rotate) + 0.5;

                float rate = round(frac(IN.texcoord.y * _LineCount));
                color *= lerp(_Color, IN.color, rate);

                #ifdef UNITY_UI_CLIP_RECT
                half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy)) * IN.mask.zw);
                color.a *= m.x * m.y;
                #endif

                //交互にくり抜く
                rate = ((rate-0.5)*2);
                color.a = 1-abs(ceil(IN.texcoord.x - rate * _Step)-1);
                
                #ifdef UNITY_UI_ALPHACLIP
                clip (color.a - 0.001);
                #endif
                

                color.rgb *= color.a;

                return color;
            }
        ENDCG
        }
    }
}

```

ほとんどがUI-Defaultのままですが、追加した部分でのポイントは

* _Angleで回転行列を作る
* 作った回転行列でIn.texcoordを回転
* round(frac())と_LineCountの掛け算で帯模様を作る
* abs(ceil())と_Stepで良い感じに左右を切り抜く　←　これが難しかった；

な感じですかね。

# 使い方
前回の放射シェーダと同じく、上記のシェーダを充てたマテリアルを作成し、UIのImageかRawImageにマテリアルを設定する事で表示をすることが可能となります
![スクリーンショット 2021-03-15 220754.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61266/fbfca933-6627-8c8e-5045-a17e87efbe5a.png)

各パラメータの用法は下記です

|パラメータ名|用法|
|:-|:-|
|_Color|ストライプの帯の色を決めます。この色とアタッチしたGraphic（Image、RawImage）のColorを交互に置いた縞模様となります|
|_Step|帯の表示をずらすことが出来ます。0で面をすべて覆いきった見た目になり、-1か1で帯が退出しきった見た目になります。この数値をDOTweenなどで滑らかに動かすことで、場面遷移に効果的に使用できます。|
|_Angle|帯の角度を決めることが出来ます0(360)か180で横帯、90か270で縦帯となります|
|_LineCount|帯の数を決めます。例えば10を入力すると、帯の色がそれぞれ10ずつ(つまり2色合わせると20本)表示されます|

実際ゲームでどう使っているかは、拙作を遊んでみて下さい。

https://unityroom.com/games/kitte_nobashite_korogatte

# DOTweenと連携
例えばこんな感じ。u1wのコードを一部抜粋します。

```cs:StripePanel.csの一部を抜粋
        //侵入アニメーション再生
        public void PlayIn( Action onComplete )
        {
            //Inフラグ立てる
            _isIn = true;
            //入力ロック
            UIManager.instance.InputLocker.Lock();
            //パネルをActive化
            gameObject.SetActive(true);
            //Image（ストライプ本体）をActive化
            _image.gameObject.SetActive(true);
            //アニメーション(-1～0までを0.4秒かけて_Stepに伝える)
            DOTween.To(()=>-1f, f =>
            {
                _bgMaterial.SetFloat("_Step", f);
            }, 0f, 0.4f).SetEase(Ease.OutQuad).OnComplete(() =>
            {
                //入力ロック解除
                UIManager.instance.InputLocker.Unlock();
                //完了コールバック
                onComplete?.Invoke();
            });
        }

        //退避アニメーション再生
        public void PlayOut(Action onComplete)
        {
            //入力ロック
            UIManager.instance.InputLocker.Lock();
            //アニメーション(0～1を0.4秒かけて_Stepに伝える)
            DOTween.To(()=>0f, f =>
            {
                _bgMaterial.SetFloat("_Step", f);
            }, 1f, 0.4f).SetEase(Ease.InQuad).OnComplete(() =>
            {
                //Inフラグ下ろす
                _isIn = false;
                //入力ロック解除
                UIManager.instance.InputLocker.Unlock();
                //帯本体非アクティブ化
                _image.gameObject.SetActive(false);
                //パネル自体を非アクティブ化
                gameObject.SetActive(false);
                //完了コールバック
                onComplete?.Invoke();
            });
        }
```
こんな感じの関数を用意しておけば、

1. 場面転換前にPlayIn()を呼び、
2. PlayIn()が完了したら画面切り替え処理を行い、
3. 画面切り替え処理が終わったらPlayOut()を呼び、
4. PlayOut()が完了したら次の画面の処理を開始する。

という流れが組み立てやすくなる、という感じですね。

# 参考資料
_Angleの実装は下記を参考にさせて頂きました。

https://zenn.dev/kento_o/articles/64c8d585924c60633342