夜風のMixedReality

xRと出会って変わった人生と出会った技術を書き残すためのGeekなHoloRangerの居場所

ゼロから始めるUnityShader開発 第五章 アニメーションするシェーダーを書く 膨張縮小

本日はShader枠です。

今回はShaderを使ったアニメーションを実装していきます。

〇Shader内でのアニメーション

 UnityではC#スクリプトやAnimatorなどを使用することで見た目のアニメーションを行うことができます。

 これらはCPU上で実行され、しばしばパフォーマンスに影響が出る場合があります。

 Shaderでのアニメーションの場合はGPU上で実行されているためパフォーマンスが良い場合があります。

 また今回紹介するシェーダーのようにシェーダーではオブジェクトの形状をより細かくカスタマイズできるためシェーダーでしかできないようなアニメーション表現もあります。

 今回は今までチュートリアル企画であまり触ってこなかった頂点シェーダーを使用したアニメーションを行っていきます。

〇シェーダーでのアニメーション

 シェーダーでは_Timeを使用することでUnity内での時間を取得することができます。

docs.unity3d.com

_Time変数は4次元の値を持っておりそれぞれxyzw=t/20, t, t2, t3となっています。

 1秒ごとに1ずつという場合は_Time.yを使用する必要がある点が注意点です。

 この_Time変数を使用して頂点シェーダーで頂点のアニメーションを行います。

〇頂点シェーダー

 今回はモデルのスケールが変わるシェーダーを記述します。

 スケールを変えるためには頂点の位置を法線方向に展開します。(膨張収縮)

 このため頂点を処理するGetVertexPositionInputsに渡す頂点の座標をいじります。

VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz + sin(_Time.x)* v.normal);

 ここではsin(_Time.x)を使用しています。 これは今回法線方向に展開するため、無限に大きくなってしまうことを避けるために周期を描くようにしています。

これを球に適応することで次のようにスケールが変わるシェーダーが完成します。

本日は以上です。

〇コード

           v2f vert (appdata v)
            {
                v2f o;
               // o.vertex = TransformObjectToHClip(v.vertex);
                //面の法線を取得、ライトの当たる向きを計算
                VertexNormalInputs normal = GetVertexNormalInputs(v.normal);
                o.uv = v.uv;
                o.normalWS = normal.normalWS;
                VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz + sin(_Time.x)* v.normal*1);
                o.vertex = vertexInput.positionCS;
                o.shadowCoord = GetShadowCoord(vertexInput);
                return o;
            }

〇Shader全文

Shader "Unlit/TutorialShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _MainColor("Color" ,color) = (1,1,1,1)
        _Alpha("Alpha" ,float) =1
        _AlphaThreshold("AlphaThreshold",float)=0.5
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200
        AlphaToMask On

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include  "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
            
            struct appdata
            {
                float4 vertex : POSITION;
                half3 normal: NORMAL;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
            float4 vertex : SV_POSITION;
            float3 normalWS : TEXCOORD1;
            float4 shadowCoord : TEXCOORD3;
                float2 uv : TEXCOORD0;
            };

            TEXTURE2D(_MainTex);
            SAMPLER(sampler_MainTex);
            float4 _MainColor;
            float _Alpha;
            float _AlphaThreshold;
            
            v2f vert (appdata v)
            {
                v2f o;
               // o.vertex = TransformObjectToHClip(v.vertex);
                //面の法線を取得、ライトの当たる向きを計算
                VertexNormalInputs normal = GetVertexNormalInputs(v.normal);
                o.uv = v.uv;
                o.normalWS = normal.normalWS;
                VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz + sin(_Time.x)* v.normal*1);
                o.vertex = vertexInput.positionCS;
                o.shadowCoord = GetShadowCoord(vertexInput);
                return o;
            }

            float4 frag (v2f i) : SV_Target
            {
                float4 col =   SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv)  *_MainColor;
                //Light.hlslで提供されるUnityのライトを取得する関数
                Light lt = GetMainLight(i.shadowCoord);

                //ライトの向きを計算
                float strength = dot(lt.direction, i.normalWS);
                float4 lightColor = float4(lt.color, 1)*(lt.distanceAttenuation * lt.shadowAttenuation);
                col = col* lightColor*strength;
                col.a = (_AlphaThreshold<col.a)? 1:0;
                return col;
            }
            ENDHLSL
        }
    }
}