チュートリアル 13:ジオメトリ シェーダー
まとめ
このチュートリアルでは、グラフィック パイプラインの、前のチュートリアルでは説明していない部分について説明します。いくつかの基本的なジオメトリ シェーダーの機能については、既に説明してあります。
このチュートリアルでは、結果として、モデルによって、そのモデルから 2 つ目のレイヤーが押し出されます。元のモデルがまだ中央に存在することに注目してください。
Source
SDK ルート\Samples\C++\Direct3D10\Tutorials\Tutorial13
ジオメトリ シェーダー
ジオメトリ シェーダー (GS) の利点は、プリミティブ単位でメッシュを操作できることです。各頂点で個別に計算を実行する代わりに、プリミティブ単位で処理するためのオプションがあります。つまり、頂点を単一の頂点、線分 (2 つの頂点)、または三角形 (3 つの頂点) として渡すことができます。
プリミティブ単位で操作できることにより、新しいアイデアに取り組むことができ、それを可能にするためにさらにデータにアクセスできるようになります。このチュートリアルでは、面のために法線を計算したことがわかります。3 つの頂点の位置がわかることによって、面の法線を特定できます。
プリミティブ全体へのアクセスが可能になることに加えて、ジオメトリ シェーダーでは、新しいプリミティブを即時に作成できます。以前の Direct3D では、グラフィック パイプラインは既存のコンテンツの操作のみが可能であり、データの増幅または減幅が可能でした。Direct3D 10 のジオメトリ シェーダーでは、単一のプリミティブ (オプションとしてエッジ隣接プリミティブもある) を読み込み、それに基づいて 0 個、1 個、または複数のプリミティブを生成できます。
入力ソースとは異なる種類のジオメトリを生成できます。たとえば、個々の頂点を読み込み、それに基づいて複数の三角形を生成できます。これにより、CPU による介入のない、グラフィック パイプラインにおいて実行されるさまざまな新しいアイデアが可能になります。
ジオメトリ シェーダーは、グラフィック パイプラインにおいて頂点とピクセル シェーダーの間に存在します。ジオメトリ シェーダーによって新しいジオメトリが作成される可能性があるため、それらをピクセル シェーダー (PS) に渡す前に、射影空間へ正しくトランスフォームされるようにしなければなりません。これは、それをジオメトリ シェーダーへ入力する前に頂点シェーダー (VS) によって実行するか、ジオメトリ シェーダー自体の内部で実行することができます。
最後に、GS の出力をバッファーに再送信することができます。大量の三角形を読み込み、新しいジオメトリを生成し、それらを新しいバッファーに格納できます。ただし、ストリーム出力の概念はこのチュートリアルの範囲外となります。その詳細については、SDK に含まれるサンプルを参照してください。
Direct3D 10 SDK に含まれる多くのサンプルでは、ジオメトリ シェーダーで実現できる特定のテクニックが説明されています。基本的なサンプルに取り組む必要がある場合は、ParticlesGS を使用してみてください。ParticlesGS は、GPU での動的なパーティクル システム (パーティクルの作成、爆発、および破棄) 全体をシミュレートおよびレンダリングします。
ジオメトリ シェーダーの初期化
VS や PS と異なり、ジオメトリ シェーダーは、入力ごとに決まった数の出力を生成するとは限りません。そのため、シェーダーを宣言する形式は、その他の 2 つとは異なります。
最初のパラメーターは、maxvertexcount です。このパラメーターは、シェーダーが実行されるたびに出力できる頂点の最大数を示します。これに、ジオメトリ シェーダーの名前が続きます。それらは、適切に GS と名付けられています。
関数名の後は、その関数に渡されるパラメーターが続きます。1 つ目には、キーワード トライアングルが含まれます。これは、GS によって、入力されるとおりに三角形が処理されることを示します。その後に、頂点フォーマット、および識別子 (配列のサイズを表す数値、三角形の場合は 3、線分の場合は 2 が付く) が続きます。2 つ目のパラメーターは、出力形式とストリームです。TriangleStream は、出力が三角形内にあり (正確にはトライアングル ストリップ)、次に、その形式が角かっこで指定されることを示します。最後に、そのストリームの識別子が示されます。
[maxvertexcount(3)]
void GS( triangle GSPS_INPUT input[3], inout TriangleStream<GSPS_INPUT> TriStream )
TriangleStream への頂点の生成が開始される場合は、それらがすべてストリップとしてリンクされていることが予想されますます。ストリップを終了するには、そのストリーム内で RestartStrip を呼び出します。トライアングル リストを作成するには、各三角形の後に必ず RestartStrip を呼び出す必要があります。
モデルの爆発
このチュートリアルでは、ジオメトリ シェーダーの基本的な機能について説明します。これを示すために、モデル上で爆発エフェクトを作成します。このエフェクトは、三角形の法線の方向に各頂点を押し出すことによって作成されます。
その法線ごとに各頂点を押し出すために、以前のチュートリアルで同様のエフェクトが実装されており、それらが膨らみスライダーによって制御されることに注意してください。このチュートリアルでは、面の法線を生成するために、三角形のすべての情報の使用方法を説明します。面の法線の使用に関する違いは、爆発した三角形の間にすき間が見られることです。さまざまな三角形で頂点が共有されるため、実際には、それらはジオメトリ シェーダーに 2 回渡されます。また、頂点とは対照的に、毎回、三角形の法線においてそれが押し出されるので、2 つの最後の頂点は異なる位置で終わる可能性があります。
面の法線の計算
面の法線を計算するには、まず、面にある 2 つのベクトルが必要となります。三角形を指定されたので、関連するベクトルを取得するために、三角形の任意の 2 つの頂点を差し引くことができます。ベクトルを取得したら、法線を取得するために外積を計算します。後で法線をスケーリングするため、その法線を正規化する必要もあります。
//
// Calculate the face normal
//
float3 faceEdgeA = input[1].Pos - input[0].Pos;
float3 faceEdgeB = input[2].Pos - input[0].Pos;
float3 faceNormal = normalize( cross(faceEdgeA, faceEdgeB) );
面の法線を取得したら、その方向に三角形の各点を押し出すことができます。これを行うには、3 回ステップ スルーして各頂点を処理するループを使用します。頂点の位置は、係数で乗算された法線によって押し出されます。次に、頂点シェーダーでは頂点を正しい射影空間にトランスフォームしていないため、ジオメトリ シェーダーでそれを行う必要があります。最後に、残りのデータをまとめたら、この新しい頂点を TriangleStream に追加できます。
for( int v=0; v<3; v++ )
{
output.Pos = input[v].Pos + float4(faceNormal*Explode,0);
output.Pos = mul( output.Pos, View );
output.Pos = mul( output.Pos, Projection );
output.Norm = input[v].Norm;
output.Tex = input[v].Tex;
TriStream.Append( output );
}
3 つの頂点が生成されたら、ストリップを切り取り、再起動します。このチュートリアルでは、各三角形を別々に押し出す必要があるため、トライアングル リストで終わります。
TriStream.RestartStrip();
この新しいトライアングル ストリームは、次に、ピクセル シェーダーに送られます。そこで、このデータを処理し、それをレンダー ターゲットに描画します。