7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2021-03-15

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

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

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

コード

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

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

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

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

DOTweenと連携

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

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の実装は下記を参考にさせて頂きました。

7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?