ミドリ黄のプログラミングメモノート

主にUnity(C#)を中心としたプログラムの備忘録

Unity Shader2D 画像にストライプの模様をつける





使用した素材

前回と同様に使用した素材はベイツ・イメージズ様のものです。素材のリンクは以下の通りです。
スプラッシュペイントと黒背景の壁紙

これをアセット内に追加しておきます。




画像にストライプを付ける

前回と同様にShaderを使うためにまず次の2つを作成します。

  1. Materialの作成(「Create」⇒「Material」)
  2. Shaderの作成(「Create」⇒「Shader」⇒「Image Effect Shader」)

作成したら次のようになります。さらに、StripeMaterialにドラッグ&ドロップでStripeShaderを追加するのを忘れずに


次に、StripeShaderのプログラムを以下の様に変更します。frag関数を書き換えるだけで大丈夫だと思います。

Shader "Hidden/StripeShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            #define PI 3.14159265359

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

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

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

            sampler2D _MainTex;

            fixed4 frag(v2f i) : SV_Target
            {
                if (abs(sin(i.uv.x * 2 * PI)) > 0.96) {
                    return 1;
                }
                else {
                    fixed4 col = tex2D(_MainTex, i.uv);
                    return col;
                }
            }
            ENDCG
        }
    }
}


ここでシーン上に用意した素材を追加します。


これにStripeMaterialを追加すると以下の様になることが確認できます。





プログラムの解説

ここでは先ほどのプログラムの解説を行っていきます。
重要なのは

if (abs(sin(i.uv.x * 2 * PI)) > 0.96) {
    return 1;
}

という部分です。
これは、画像のx座標を変数としたサイン関数の絶対値が0.96以上の時に画像を白くするというものです。
図で表すと画像のようなアルゴリズムとなっています。





ストライプを動かす

次は画像に表示したストライプを動かす方法を紹介します。
そのためには先ほど示したサイン関数を時間軸に沿って動かせばよいです。下の画像はsin(x-t)のtを変化させたときのグラフですが、山の位置が動いていることが確認できます。


そのためにはゲーム実行からの経過時間を取得する必要があるのですが、すでにUnityが用意している変数である_Timeを利用すればOKです。詳細は公式ドキュメント「ビルトインのシェーダー変数」に書かれていますが、

_Time.y

とすればゲーム実行からの経過時間を秒単位で取得できそうです。

以上を踏まえると先ほどのプログラムのif文を

if (abs(sin((i.uv.x - _Time.y) * 2 * PI)) > 0.96)

を書き換えれば目的の動作を行いそうです。

実際に動かしてみた結果が下の動画です。