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_Target1
, SV_Target2
, など: これらはシェーダーによって加えられた追加の色です。1度に 1つ以上のレンダーターゲットにレンダリングをする (Multiple render targets 法、または MRT) ときに使用されます。 SV_Target0
は SV_Target
と同じです。
通常は、フラグメントシェーダーは Z バッファ値をオーバーライドしません。デフォルト値は標準の三角形のラスタライゼーションから使用されます。ただし、いくつかのエフェクトにおいては、ピクセルごとにカスタムの Z バッファのデプス値を出力することが有効です。
これにより、多くの GPU でデプスバッファの最適化が無効になってしまう場合がある点に注意してください。そのため、合理的な理由が認められない場合は Z バッファの値をオーバーライドしないでください。SV_Depth
によって発生する費用は、GPU の構造によって異なりますが、全般的に見ると、アルファテストの費用に近いといえます (HLSL でビルトイン関数 clip()
を使用)。すべての標準の不透明シェーダーの後にデプスを修正する (例: レンダリング順 で AlphaTest
を使用して) シェーダーをレンダリングするレンダーシェーダー。
デプス出力値は、単精度の float
型でなくてはなりません。
頂点シェーダーは、頂点のクリップの最終的な空間位置を出力する必要があります。それにより、GPU は画面のどこに、どのくらいのデプスでそれをラスタライズするかが分かります。この出力には、SV_POSITION
セマンティックと float4
型の出力が必要です。
頂点シェーダーによって作成されるこの他の出力 (“interpolator” または “varying”) は、使用するシェーダーが何を必要とするかによります。頂点シェーダーから出力される値は、レンダリングされた三角形の表面を横切って補完されます。そして、各ピクセルの値は入力値としてフラグメントシェーダーに渡されます。
多くの最新の GPU には、これらの変数にどのようなセマンティクスがあるかは、実際のところ問題ではありません。ただし、古いシステムのいくつか (最もよく知られているのはシェーダーモデル 2 GPU) には、セマンティクスに関して以下のような特別なルールがありました。
TEXCOORD0
, TEXCOORD1
などは、テクスチャ座標や位置などの任意の高精度のデータを示すために使用されますCOLOR0
と COLOR1
は低精度の 0–1 の範囲のデータ (単純な色の値のなど) に使用されますそのため、最良のクロスプラットフォームの対応として、
セマンティクス上、頂点出力とフラグメント入力を TEXCOORDn
セマンティクスにするのが一般的です。
頂点からフラグメントシェーダーに情報を渡すために、使用できる Interpolator 変数の総数には制限があります。 制限数は、プラットフォームと GPU によって異なります。 一般的な目安は以下のとおりです。
float4
変数に渡すことができます (1 つの座標の .xy、2 番目の座標の .zw
)。#pragma target 3.0
)#pragma target 4.0
)特定のターゲットハードウェアに関係なく、パフォーマンス上の理由で、できる限り少ない数の Interpolator を使うほうが一般的によいとされています。
フラグメントシェーダーは、特殊な 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
セマンティクス入力変数は、正面向きの三角形には正の数を持ち、裏向きの三角形には負の値を持ちます。
この機能は、シェーダーモデル 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” を符号なし整数として持つ変数を受け取ることができます。これは主に、テクスチャや コンピュートバッファ から追加の頂点ごとのデータをフェッチするのに役立ちます。
この機能は、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
}
}
}