ストリーム出力ステージの使用開始
このセクションでは、ストリーム出力ステージでジオメトリ シェーダーを使用する方法について説明します。
ジオメトリ シェーダーをコンパイルする
このジオメトリ シェーダー (GS) は、三角形ごとに面法線を計算し、位置、法線、テクスチャ座標データを出力します。
struct GSPS_INPUT
{
float4 Pos : SV_POSITION;
float3 Norm : TEXCOORD0;
float2 Tex : TEXCOORD1;
};
[maxvertexcount(12)]
void GS( triangle GSPS_INPUT input[3], inout TriangleStream<GSPS_INPUT> TriStream )
{
GSPS_INPUT output;
//
// 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) );
float3 ExplodeAmt = faceNormal*Explode;
//
// Calculate the face center
//
float3 centerPos = (input[0].Pos.xyz + input[1].Pos.xyz + input[2].Pos.xyz)/3.0;
float2 centerTex = (input[0].Tex + input[1].Tex + input[2].Tex)/3.0;
centerPos += faceNormal*Explode;
//
// Output the pyramid
//
for( int i=0; i<3; i++ )
{
output.Pos = input[i].Pos + float4(ExplodeAmt,0);
output.Pos = mul( output.Pos, View );
output.Pos = mul( output.Pos, Projection );
output.Norm = input[i].Norm;
output.Tex = input[i].Tex;
TriStream.Append( output );
int iNext = (i+1)%3;
output.Pos = input[iNext].Pos + float4(ExplodeAmt,0);
output.Pos = mul( output.Pos, View );
output.Pos = mul( output.Pos, Projection );
output.Norm = input[iNext].Norm;
output.Tex = input[iNext].Tex;
TriStream.Append( output );
output.Pos = float4(centerPos,1) + float4(ExplodeAmt,0);
output.Pos = mul( output.Pos, View );
output.Pos = mul( output.Pos, Projection );
output.Norm = faceNormal;
output.Tex = centerTex;
TriStream.Append( output );
TriStream.RestartStrip();
}
for( int i=2; i>=0; i-- )
{
output.Pos = input[i].Pos + float4(ExplodeAmt,0);
output.Pos = mul( output.Pos, View );
output.Pos = mul( output.Pos, Projection );
output.Norm = -input[i].Norm;
output.Tex = input[i].Tex;
TriStream.Append( output );
}
TriStream.RestartStrip();
}
そのコードを念頭に置いて、ジオメトリ シェーダーは頂点シェーダーまたはピクセル シェーダーとよく似ていますが、次の例外を除いて、関数によって返される型、入力パラメーター宣言、および組み込み関数を考慮してください。
Item | 説明 | |
---|---|---|
関数の戻り値の型 |
関数の戻り値の型は 1 つの処理を行い、シェーダーによって出力できる頂点の最大数を宣言します。 この場合、
は、最大 12 個の頂点に出力を定義します。 |
|
入力パラメーター宣言 |
この関数は、次の 2 つの入力パラメーターを受け取ります。
最初のパラメーターは、GSPS_INPUT 構造体 (位置、法線、テクスチャ座標として頂点ごとのデータを定義する) によって定義される頂点の配列 (この場合は 3) です。 最初のパラメーターでは、三角形のキーワードも使用されます。つまり、入力アセンブラー ステージは、三角形のプリミティブ型 (三角形リストまたは三角形ストリップ) の 1 つとしてジオメトリ シェーダーにデータを出力する必要があります。 2 番目のパラメーターは、TriangleStream<GSPS_INPUT> 型で定義された三角形ストリームです。 つまり、パラメーターは三角形の配列であり、それぞれが 3 つの頂点 (GSPS_INPUT のメンバーからのデータを含む) で構成されます。 三角形と三角形のストリームのキーワードを使用して、GS 内の個々の三角形または三角形のストリームを識別します。 |
|
組み込み関数 |
シェーダー関数のコード行では、Append と RestartStrip を呼び出す最後の 2 行を除き、共通シェーダー コア HLSL 組み込み関数が使用されます。 これらの関数は、ジオメトリ シェーダーでのみ使用できます。 Append は、現在のストリップに出力を追加するようにジオメトリ シェーダーに通知します。RestartStrip では、新しいプリミティブ ストリップが作成されます。 GS ステージを呼び出すたびに、新しいストリップが暗黙的に作成されます。 |
残りのシェーダーは、頂点シェーダーまたはピクセル シェーダーによく似ています。 ジオメトリ シェーダーは、構造体を使用して入力パラメーターを宣言し、位置メンバーを SV_POSITION セマンティックでマークして、これが位置データであることをハードウェアに伝えます。 入力構造体は、他の 2 つの入力パラメーターをテクスチャ座標として識別します (そのうちの 1 つに面法線が含まれている場合でも)。 必要に応じて、面法線に独自のカスタム セマンティックを使用できます。
ジオメトリ シェーダーを設計した後、次のコード例に示すように D3DCompile を呼び出してコンパイルします。
DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
ID3DBlob** ppShader;
D3DCompile( pSrcData, sizeof( pSrcData ),
"Tutorial13.fx", NULL, NULL, "GS", "gs_4_0",
dwShaderFlags, 0, &ppShader, NULL );
頂点シェーダーやピクセル シェーダーと同様に、シェーダーをコンパイルする方法 (デバッグ用、速度用に最適化など)、エントリ ポイント関数、および検証対象のシェーダー モデルをコンパイラに伝えるシェーダー フラグが必要です。 この例では、GS 関数を使用して Tutorial13.fx ファイルから構築されたジオメトリ シェーダーを作成します。 シェーダーはシェーダー モデル 4.0 用にコンパイルされます。
ストリーム出力を使用してジオメトリ シェーダー オブジェクトを作成する
ジオメトリからデータをストリーミングし、シェーダーを正常にコンパイルしたら、次の手順は ID3D11Device::CreateGeometryShaderWithStreamOutput を呼び出してジオメトリ シェーダー オブジェクトを作成することです。
ただし、まず、ストリーム出力 (SO) ステージの入力シグネチャを宣言する必要があります。 このシグネチャは、オブジェクト作成時に GS 出力と SO 入力と一致または検証します。 次のコードは、SO 宣言の例です。
D3D11_SO_DECLARATION_ENTRY pDecl[] =
{
// semantic name, semantic index, start component, component count, output slot
{ "SV_POSITION", 0, 0, 4, 0 }, // output all components of position
{ "TEXCOORD0", 0, 0, 3, 0 }, // output the first 3 of the normal
{ "TEXCOORD1", 0, 0, 2, 0 }, // output the first 2 texture coordinates
};
D3D11Device->CreateGeometryShaderWithStreamOut( pShaderBytecode, ShaderBytecodesize, pDecl,
sizeof(pDecl), NULL, 0, 0, NULL, &pStreamOutGS );
この関数は、次のようないくつかのパラメーターを受け取ります。
- コンパイルされたジオメトリ シェーダーへのポインター (ジオメトリ シェーダーが存在せず、頂点シェーダーから直接データがストリーミングされる場合は頂点シェーダー)。 このポインターを取得する方法については、「コンパイル済みシェーダーへのポインターの取得」を参照してください。
- ストリーム出力ステージの入力データを記述する宣言の配列へのポインター。 (D3D11_SO_DECLARATION_ENTRY を参照してください。)SO ステージから出力される要素の種類ごとに 1 つずつ、最大 64 個の宣言を指定できます。 宣言エントリの配列は、ストリーム出力にバインドするバッファーが 1 つだけであるか複数であるかに関係なく、データ レイアウトを記述します。
- SO ステージによって書き出される要素の数。
- 作成されるジオメトリ シェーダー オブジェクトへのポインター (ID3D11GeometryShader を参照)。
この状況では、バッファー ストライドは NULL、ラスタライザーに送信されるストリームのインデックスは 0、クラス リンケージ インターフェイスは NULL です。
ストリーム出力宣言は、データをバッファー リソースに書き込む方法を定義します。 出力宣言には、必要な数のコンポーネントを追加できます。 SO ステージを使用して、1 つのバッファー リソースまたは多数のバッファー リソースに書き込みます。 1 つのバッファーの場合、SO ステージでは、頂点ごとにさまざまな要素を書き込むことができます。 複数のバッファーの場合、SO ステージでは、頂点ごとのデータの 1 つの要素のみを各バッファーに書き込むことができます。
ジオメトリ シェーダーを使用せずに SO ステージを使用するには、ID3D11Device::CreateGeometryShaderWithStreamOutput を呼び出し、頂点シェーダーへのポインターを pShaderBytecode パラメーターに渡します。
出力ターゲットを設定する
最後の手順では、SO ステージ バッファーを設定します。 データは、後で使用するためにメモリ内の 1 つ以上のバッファーにストリーミングできます。 次のコードは、頂点データと、データをストリーミングする SO ステージに使用できる単一のバッファーを作成する方法を示しています。
ID3D11Buffer *m_pBuffer;
int m_nBufferSize = 1000000;
D3D11_BUFFER_DESC bufferDesc =
{
m_nBufferSize,
D3D11_USAGE_DEFAULT,
D3D11_BIND_STREAM_OUTPUT,
0,
0,
0
};
D3D11Device->CreateBuffer( &bufferDesc, NULL, &m_pBuffer );
ID3D11Device::CreateBuffer を呼び出してバッファーを作成します。 この例では、CPU によって頻繁に更新されることが予想されるバッファー リソースの一般的な既定の使用方法を示します。 バインド フラグは、リソースをバインドできるパイプライン ステージを識別します。 SO ステージで使用されるリソースも、バインド フラグ D3D10_BIND_STREAM_OUTPUT を使用して作成する必要があります。
バッファーが正常に作成されたら、ID3D11DeviceContext::SOSetTargets を呼び出して現在のデバイスに設定します。
UINT offset[1] = 0;
D3D11Device->SOSetTargets( 1, &m_pBuffer, offset );
この呼び出しは、バッファーの数、バッファーへのポインター、オフセットの配列 (データの書き込みを開始する場所を示す各バッファーへの 1 つのオフセット) を受け取ります。 描画関数が呼び出されると、これらのストリーミング出力バッファーにデータが書き込まれます。 内部変数は、ストリーミング出力バッファーへのデータの書き込みを開始する位置を追跡し、SOSetTargets が再度呼び出され、新しいオフセット値が指定されるまで、その変数は引き続きインクリメントされます。
ターゲット バッファーに書き出されるすべてのデータは、32 ビット値になります。