Version: 2023.1
言語: 日本語
HLSL のシェーダーキーワードの宣言と使用
Cg/HLSL でシェーダープロパティを参照する

シェーダーセマンティクス

HLSL シェーダープログラム を書くとき、入力変数と出力変数は セマンティクス を使って “目的” を示す必要があります。これは、HLSL シェーダー言語の基本的なコンセプトです。詳細は、MSDN のセマンティクスのドキュメント (英語) を参照してください。

頂点シェーダー入力セマンティクス

メインの頂点シェーダー関数 (#pragma vertex directive で表示) は、すべての入力パラメーターに対してセマンティクスを持つ必要があります。 これらは、頂点位置、法線メッシュ、テクスチャ座標などの個々の メッシュ データ要素に対応します。 詳細は 頂点プログラムの入力 を参照してください。

ここに、1 回の入力で頂点位置とテクスチャ座標を取得する簡単な頂点シェーダーの例があります。ピクセルシェーダーは、テクスチャ座標を 1つの色として可視化しています。

Shader "Unlit/Show UVs"
{
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            struct v2f {
                float2 uv : TEXCOORD0;
                float4 pos : SV_POSITION;
            };

            v2f vert (
                float4 vertex : POSITION, // 頂点位置入力
                float2 uv : TEXCOORD0 // 最初のテクスチャ座標入力
                )
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                o.uv = uv;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return fixed4(i.uv, 0, 0);
            }
            ENDCG
        }
    }
}

すべての個々の入力を 1 つずつ記述する代わりに、 それらの構造体を宣言し、構造体の個々のメンバー変数の セマンティクスを示します。

フラグメントシェーダー出力セマンティクス

多くの場合、フラグメント (ピクセル) シェーダーは色を出力し、SV_Target セマンティクスを持ちます。この例のフラグメントシェーダーは、まさしくそれを行います。

fixed4 frag (v2f i) : SV_Target

関数 frag は、fixed4 型 (低精度 RGBA カラー) の戻り値を求めます。 値が 1つだけ返されるので、 セマンティクスは関数 SV_Target そのものに表示されます。

出力で構造体として返すことも可能です。 上のフラグメントシェーダーもこのように書き換えることができ、 まったく同じことができます。

struct fragOutput {
    fixed4 color : SV_Target;
};
fragOutput frag (v2f i)
{
    fragOutput o;
    o.color = fixed4(i.uv, 0, 0);
    return o;
}

フラグメントシェーダーから構造体を返すのは、 主に、シェーダーが単数のカラーを返すだけでない場合に有用です。 フラグメントシェーダー出力にサポートされるその他のセマンティクスは以下のとおりです。

複数レンダリングターゲット: SV_TargetN

SV_Target1, SV_Target2, など: これらはシェーダーによって加えられた追加の色です。1度に 1つ以上のレンダーターゲットにレンダリングをする (Multiple render targets 法、または MRT) ときに使用されます。 SV_Target0SV_Target と同じです。

ピクセルシェーダーデプス出力: SV_Depth

通常は、フラグメントシェーダーは Z バッファ値をオーバーライドしません。デフォルト値は標準の三角形のラスタライゼーションから使用されます。ただし、いくつかのエフェクトにおいては、ピクセルごとにカスタムの Z バッファのデプス値を出力することが有効です。

これにより、多くの GPU でデプスバッファの最適化が無効になってしまう場合がある点に注意してください。そのため、合理的な理由が認められない場合は Z バッファの値をオーバーライドしないでください。SV_Depth によって発生する費用は、GPU の構造によって異なりますが、全般的に見ると、アルファテストの費用に近いといえます (HLSL でビルトイン関数 clip() を使用)。すべての標準の不透明シェーダーの後にデプスを修正する (例: レンダリング順AlphaTest を使用して) シェーダーをレンダリングするレンダーシェーダー。

デプス出力値は、単精度の float 型でなくてはなりません。

頂点シェーダー出力とフラグメントシェーダー入力

頂点シェーダーは、頂点のクリップの最終的な空間位置を出力する必要があります。それにより、GPU は画面のどこに、どのくらいのデプスでそれをラスタライズするかが分かります。この出力には、SV_POSITION セマンティックと float4 型の出力が必要です。

頂点シェーダーによって作成されるこの他の出力 (“interpolator” または “varying”) は、使用するシェーダーが何を必要とするかによります。頂点シェーダーから出力される値は、レンダリングされた三角形の表面を横切って補完されます。そして、各ピクセルの値は入力値としてフラグメントシェーダーに渡されます。

多くの最新の GPU には、これらの変数にどのようなセマンティクスがあるかは、実際のところ問題ではありません。ただし、古いシステムのいくつか (最もよく知られているのはシェーダーモデル 2 GPU) には、セマンティクスに関して以下のような特別なルールがありました。

  • TEXCOORD0, TEXCOORD1 などは、テクスチャ座標や位置などの任意の高精度のデータを示すために使用されます
  • 頂点出力とフラグメント入力に関する COLOR0COLOR1 は低精度の 0–1 の範囲のデータ (単純な色の値のなど) に使用されます

そのため、最良のクロスプラットフォームの対応として、 セマンティクス上、頂点出力とフラグメント入力を TEXCOORDn セマンティクスにするのが一般的です。

Interpolator の数的制限

頂点からフラグメントシェーダーに情報を渡すために、使用できる Interpolator 変数の総数には制限があります。 制限数は、プラットフォームと GPU によって異なります。 一般的な目安は以下のとおりです。

  • Up to 8 interpolators: Direct3D 11 9.x level (Windows Phone) . Since the interpolator count is limited, but each interpolator can be a 4-component vector, some shaders pack things together to stay within limits. For example, you can pass two texture coordinates in one float4 variable (.xy for one coordinate, .zw for the second coordinate).
  • Interpolator 10 個まで - シェーダーモデル 3.0 (#pragma target 3.0)
  • Interpolator 16 個まで - OpenGL ES 3.0 (Android)、Metal (iOS)
  • Interpolator 32 個まで - Direct3D 10 シェーダーモデル 4.0 (#pragma target 4.0)

特定のターゲットハードウェアに関係なく、パフォーマンス上の理由で、できる限り少ない数の Interpolator を使うほうが一般的によいとされています。

他の特別なセマンティクス

スクリーンスペースのピクセル位置: VPOS

フラグメントシェーダーは、特殊な VPOS セマンティックとしてレンダリングされるピクセルの座標を受け取る事ができます。 この機能は、シェーダーモデル 3.0 から搭載されています。そのため、シェーダーに #pragma target 3.0 コンパイラーディレクティブが必要です。

異なるプラットフォームでは、スクリーンスペース位置入力の基本的な型が異なるため、可搬性を最大にするために、UNITY_VPOS_TYPE 型を使用してください (大抵のプラットフォームでは float4)。

さらに、ピクセル位置のセマンティクスを使用することによって、頂点からフラグメントの構造体にクリップスペース位置 (SV_POSITION) と VPOS の両方を配置することが困難になります。そこで、頂点シェーダーはクリップスペース位置を別の out 変数として出力する必要があります。以下のシェーダーの例を参照してください。

Shader "Unlit/Screen Position"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 3.0

            // この構造体に SV_POSITION がないことに注意
            struct v2f {
                float2 uv : TEXCOORD0;
            };

            v2f vert (
                float4 vertex : POSITION, // 頂点位置の入力
                float2 uv : TEXCOORD0, // テクスチャ座標の入力
                out float4 outpos : SV_POSITION // クリップスペース位置の出力
                )
            {
                v2f o;
                o.uv = uv;
                outpos = UnityObjectToClipPos(vertex);
                return o;
            }

            sampler2D _MainTex;

            fixed4 frag (v2f i, UNITY_VPOS_TYPE screenPos : VPOS) : SV_Target
            {
                // screenPos.xy はピクセルの整数の座標を含みます
                // それを使用して、4×4 のピクセルの
                // レンダリングを行わない碁盤模様を実装します

                // 碁盤模様の 4x4 のピクセルの
                // checker 値は負の値です
                screenPos.xy = floor(screenPos.xy * 0.25) * 0.5;
                float checker = -frac(screenPos.r + screenPos.g);

                // 値が負の場合、HLSL の clip はレンダリングを行いません
                clip(checker);

                // 維持されたピクセルが、テクスチャを読み込み、それを出力します
                fixed4 c = tex2D (_MainTex, i.uv);
                return c;
            }
            ENDCG
        }
    }
}

表面の向き: VFACE

フラグメントシェーダーは、レンダリングされた面がカメラをむいているか、反対を向いているかを示す変数を受け取ることができます。これは、形状を両方向からみられるようにレンダリングするときに役に立ち、しばしば、葉や似たような薄いオブジェクトに使用されます。VFACE セマンティクス入力変数は、正面向きの三角形には正の数を持ち、裏向きの三角形には負の値を持ちます。

この機能は、シェーダーモデル 3.0 以降に加えられています。そのため、シェーダーに #pragma target 3.0 コンパイラーディレクティブが必要です。

Shader "Unlit/Face Orientation"
{
    Properties
    {
        _ColorFront ("Front Color", Color) = (1,0.7,0.7,1)
        _ColorBack ("Back Color", Color) = (0.7,1,0.7,1)
    }
    SubShader
    {
        Pass
        {
            Cull Off // 裏向きのカリングをオフにします

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 3.0

            float4 vert (float4 vertex : POSITION) : SV_POSITION
            {
                return UnityObjectToClipPos(vertex);
            }

            fixed4 _ColorFront;
            fixed4 _ColorBack;

            fixed4 frag (fixed facing : VFACE) : SV_Target
            {
                // VFACE 入力は正面向きでは負の値、
                // 裏向きでは負の値です。その値によって 
                // 2 色のうちの 1 つを出力します。
                return facing > 0 ? _ColorFront : _ColorBack;
            }
            ENDCG
        }
    }
}

上のシェーダーは、Cull ステートを使い、裏側のカリングを無効にします (デフォルトでは、裏側の三角形はまったく描画されません)。ここで、シェーダーを多数のクアッドメッシュに適用し、異なる方向に回転させます。

頂点 ID: SV_VertexID

頂点シェーダーは、“頂点 ID” を符号なし整数として持つ変数を受け取ることができます。これは主に、テクスチャや コンピュートバッファ から追加の頂点ごとのデータをフェッチするのに役立ちます。

この機能は、DX10 (シェーダーモデル 4.0) と GLCore / OpenGL ES 3 以降に搭載されています。そのため、シェーダーに #pragma target 3.5 コンパイラーディレクティブが必要です。

Shader "Unlit/VertexID"
{
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 3.5

            struct v2f {
                fixed4 color : TEXCOORD0;
                float4 pos : SV_POSITION;
            };

            v2f vert (
                float4 vertex : POSITION, // 頂点位置の入力
                uint vid : SV_VertexID // vertex ID、 uint の必要があります
                )
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                // vertex ID に基づいて面白い色を出力します
                float f = (float)vid;
                o.color = half4(sin(f/10),sin(f/100),sin(f/1000),0) * 0.5 + 0.5;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return i.color;
            }
            ENDCG
        }
    }
}
HLSL のシェーダーキーワードの宣言と使用
Cg/HLSL でシェーダープロパティを参照する