Version: 2019.3
言語: 日本語
頂点シェーダーとフラグメントシェーダーの例
Cg/HLSL でシェーダープロパティーを参照する

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

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

以下のページから例をダウンロードできます。Unity プロジェクトの Zip ファイル

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

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

ここに、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 // 1つめのテクスチャ座標の入力
                )
            {
                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 バッファの値をオーバーライドしないでください。The cost incurred by SV_Depth の変更によって被る被害に関しては、GPU の構造によります。しかし、まとめると、だいたいアルファテストの負担に近いといえます (HLSL でビルトイン関数の clip() を使用)。すべての標準の不透明シェーダーのあとにデプスを修正する (例えば レンダリング順AlphaTest を使用して)シェーダーをレンダリングします。

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

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

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

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

現段階で一般的に使用されている GPU の多くは、これらの変数にどんなセマンティクスがあるかなどほとんど関係ありません。ただし、古いシステムのいくつか (最もよく知られているのは Direct3D 9 の shader model 2 GPU) には、セマンティクスに関して以下のような特別なルールがありました。

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

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

頂点シェーダーとフラグメントシェーダーの例 を参照してください。

Interpolator の数的制限

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

  • Interpolator 8 個まで - OpenGL ES 2.0 (iOS / Android)、Direct3D 11 9.x レベル (Windows Phone)、Direct3 9 シェーダーモデル 2.0 (古いPC)。Interpolator の数は限られていますが、各 Interpolator は 4 要素のベクトルにすることもできるため、情報をまとめて制限内に収めることができるシェーダーもあります。例えば、2 つのテクスチャ座標を 1 つの float4 変数 に渡すことができます (1つ目の座標は .xy、2つ目の座標は.zw)。
  • Interpolator 10 個まで - Direct3D 9 シェーダーモデル 3.0 (#pragma target 3.0)
  • Interpolator 16 個まで - OpenGL ES 3.0 (iOS/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 で、Direct3D 9 では float2)。

さらに、ピクセル位置のセマンティクスを使用して、頂点からフラグメントに渡すための構造体にクリップスペース位置 (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
        }
    }
}

上記のシェーダーは、カリング構文を使い、裏向きのカリングを無効にしています (デフォルトでは、裏向きの三角形はまったくレンダリングされません)。ここで、シェーダーを多数の Quad メッシュに応用し、異なる方向に回転させます。

頂点 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
        }
    }
}

(以下のページから例をダウンロードできます。Unity プロジェクトの Zip ファイル)

頂点シェーダーとフラグメントシェーダーの例
Cg/HLSL でシェーダープロパティーを参照する