日別アーカイブ: 2007年10月5日

Direct3D 10 Shader4.0 ジオメトリシェーダーで QUADLIST の描画

描画するプリミティブの形状は通常 IASetPrimitiveTopology() で
指定します。D3D10/DX10 では次の 9種類が定義されています。

D3D10_PRIMITIVE_TOPOLOGY_POINTLIST  // 点
D3D10_PRIMITIVE_TOPOLOGY_LINELIST   // 線
D3D10_PRIMITIVE_TOPOLOGY_LINESTRIP  // 連続線
D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST  // 三角形
D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP // 連続三角形
D3D10_PRIMITIVE_TOPOLOGY_LINELIST_ADJ
D3D10_PRIMITIVE_TOPOLOGY_LINESTRIP_ADJ
D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ
D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP_ADJ

ところがジオメトリシェーダーを使っている場合、
IASetPrimitiveTopology() の設定値は実際に描画する
プリミティブ形状と連動していません。

というのは、GeometryShader の出力プリミティブの種類はシェーダー
側で再定義されるからです。

PointSprite がその良い例で、入力 Topology は PointList でも
実際に描画される形状は TriangleStrip の板ポリゴンになっています。

つまりジオメトリシェーダーを使う場合の IASetPrimitiveTopology()
の設定値は、

・頂点ストリームを一度に読み進める量
・GeometryShader へ同時に入力されるパラメータ数

の決定にだけ使われると考えられます。

そこでジオメトリシェーダーへの入力頂点数にだけ注目してまとめると
次のようになります。

同時 1 頂点 POINTLIST
同時 2 頂点 LINELIST
同時 3 頂点 TRIANGLELIST
同時 4 頂点 LINELIST_ADJ
同時 6 頂点 TRIANGLELIST_ADJ

同時 5頂点が無いのが残念ですが、ほぼ 1~6 頂点までの汎用的な入力
頂点数として流用することが可能です。この考え方を使って
4頂点プリミティブや 6頂点プリミティブを定義することができます。

実際に 4角ポリゴン でモデルを作って描画してみました。
いわゆる QUADLIST 相当です。(D3DPT_QUADLIST ?)

ss07

いつものように、実行ファイル、ソース、シェーダー込みでダウンロード
できます。

モデルデータ側は四角形のあつまりなので、index list は 4つで
1ポリゴンとなります。

// torus.inc
static unsigned short _Index[]= {
    0, 1, 2, 3,	// 四角ポリゴン1
    4, 0, 3, 5,	// 四角ポリゴン2
    6, 4, 5, 7,	// 四角ポリゴン3
    8, 6, 7, 9,	// 四角ポリゴン4
      :

描画命令の発行は 同時4頂点入力の LINELIST_ADJ を使います。

// 描画 (ss07.cpp)
iDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_LINELIST_ADJ );
iDevice->IASetInputLayout( iInputLayout );
UINT	vsize= sizeof(VertexType);
UINT	voffset= 0;
iDevice->IASetVertexBuffers( 0, 1, &iVertexBuffer, &vsize, &voffset );
iDevice->IASetIndexBuffer( iIndexBuffer, DXGI_FORMAT_R16_UINT, 0 );

iEffectModel->GetTechniqueByName( "Main" )->GetPassByIndex( 0 )->Apply( 0 );
iDevice->DrawIndexedInstanced( IndexCount, 16, 0, 0, 0 );

無駄に凝ったことをしているので DrawIndexedInstance() になってますが、
LINELIST_ADJ 以外はごく普通の描画コードです。
DrawIndexed~() に渡す値もそのまま頂点数 (Index) の個数になっています。
もし1枚だけ描くなら 4頂点です。

// 1枚だけ描く場合
iDevice->DrawIndexed( 4, 0, 0 );

VertexShader も PixelShader も通常の描画と全く同じものです。
ジオメトリシェーダーは次のとおりです。

typedef VS_OUTPUT   GS_OUTPUT;

[maxvertexcount(4)]
void GS_Main( lineadj GS_OUTPUT In[4],
		inout TriangleStream gsstream )
{
    gsstream.Append( In[0] );
    gsstream.Append( In[1] );
    gsstream.Append( In[3] );
    gsstream.Append( In[2] );
    gsstream.RestartStrip();
}

lineadj で 4頂点入力、TriangleStrip で四角形を描画します。
これで QUADLIST の描画が可能となります。

ファイルはこちらです。
wheelhandle_ss07t.zip