知識0からのUnityShader勉強

知識0からのUnityShader勉強

UnityのShaderをメインとして、0から学んでいくブログです。

【UnityShader】動く円を作る #14

前回の成果

soramamenatan.hatenablog.com

テクスチャのブレンドができるようになった。

今回やること

nn-hokuson.hatenablog.com

円を描画し、それを動かしていこうと思います。

前準備

f:id:soramamenatan:20190730120243p:plain

SceneにPlaneを置きます。
その時に、Positionが0,0,0になるように配置してください。
他のパラメーターは適当でも大丈夫です。

まずは円を描画していきます。

ソースコード

Shader "Custom/moveRing" {
    SubShader {
        Tags { "RenderType" = "Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf Standard
        #pragma target 3.0

        struct Input {
            float3 worldPos;
        };

        void surf (Input IN, inout SurfaceOutputStandard o) {
            float dist = distance(fixed3(0, 0, 0), IN.worldPos);
            float radius = 2;
            if (radius < dist) {
                fixed4 purple = fixed4(110 / 255.0f, 87 / 255.0f, 139 / 255.0f, 1);
                o.Albedo = purple;
            } else {
                o.Albedo = fixed4(1,1,1,1);
            }
        }
        ENDCG
    }
    FallBack "Diffuse"
}

distとradiusを比較して、ベースカラーを変更しているように見えます。

worldPos

ワールド座標のことです。

名称 説明
ワールド座標(グローバル座標) 原点から見た座標
ローカル座標 1つ上の親から見た相対的な座標

dkrevel.com

distance

これは指定した2点の間の距離を求める関数です。

// a ... 距離を求めたい1つ目の点
// b ... 距離を求めたい2つ目の点
distance (a, b);

今回の場合、

distance(fixed3(0, 0, 0), IN.worldPos);

なので原点とワールド座標との距離を取っています。

円の完成

f:id:soramamenatan:20190730124056p:plain

このように出れば完成です。

円をリングにする

Shader "Custom/moveRing" {
    SubShader {
        Tags { "RenderType" = "Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf Standard
        #pragma target 3.0

        struct Input {
            float3 worldPos;
        };

        void surf (Input IN, inout SurfaceOutputStandard o) {
            float dist = distance(fixed3(0, 0, 0), IN.worldPos);
            // 変更点
            float circleRadius = 2;
            float ringWidth = 0.2f;
            if (circleRadius < dist && dist < circleRadius + ringWidth) {
                o.Albedo = fixed4(1,1,1,1);
            } else {
                fixed4 purple = fixed4(110 / 255.0f, 87 / 255.0f, 139 / 255.0f, 1);
                o.Albedo = purple;
            }
            // ここまで
        }
        ENDCG
    }
    FallBack "Diffuse"
}

リングの幅を作ってあげるようにします。

リングの完成

f:id:soramamenatan:20190730160042p:plain

これでリングを描画することができました。
次はたくさんリングを描画してみます。

リングをたくさん描画

Shader "Custom/moveRing" {
    SubShader {
        Tags { "RenderType" = "Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf Standard
        #pragma target 3.0

        struct Input {
            float3 worldPos;
        };

        void surf (Input IN, inout SurfaceOutputStandard o) {
            float dist = distance(fixed3(0, 0, 0), IN.worldPos);
            // 変更点
            float interval = 3.0f;
            float val = abs(sin(dist * interval));
            float ringWidth = 0.98f;
            if (val > ringWidth) {
                o.Albedo = fixed4(1,1,1,1);
            } else {
                fixed4 purple = fixed4(110 / 255.0f, 87 / 255.0f, 139 / 255.0f, 1);
                o.Albedo = purple;
            }
            // ここまで
        }
        ENDCG
    }
    FallBack "Diffuse"
}

だんだん複雑になってきました。

sin

#13で行なったsinTimeと同じで、こちらはtimeを使用していないだけです。

soramamenatan.hatenablog.com

前回も説明しましたが、正弦波の絶対値を取得することによって、0~1の間を往復する波ができます。

f:id:soramamenatan:20190730163801j:plain

【Unityシェーダ入門】円やリングをかっこよく動かす方法 - おもちゃラボ:より引用

次に、リングの幅を示すために正弦波の山の部分のみを白にします。
これでリングを複数個描画できるようになりました。

f:id:soramamenatan:20190730164244j:plain

【Unityシェーダ入門】円やリングをかっこよく動かす方法 - おもちゃラボ:より引用

リングをたくさん描画の完成

f:id:soramamenatan:20190730164355p:plain

あとは動かすだけです!

動かす

Shader "Custom/moveRing" {
    Properties {
        // 変更点
        _Color ("Color" , Color) = (1 ,1 ,1 ,1)
        // ここまで
    }
    SubShader {
        Tags { "RenderType" = "Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf Standard
        #pragma target 3.0

        struct Input {
            float3 worldPos;
        };

        // 変更点
        fixed4 _Color;
        // ここまで

        void surf (Input IN, inout SurfaceOutputStandard o) {
            float dist = distance(fixed3(0, 0, 0), IN.worldPos);
            float interval = 3.0f;
            // 変更点
            float speed = 10.0f;
            float val = abs(sin(dist * interval + _Time.y * speed));
            float ringWidth = 0.98f;
            if (val > ringWidth) {
                o.Albedo = fixed4(1,1,1,1);
            } else {
                o.Albedo = _Color;
            }
            // ここまで
        }
        ENDCG
    }
    FallBack "Diffuse"
}

主な変更点は

float val = abs(sin(dist * interval + _Time.y * speed));

ここで、正弦波の波をTimeで移動させてあげているだけです。

ちなみに

interval + _Time.y

ここを-に変更すると、波の動きが逆になります。

リングを動かしてみた

f:id:soramamenatan:20190730181959g:plain

このようにリングが動けば完成です。

リングの動きは同じなので、別のオブジェクトにマテリアルをアタッチしてあげるとこのようなこともできます。

f:id:soramamenatan:20190730181856g:plain

ソースコードにコメントを付与

Shader "Custom/moveRing" {
    // プロパティ
    Properties {
        // 波紋の色
        _Color ("Color" , Color) = (1 ,1 ,1 ,1)
    }
    // Shaderの中身の記述
    SubShader {
        // 一般的なShaderを使用
        Tags { "RenderType" = "Opaque" }
        // しきい値
        LOD 200

        // cg言語記述
        CGPROGRAM
        // 指定しないとフォワードレンダリング
        #pragma surface surf Standard
        // Shader Model
        #pragma target 3.0

        // input構造体
        struct Input {
            // ワールド座標
            float3 worldPos;
        };

        // 波紋の色
        fixed4 _Color;

        // surf関数
        void surf (Input IN, inout SurfaceOutputStandard o) {
            // 原点からの距離
            float dist = distance(fixed3(0, 0, 0), IN.worldPos);
            // 波紋の感覚
            float interval = 3.0f;
            // 波紋のスピード
            float speed = 10.0f;
            // sin波
            float val = abs(sin(dist * interval + _Time.y * speed));
            // リングの幅(0 ~ 1)
            float ringWidth = 0.98f;
            // ベースカラーを変化させる
            if (val > ringWidth) {
                o.Albedo = fixed4(1,1,1,1);
            } else {
                o.Albedo = _Color;
            }
        }
        // Shaderの記述終了
        ENDCG
    }
    // SubShaderが失敗した時に呼ばれる
    FallBack "Diffuse"
}

今回は以上となります。
次回は、オブジェクトの座標をワールド座標からローカル座標に変換してみようと思います!