Unity Shader を用いたメッシュ頂点の編集 (URP 環境)

Unity シェーダーの勉強.
おそらくこれからは URP 環境での開発が一般的になると思われるので, URP 環境での書き方について調べた.
Shader Graph を使えばもっと楽だとは思うが WebGL などへの応用性を考えて HLSL を用いた記述について勉強する.

本項ではメッシュ頂点座標の編集までの部分についてまとめる.

検証環境

各シェーダーの違いについて

シェーダーについて種類と簡単な違いについては以下の通り

Lit 系

Unlit

カスタムシェーダー記述についての基礎

テンプレートの作成

// このシェーダーはコード内に事前定義されている色でメッシュ形状を塗りつぶします。
Shader "$FILENAME/URPUnlitShaderBasic"
{
    // Unity シェーダーのプロパティブロック。この例では出力の色がフラグメントシェーダーの
    // コード内に事前定義されているため、このブロックは空です。
    Properties
    { }

    // シェーダーのコードが含まれる SubShader ブロック。
    SubShader
    {
        // SubShader Tags では SubShader ブロックまたはパスが実行されるタイミングと条件を
        // 定義します。
        Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }

        Pass
        {
            // HLSL コードブロック。Unity SRP では HLSL 言語を使用します。
            HLSLPROGRAM
            // この行では頂点シェーダーの名前を定義します。
            #pragma vertex vert
            // この行ではフラグメントシェーダーの名前を定義します。
            #pragma fragment frag

            // Core.hlsl ファイルには、よく使用される HLSL マクロおよび関数の
            // 定義が含まれ、その他の HLSL ファイル (Common.hlsl、
            // SpaceTransforms.hlsl など) への #include 参照も含まれています。
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            // この構造体定義では構造体に含まれる変数を定義します。
            // この例では Attributes 構造体を頂点シェーダーの入力構造体として
            // 使用しています。
            struct Attributes
            {
                // positionOS 変数にはオブジェクト空間内での頂点位置が
                // 含まれます。
                float4 positionOS   : POSITION;
            };

            struct Varyings
            {
                // この構造体内の位置には SV_POSITION セマンティクスが必要です。
                float4 positionHCS  : SV_POSITION;
            };

            // Varyings 構造体内に定義されたプロパティを含む頂点シェーダーの
            // 定義。vert 関数の型は戻り値の型 (構造体) に一致させる
            // 必要があります。
            Varyings vert(Attributes IN)
            {
                // Varyings 構造体での出力オブジェクト (OUT) の宣言。
                Varyings OUT;
                // TransformObjectToHClip 関数は頂点位置をオブジェクト空間から
                // 同種のクリップスペースに変換します。
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                // 出力を返します。
                return OUT;
            }

            // フラグメントシェーダーの定義。
            half4 frag() : SV_Target
            {
                // 色変数を定義して返します。
                half4 customColor = half4(0.5, 0, 0, 1);
                return customColor;
            }
            ENDHLSL
        }
    }
}
        

頂点シェーダーとフラグメントシェーダーの役割

頂点シェーダーの編集

// vertex shader
Varyings vert(Attributes IN)
{
    // output object
    Varyings OUT;

    // position change with noise
    float3 outPos = IN.positionOS.xyz;

    /* 任意の頂点座標操作 */

    // to clip space
    OUT.positionHCS = TransformObjectToHClip(outPos);

    return OUT;
}
        

(例) 各頂点座標にランダムな揺らぎを与える

ノイズ関数の定義

inline float randomNoise(float2 uv, float time)
{
    return frac(sin(dot(uv + time, float2(12.9898, 78.233))) * 43758.5453);
}
        

時間の取得

頂点シェーダーの書き換え

// vertex shader
Varyings vert(Attributes IN)
{
    // output object
    Varyings OUT;

    // position change with noise
    float3 outPos = IN.positionOS.xyz;

    float scale = 0.1;
    outPos.x += randomNoise(IN.positionOS.yz, _Time.x) * scale;
    outPos.y += randomNoise(IN.positionOS.xz, _Time.x) * scale;
    outPos.z += randomNoise(IN.positionOS.xy, _Time.x) * scale;

    // to clip space
    OUT.positionHCS = TransformObjectToHClip(outPos);

    return OUT;
}
        

シェーダーをマテリアルに適用

noised sphere

サンプルコード

Shader "VertNoiseShader/URPUnlitShaderBasic"
{
    Properties
    { }

    SubShader
    {
        Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }

        Pass
        {
            HLSLPROGRAM
            
            #pragma vertex vert
            
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            // float4 _Time; // unity defined time function

            // user defined random function
            inline float randomNoise(float2 uv, float time)
            {
                return frac(sin(dot(uv + time, float2(12.9898, 78.233))) * 43758.5453);
            }

            struct Attributes
            {
                // vertices position of object space
                float4 positionOS   : POSITION;
            };

            struct Varyings
            {
                // vertices position of screen space
                float4 positionHCS  : SV_POSITION;
            };

            // vertex shader
            Varyings vert(Attributes IN)
            {
                // output object
                Varyings OUT;

                // position change with noise
                float3 outPos = IN.positionOS.xyz;

                float scale = 0.1;
                outPos.x += randomNoise(IN.positionOS.yz, _Time.x) * scale;
                outPos.y += randomNoise(IN.positionOS.xz, _Time.x) * scale;
                outPos.z += randomNoise(IN.positionOS.xy, _Time.x) * scale;

                // to clip space
                OUT.positionHCS = TransformObjectToHClip(outPos);

                return OUT;
            }

            // fragment shader
            half4 frag() : SV_Target
            {
                // return color
                half4 customColor = half4(0.5, 0, 0, 1);
                return customColor;
            }
            ENDHLSL
        }
    }
}