切り絵が趣味の僕がプログラムや創作物について書き溜めるブログ

絵やモデリングやプログラミングなど僕が作った物について。アニメの感想や心動かされたきっかけなどを書き溜めるブログです。Boothで3Dアバターの販売もしていますhttps://paw.booth.pm/

【3日目】VRChat民のための少しづつ身に着けるシェーダー入門

こんぱう~~~
 
今回はスライドショーを作っていきます
シェーダー入門の一日目に画像を動かすってことをしたと思います

kiriesto.hatenablog.com
その画像を動かすプログラミングを応用してスライドショーみたいに画像が切り替わるシェーダーを書いていきます
完成すると ↓ の動画ように動きます!!

youtu.be

 では、さそっく作っていこう!!!

 ・必要なもの

・適当な画像を5枚くらい、正方形のほうが見た目がいいと思います
(別に動く機能を作りたいだけならなんでもOK)
・Unity

・Unityの起動と下準備 

f:id:kiriesto:20210618200728p:plain

さっそく下の四つをそろえていきましょう
・スライドショーに使いたい画像

・Unlit Shader

・3DオブジェクトのPlane

・マテリアル

 適当な場所にスライドショーに使いたい画像を数枚(フォルダーからマウスでピッてやれば入る)、Unlit Shader(右クリックCreate>Shader>Unlit Shaderで作成できる)、3DオブジェクトのPlane(Hierarchyで右クリック>3D Object>Plane)とマテリアル(右クリック>Create>Material )も作成しておきましょう。

Projectのmaterialをドラッグ&ドロップ(マウスでピッてやる)でHierachyのPlaneに入れるとマテリアルを適用できます。

materialのshaderを作ったUnlit Shaderにしておきましょう。

 

・Shaderを書く

f:id:kiriesto:20210618202511p:plain

Show in Explorerを押すとコードがあるファイルに行くので好きなテキストエディターで表示しましょう(僕はVScodeを使っています。)

今回作ったコードは一番下に置いてあるのでコピペして使ってください。

まずはスライドする画像を入れる箱を作ってあげます。

f:id:kiriesto:20210618204050p:plain

こんな感じに書くと...

f:id:kiriesto:20210618204146p:plain
画像をいれるところが増えてる~~~
好きな画像を入れてあげましょう!!

一番上の_MainTexは最初から宣言されていますが、_Tex2~_Tex5は宣言していないので追加しておきましょう。これがないとUnityから入ってきた画像を使えません><

f:id:kiriesto:20210618204327p:plain

最後にfragの中を書いていきます。
今回の中で一番重要なので頑張っていきましょう。

f:id:kiriesto:20210618204710p:plain

完成するとこんな感じ
62行目の

i.uv.x += ((-2*x*x*x) + 3*x*x) + floor(T);

は画像のスライドする速さを調整しています。
簡単なのにすると

i.uv.cx += T;

これでも動きます。これだと一定の速度で常に画像がスライドするっていう感じです。

 

i.uv.x += ((-2*x*x*x) + 3*x*x) + floor(T);

この式の説明をしたいところですが先に

f:id:kiriesto:20210618205336p:plain

このif文がくっついているやつの説明をします。
i.uv.xはUVのx軸座標の値をとってきます。62行目で時間経過で値が足されていることに注意しましょう。

floor()は整数部分のみにしています。
floor(i.uv.x)%5はfloor(i.uv.x)を5で割ったあまりになります。
こうすることで全ての整数が0~4の値であらわされることになります。

f:id:kiriesto:20210618212155p:plain

こんな風に0~29までの値を5で割ったあまりの表作ってみました。
全てのあまり値が0~4になっていることがわかると思います。
しかも、0,1,2,3,4と順番になっているのでこの仕組みを使えば画像を切り替えられそうだなぁと考えました。

(int)floor(i.uv.x)%5の(int)は何??っとなると思います。
これはキャスト演算子と呼ばれ型を強制的に変更するっていうやつです。

今回の場合はfloor(i.uv.x)%5はfloat型(小数点も含んだ値のこと)なので0の場合は0.0という風に1の場合は1.0という扱いになるので==で値が同じかどうか比べる際に==0や==1とすると偽とでてしまったので(int)をつかって無理やりint型(整数部分のみの型)に変換してあげています。

余談、後々に検証したのですがfloor()がなくても(int)でキャストしているので小数点を省いてくれるからちゃんと動いてくれました。

つまり、このif文で画像を切り替えているわけです。UVの値は0~1の間なので
0~1までは一枚目の画像、
1~2の間は二枚目の画像、
2~3の間は三枚目の画像、・・・
ってことです。

62行目に戻って

i.uv.x += ((-2*x*x*x) + 3*x*x) + floor(T);

について説明します。
floor(T)は時間経過Tの整数部分のみを足しています。
小数部分の値の変化が画像の切り替わりかたに直結するので((-2*x*x*x)+3*x*x)でいい感じにしているわけです(xはTの小数部分になります。:float x = frac(T);)
小数部分の値をxに代入しているのでグラフにした場合の範囲は0<=x<1になります。

f:id:kiriesto:20210618214753p:plain
 (0, 0) と (1, 1) を通っていればなんでも大丈夫です。この二点の近くだと値の変化が小さくなるので画像をスライドさせた場合、画像一枚がすべて表示されるところでスライドする速度がゆっくりになります。
62行目のコードをいろいろな式に書き換えて変化を楽しんでみても面白いと思います。

 

それでは今日はここまで、
いい夢を~~~
 

 次の記事

kiriesto.hatenablog.com

 前回の記事

kiriesto.hatenablog.com

 

サンプルコード

    Shader "Unlit/slide"
{
    Properties
    {
        _MainTex ("Texture"2D) = "white" {}
        _Tex2 ("Texture"2D) = "white" {}
        _Tex3 ("Texture"2D) = "white" {}
        _Tex4 ("Texture"2D) = "white" {}
        _Tex5 ("Texture"2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            sampler2D _Tex2;
            sampler2D _Tex3;
            sampler2D _Tex4;
            sampler2D _Tex5;

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

            fixed4 frag (v2f i) : SV_Target
            {
                float T = _Time.y/5;
                fixed4 Tex = 0;
                float x = frac(T);

                
                i.uv.x +=( (-2*x*x*x)+3*x*x)+floor(T);
                //i.uv.y += abs(sin(T));

                if( (int)floor(i.uv.x)%5 == 0){
                    Tex = tex2D( _MainTex, i.uv);
                }else if( (int)floor(i.uv.x)%5 == 1){
                    Tex = tex2D( _Tex2, i.uv);
                }else if( (int)floor(i.uv.x)%5 == 2){
                    Tex = tex2D( _Tex3, i.uv);
                }else if( (int)floor(i.uv.x)%5 == 3){
                    Tex = tex2D( _Tex4, i.uv);
                }else if( (int)floor(i.uv.x)%5 == 4){
                    Tex = tex2D( _Tex5, i.uv);
                }

                fixed4 col = Tex;

                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}