-100p

-10p

+10p

+100p

Unityカーテンシェーダー

UnityでuGUI用カーテンシェーダー。y軸座標に応じてx軸のuv座標をずらす実装。
(A Shader for Unity uGUI that produce natural curves of the curtains)

関連ページ 参考URL
こないだ業務でカーテンの歪みをパッと実装したけど、クオリティに満足いってなかったので更に研究をしていた。
結局求める内容には届かなかったけど、これ以上研究する気力を失くしたのでここで公開。

カーテンシェーダー、コード全文

下がカーテンシェーダーのサンプル動画。
窓が途中で光ってる件に関しては、これは演出上 発光シェーダー を入れてみただけで今回のカーテンシェーダーは関係ない。

以下コード全文。uGUI Imageへの反映方法は こちらの記事 参照。
Shader "UI/CurtainShader"
{
    Properties
    {
        
//カーテンを横に押す力

        _PressPow ("PressPow", Range(-1, 1)) = 0
        
//カーテンのカーブの強さ

        _CurvePow ("CurvePow", Range(0, 10)) = 2
        
//カーブの外カーブと内カーブの比率に影響

        _ReverseCurveRatio ("ReverseCurveRatio", Range(0, 10)) = 2
    }

    SubShader
    {
        Tags { "Queue" = "Transparent" }
        Blend SrcAlpha OneMinusSrcAlpha
        Cull Off
        LOD 100
        ZWrite Off
                
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            struct appdata
            {
                fixed2 uv : TEXCOORD0;
                fixed4 vertex : POSITION;
                fixed4 color : COLOR;
            };

            struct v2f
            {
                fixed2 uv : TEXCOORD0;
                fixed4 vertex : SV_POSITION;
                fixed4 color : COLOR;
            };

            sampler2D _MainTex;

            float _PressPow;
            float _CurvePow;
            float _ReverseCurveRatio;

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

            fixed partRange(v2f i)
            {
                return 1 - i.uv.y;
            }

            
//外カーブ率を百パーセントとした場合の、x軸のピクセルの移動値を返す

            fixed outCurveShift(v2f i)
            {
                fixed y = partRange(i);
                fixed slidePos = y * y * pow(y, _CurvePow) * _PressPow;
                return slidePos;
            }

            
//内カーブ率を百パーセントとした場合の、x軸のピクセルの移動値を返す

            fixed inCurveShift(v2f i)
            {
                fixed y = partRange(i);
                fixed slidePos = y * (y - 2)  * pow((y - 2) * (y - 2), _CurvePow) * _PressPow;
                return -slidePos;
            }

            
//内カーブの比率

            fixed inRatio(v2f i)
            {
                fixed y = partRange(i);
                fixed inRatio = y * pow(y, _ReverseCurveRatio);
                return inRatio;
            }

            
//外カーブのピクセル移動値を比率を適用して取得

            fixed outCurveShiftWithRatio(v2f i)
            {
                return outCurveShift(i) * (1 - inRatio(i));
            }

            
//内カーブのピクセル移動値を比率を適用して取得

            fixed inCurveShiftWithRatio(v2f i)
            {
                return inCurveShift(i) * inRatio(i);
            }

            fixed4 frag (v2f i) : SV_Target
            {
                
//外カーブと内カーブのピクセル移動値を合成

                float curveX = i.uv.x + outCurveShiftWithRatio(i) + inCurveShiftWithRatio(i);
                fixed2 fixUv = fixed2(curveX , i.uv.y);

                
//_PressPowに応じてカーテンのy軸のスケールを少し小さくする

                fixed scaler1 = (1 + abs(_PressPow / 1));
                
//y軸のスケールを小さくしたのに応じて、uv座標を少し上にあげる

                fixed adjust1 = (1 - scaler1) * 0.9;
                fixUv = fixed2(fixUv.x, fixUv.y * scaler1 + adjust1);
                fixed4 col = tex2D(_MainTex, fixUv);

                col *= i.color;
                return col;
            }
            ENDCG
        }
    }
}

プロパティを解説

インスペクターでプロパティの値を操作し、効果を試すことが出来る。プロパティは3つしかない。
 PressPow 
: カーテンを横に押す力
 CurvePow 
: カーテンのカーブの強さ
 ReverseCurveRatio 
: カーブの外カーブと内カーブの比率に影響

コードを少し解説

基本的にやってることは、v2f iに渡された各ピクセルのy軸座標の大きさに応じて、x軸の座標を横にずらすという処理をしている。

uv座標のy軸は、下から上に向けて0から1に進むので、partRange関数内で1から引かせている。
こうすることで、カーテンが下にいくほどx軸のズレが大きくなっている。
            fixed partRange(v2f i)
            {
                return 1 - i.uv.y;
            }

y軸座標からx軸の移動値を算出する際に、まず対象のy軸座標に極端なEaseをかけて値を歪め、x軸のピクセル移動のカーブ処理につなげている。
カーテンの上半分を占める外カーブは、OutQuadの式の一部であるt * tを参考にした。
ただし、t(時間)の値をyに置き換えている。
            
//外カーブ率を百パーセントとした場合の、x軸のピクセルの移動値を返す

            fixed outCurveShift(v2f i)
            {
                fixed y = partRange(i);
                fixed slidePos = y * y * pow(y, _CurvePow) * _PressPow;
                return slidePos;
            }
powのべき乗によって、_Curveの値に応じて更に強いEaseをかけている。

カーテンの下半分を占める内カーブは、InQuadの式の一部であるt * (t - 2)を参考にした。こちらもtをyに置き換え。
            
//内カーブ率を百パーセントとした場合の、x軸のピクセルの移動値を返す

            fixed inCurveShift(v2f i)
            {
                fixed y = partRange(i);
                fixed slidePos = y * (y - 2)  * pow((y - 2) * (y - 2), _CurvePow) * _PressPow;
                return -slidePos;
            }
0
0

-100p

-10p

+10p

+100p