いんでぃーづ

ゲームいろいろ、いろいろ自由

Unityシェーダー:マニュアル操作で波っぽくウネウネさせるシェーダーを書く

前回に引き続きシェーダー話です。

製作中のゲームに 自由にうごかせる波 がほしいなあと思っていて、なんとなくそれっぽい動きが実装できたのでバンザイバンザイ。

f:id:sugar_affordance:20180601145115g:plain

波の高さ、細かさ、波の状態を自由自在に操れるという会心作ですゲッヘッヘ

プロパティ追加

   Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0

        _WSizeX ("WaveSizeX", float) = 5
        _WSizeZ ("WaveSizeZ", float) = 5
        _CyclePosition ("Wave CyclePosition", Range(0, 1)) = 0
        _WaveHeight ("Wave Height", float) = 1
    }
  • _WaveSizeX
    X軸方向の波の細かさ
  • _WaveSizeZ
    Z方向の波の細かさ
  • _CyclePosition
    波の状態を示す変数です。連続的に 0 ~ 1 へと変化させることで波の動きになります。1までいったら0を代入してしまえば永久ループ可能です。
  • _WaveHeight
    波の高さです。一番低い位置から一番高い位置までの距離。

頂点シェーダー

今回のキモは頂点シェーダーで、ババッと説明していきます。

        void vert(inout appdata_full v, out Input o )
        {
            UNITY_INITIALIZE_OUTPUT(Input, o);
    
            float y = 0.5 * sin(radians(
                (_CyclePosition * 360) + (((v.vertex.x % _WSizeX) / _WSizeX) * ((v.vertex.z / _WSizeZ) / _WSizeZ) * 360)
            ));
            v.vertex.xyz = float3(v.vertex.x, y * _WaveHeight, v.vertex.z);            
        }

このシェーダでは作為を加えるのは メッシュのY方向の高さ のみです。

1. 計算のベースとなる周期の位置を決める

まず今の波が時間軸のどこの状態かを決めます。

(_CyclePosition * 360) 

とすることで、プロパティに設定した 0 ~ 1 のfloat値から、 0〜360 の数値が出るので、これをベースの値にとります。

2. X軸、Z軸の波のブレを作る

引数に渡ってくるメッシュの位置からオフセット値を作ることで、頂点ごとの波の位置を作ります。

(v.vertex.x % _WSizeX) / _WSizeX) * ((v.vertex.z / _WSizeZ) / _WSizeZ)

上の計算は、プロパティに設定した「波がループする広さ」をもとに、Y位置のブレを作っています。

計算をよく見ると 0 ~ 1 の範囲におさまることがわかると思います。これに360°をかけることで、頂点ごとに 波の周期のオフセット値 として使用することができます。

3. 波の時間軸に変換する

波の周期は sin 関数で作っています。
sin関数は引数にとるラジアン値が 0 から 2π rad へと連続的に変化することで1周期となります。

が、ラジアンをそのまま扱うのは無理なので、radians 関数を使って度数法(0°〜360°)から変換しています。

radians にとるのは、2までで計算している、波の周期の位置を表す 0 ~ 360°の数値です

sin(radians(
   *****
))

0.5 をかけているのは、sin関数のとる値の範囲が -1 ~ 1 のためです。計算を減らしたいならとっぱらってもいいでしょう。


以下、シェーダ全文

Shader "Custom/ManualWave" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0

        _WSizeX ("WaveSizeX", float) = 5
        _WSizeZ ("WaveSizeZ", float) = 5
        _CyclePosition ("Wave CyclePosition", Range(0, 1)) = 0
        _WaveHeight ("Wave Height", float) = 1
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard addshadow vertex:vert

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0

        sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;

        float _CycleSec;
        float _CyclePosition;
        float _WSizeX;
        float _WSizeZ;
        float _WaveHeight;

        // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
        // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
        // #pragma instancing_options assumeuniformscaling
        UNITY_INSTANCING_BUFFER_START(Props)
        // put more per-instance properties here
        UNITY_INSTANCING_BUFFER_END(Props)

        void vert(inout appdata_full v, out Input o )
        {
            UNITY_INITIALIZE_OUTPUT(Input, o);
            
            // sinは -1 ~ 1 をとるので0.5をかける
            float y = 0.5 * sin(radians(
                (_CyclePosition * 360) + (((v.vertex.x % _WSizeX) / _WSizeX) * ((v.vertex.z / _WSizeZ) / _WSizeZ) * 360)
            ));
            v.vertex.xyz = float3(v.vertex.x, y * _WaveHeight, v.vertex.z);            
        }

        void surf (Input IN, inout SurfaceOutputStandard o) {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

マテリアルから _CyclePosition をいじれば、波の状態を自由自在にあやつれます。ぜひ一度おためしあれ。


“Unity” and Unity logos are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere, and are used under license.


免責事項

当サイトの広告バナー、リンクによって提供される情報、サービス内容について、当サイトは一切の責任を負いません。

また、当サイトの情報を元にユーザ様が不利益を被った場合にも、当サイトは一切の責任を負いません。

すべて自己責任でお願いします。