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

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

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

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

The main vertex shader function (indicated by the #pragma vertex directive) needs to have semantics on all the input parameters. These correspond to individual Mesh data elements, like vertex position, normal mesh, and texture coordinates. See vertex program inputs for more details.

ここに、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
        }
    }
}

Instead of spelling out all individual inputs one by one, it’s also possible to declare a structure of them, and indicate semantics on each individual member variable of the struct.

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

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

fixed4 frag (v2f i) : SV_Target

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

It’s also possible to return a structure with the outputs. The fragment shader above could be rewritten this way too, and it would do exactly the same:

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

Usually the fragment shader doesn’t override the Z buffer value, and a default value is used from the regular triangle rasterization. However, for some effects it’s useful to output custom Z buffer depth values per pixel.

Note that on many GPUs this turns off some depth buffer optimizations, so don’t override Z buffer value without a good reason. The cost incurred by SV_Depth varies depending on the GPU architecture, but overall it’s fairly similar to the cost of alpha testing (using the built-in clip() function in HLSL). Render shaders that modify depth after all regular opaque shaders (for example, by using the AlphaTest rendering queue.

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

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

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

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

Many modern GPUs don’t really care what semantics these variables have; however some old systems (most notably, shader model 2 GPUs) did have special rules about the semantics:

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

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

Interpolator の数的制限

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

  • Up to 8 interpolators: OpenGL ES 2.0 (Android), 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).
  • Up to 10 interpolators: Shader model 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)

Regardless of your particular target hardware, it’s generally a good idea to use as few interpolators as possible for performance reasons.

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

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

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

On different platforms the underlying type of the screen space position input varies, so for maximum portability use the UNITY_VPOS_TYPE type for it, which is float4 on most platforms.

Additionally, using the pixel position semantic makes it hard to have both the clip space position (SV_POSITION) and VPOS in the same vertex-to-fragment structure. Therefore, the vertex shader should output the clip space position as a separate “out” variable. See the example shader below:

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

The shader above uses the Cull state to disable back-face culling (by default back-facing triangles aren’t rendered at all). Here is the shader applied to a bunch of Quad meshes, rotated at different orientations:

頂点 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 でシェーダープロパティを参照する