ホーム < ゲームつくろー! < DirectX10技術編

その2 板ポリ1枚で描画を知ろう


 前章で初期化作業が終わりました。この章では早速単純な板ポリゴンを表示させてみたいと思います。すべての基本となる部分で、そしてDirectX10特有の難しさもある章です。じっくり行きましょう。



@ DirectX9と全然違うのです!

 DirectX10の描画プロセスはDirectX9とは違いGPUとシェーダを意識させる仕様になっています。その為描画するのに必要な情報がどかっと増えています。DirectX10の描画プロセスを描いてみると次のようになります:


 ぱっと見ると目がくらんでしまいますが(^-^;、整理して追っていけば大丈夫です。

 物凄くざっくり言えば、3Dポリゴンを描画するには「ポリゴン(描画対象)」と「シェーダ(描画の方法)」があれば良い訳です。上の図ではそれを「頂点の情報(上のくくり)」と「シェーダの情報(下のくくり)」の2つに大局で示してあります。

 頂点の情報で一番大切なのはポリゴンの情報が格納されている頂点バッファです。一方描画方法を規定するシェーダの情報はシェーダコードが記載されているシェーダファイルから得ます。シェーダは頂点を入力値としますが、DirectX10からはシェーダの入力が非常に柔軟になった(POSITION等の入力セマンティクスをプログラマが自由に決められるようになった!)ため、その整合性を合わせる「頂点レイアウト(入力レイアウト)」という仲介インターフェイスに双方の情報を集めるようになりました。その頂点レイアウトを描画デバイスに登録すれば、描画時に頂点バッファとシェーダが結びつく事になります。

  頂点の情報、シェーダの情報そしてそれらを結びつける頂点レイアウト。この辺りを念頭に入れて、お茶でもすすってまったりしながら描画周りを見ていく事にしましょう(^-^)



A 頂点構造体を宣言

 最初にする事は、ポリゴンの基盤である頂点(Vertex)を宣言する事です。DirectX9ではこれを構造体で定義していました。DirectX10もこれは同様です。例として頂点位置、頂点カラーを含んだ頂点構造体を作ってみます:

頂点構造体を宣言
struct MyVertex {
   D3DXVECTOR3 Pos;      // 頂点位置
   D3DXVECTOR3 Color;    // 頂点カラー
};

 この構造体はポリゴンの1点を定義するものです。これが3つ集まれば三角ポリゴンになります。「集まる」というのは「配列を作る事」に他なりません:

三角ポリゴン頂点を生成
MyVertex vtx[] = {
   D3DXVECTOR3( 0.0f, 0.5f, 0.5f ), D3DXVECTOR3( 1.0f, 1.0f, 1.0f ),
   D3DXVECTOR3( 0.5f, -0.5f, 0.5f ), D3DXVECTOR3( 1.0f, 1.0f, 1.0f ),
   D3DXVECTOR3( -0.5f, -0.5f, 0.5f), D3DXVECTOR3( 1.0f, 1.0f, 1.0f )
};

この座標はもちろん「ローカル空間」として定義します。


【トピック】
頂点構造体は必要か?
 説明しておいて逆説的な話をするのも何なのですが、頂点の構造体は厳密に言えば必要な物ではありません。頂点バッファは単なるメモリブロックにすぎず、そこにシェーダが認識できる正しい頂点情報が並んでいれば良いだけです。実際モデルを読み込んで表示するまでのプロセスは「モデル内の頂点データをメモリにコピー」→「頂点宣言を作成」となり、その間に頂点構造体を挟む事はありません。
 最近ではより印象的な絵を出すため、一つのモデルを複数のシェーダで流用し複数回描画する事も少なくありません。その際1頂点の構造が上の構造体のように固まっているとかえって扱いにくくなる事があります。そこで座標、カラー、法線など属性別に配列にして各々を別のストリームに通して描画する方法を取る事があります。こうすると例えば座標情報だけ必要な描画などで余計なデータをメモリから読み込んだりそれをシェーダ内で扱うコストが無くなるため、パフォーマンスの向上に繋がります。
 とは言えそれはそれで色々面倒な仕組みを整える必要があるため、まずは頂点構造体を用いて描画の方法を整理して学び、徐々に補助的に使う位の位置付けにしていけば初心者卒業です(^-^)。

 DirectX9の時は、これを頂点バッファ(IDirect3DVertexBuffer9)に格納して描画デバイスに渡せばおしまいでした。その際、どういう頂点の並びであるかを「柔軟な頂点フォーマット(FVF)」でデバイスに教える必要がありました(固定機能描画の場合)。しかし、DirectX10になって固定機能は廃止、その煽りでFVFも無くなってしまいました。それはプログラマが頂点の並びを自由に定義できるようになったからです。それこそ「頂点」という概念が無くなるほど自由になったんです。とは言え、その並びに付いてシェーダに伝えないと描画出来ないのは一緒で、D3D10_INPUT_ELEMENT_DESCがそれを担います。



B D3D10_INPUT_ELEMENT_DESC

 D3D10_INPUT_ELEMENT_DESCは頂点構造体に宣言されている項目の種類やサイズを記述する構造体です:

D3D10_INPUT_ELEMENT_DESC構造体
typedef struct D3D10_INPUT_ELEMENT_DESC {
   LPCSTR                      SemanticName;
   UINT                        SemanticIndex;
   DXGI_FORMAT                 Format;
   UINT                        InputSlot;
   UINT                        AlignedByteOffset;
   D3D10_INPUT_CLASSIFICATION  InputSlotClass;
   UINT                        InstanceDataStepRate;
} D3D10_INPUT_ELEMENT_DESC;

この構造体が示すのは頂点構造体の1つの項目(座標とか法線など)についてです。
SemanticNameは頂点構造体の1つの項目名を文字列で示します。例えば位置であれば「POSITION」、法線であれば「NORMAL」などとなりますが、この名前は基本的に自由でしてIN_POSITIONとかXYZ_NORMAL等としても構いません。この名前は後でシェーダの入力セマンティクスの名前と比較され、整合性がチェックされます。
SemanticIndexは同名のSemanticNameを識別するIDです。例えばテクスチャ座標を表す「TEXCOORD」は複数宣言できるためこのIDで区別する必要があります。これにより名前は「TEXCORRD0」などと解釈されます。
Formatは項目のフォーマット(整数、浮動小数点など)をDXGI_FORMAT列挙型で指定します。先の例の頂点位置はD3DXVECTOR3でしてfloat型が3要素あります。この場合はDXGI_FORMAT_R32G32B32_FLOATを指定します。指定できるフラグはDXGI_FORMAT列挙型で示されています。
InputSlotには項目情報を送り込むスロットを指定します。スロットというのは1つの頂点構造体の情報をシェーダに並列に流し込む入り口のようなものです。例えば頂点構造体が位置と色を持っている場合、スロット0番にこれらをセットすると、0番のスロットからは位置と色の2つの情報がシェーダに流れ込みます。別の頂点構造体で法線を定義し、スロット1番にそれをセットすると、頂点シェーダには位置・色・法線の情報が入力されるわけです。面白い仕組みです。DirectX10では16個のスロットが用意されています。
AlignedByteOffsetは頂点構造体の先頭からこの項目までのオフセットバイト値を入れます。位置と色を宣言した場合、位置は先頭なので0オフセット、色は先頭から見てsizeof(float*3)もしくはsizeof(D3DXVECTOR3)のオフセットとなります。これを間違うと描画がおかしくなるので慎重に。
InputSlotClassにはこの項目が頂点情報であるかインスタンス情報であるかをD3D10_INPUT_CLASSIFICATION列挙型で指定します。今回は頂点ですからD3D10_INPUT_PER_VERTEX_DATAとなります。機械的に指定して良い部分です。
InstanceDataStepRateは一度に描画するインスタンスの数を指定します。今回のように頂点情報を記述する場合は0にしなければなりません。


 この構造体を使って先のMyVertex構造体の情報を表すと次のようになります:

頂点要素の記述
D3D10_INPUT_ELEMENT_DESC MyVertexDesc[] = {
  { "IN_POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }
  { "IN_COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, sizeof(D3DXVECTOR3), D3D10_INPUT_PER_VERTEX_DATA, 0 }
};


 セマンティクスの情報はあえてプログラマ独自の指定にしてみました。この情報は後々「入力レイアウト」というものを作る時に使います。続いて頂点バッファを作成します。 



C 頂点バッファの作成

 DirectX9の時は頂点バッファは頂点バッファ(IDirect3DVertexBuffer9)、インデックスバッファはインデックスバッファ(IDirect3DIndexBuffer9)と、各バッファは明確に分かれていました。DirectX10になって「頂点バッファもインデックスバッファも大きいメモリブロックを使うという点で一緒だからまとめてバッファで良くね?」という発想になり、ID3D10Bufferというインターフェイスで共通表現されるようになりました。その為作るバッファがどういう使用目的であるのかを記述する必要が生まれました。

 ID3D10Bufferを頂点バッファとして使用する場合、まずそれをバッファ情報としてD3D10_BUFFER_DESC構造体に登録します:

D3D10_BUFFER_DESC構造体
typedef struct D3D10_BUFFER_DESC {
   UINT         ByteWidth;
   D3D10_USAGE  Usage;
   UINT         BindFlags;
   UINT         CPUAccessFlags;
   UINT         MiscFlags;
} D3D10_BUFFER_DESC;

ByteWidthには作成するバッファのサイズを指定します。これは純粋に頂点の数×1頂点のサイズです。
Usageにはこの頂点バッファがGPUやCPUからどのように使われるか(アクセスされるか)をフラグ指定します。リードオンリーにしたりGPUだけに許可を与えたりなどができますが、特段理由が無ければD3D10_USAGE_DEFAULTを指定すればOKです。
BindFlagsにはこのバッファがどういう種類のバッファであるかを指定します。頂点バッファであればD3D10_BIND_VERTEX_BUFFER、インデックスバッファであればD3D10_BIND_INDEX_BUFFERなどと項目が決まっています。今回は頂点バッファなのでD3D10_BIND_VERTEX_BUFFERです。
CPUAccessFlagsにはバッファに対するCPUのアクセス権限を指定します。書き込みを許可するならばD3D10_CPU_ACCESS_WRITE、読み込みを許可するならばD3D10_CPU_ACCESS_READを指定します。CPUからのアクセスを許可しない場合は0を指定します。
MiscFlagsはオプションです。Miscellaneous(雑多な)という意味の通りで特に理由が無ければ0でOKです。

 典型的な設定は例えば次のようになります:

D3D10_BUFFER_DESC構造体の設定例
D3D10_BUFFER_DESC BufferDesc;
BufferDesc.ByteWidth = 3 * sizeof( MyVertex );
BufferDesc.Usage = D3D10_USAGE_DEFAULT;
BufferDesc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
BufferDesc.CPUAccessFlags = 0;
BufferDesc.MiscFlags = 0;


 MyVertexで表現される頂点を3点登録できる頂点バッファを想定しています。

 上のはバッファの種類を指定したようなものです。実際のバッファデータは次のD3D10_SUBRESOURCE_DATA構造体に別途登録する必要があります:

D3D10_SUBRESOURCE_DATA
typedef struct D3D10_SUBRESOURCE_DATA {
   const void *pSysMem;
   UINT       SysMemPitch;
   UINT       SysMemSlicePitch;
} D3D10_SUBRESOURCE_DATA;

pSysMemにはリソースとなるメモリブロックへのポインタを渡します。今回の場合ここには頂点配列の先頭ポインタ(vtx)が来ます。
SysMemPitchはりソースがテクスチャの場合にだけ使われる値です。これについて触りだけ説明しますと、DirectX10ではテクスチャリソースは「配列」として扱われます。ここで与える値はテクスチャ配列に登録された1枚のテクスチャのサイズです。要はこれでシステムがn番目の配列にアクセスするための頭出しが出来るようになるわけです。頂点リソースである場合、この値は無視されます。
SysMemSlicePitchはデプスレベルの大きさを与えます。デプスレベルというのは3Dテクスチャに関連する値です。頂点リソースの場合この値も無視されます。


 結局頂点リソースと認識させる場合はpSysMemに頂点vtxを与えればよいだけです:

D3D10_SUBRESOURCE_DATAの設定
D3D10_SUBRESOURCE_DATA SubresourceData;
SubresourceData.pSysMem = vtx;


 D3D10_BUFFER_DESCとD3D10_SUBRESOURCE_DATAを揃えれば頂点バッファ用のID3D10Bufferを作成できます。バッファを確保するにはID3D10Device::CreateBufferメソッドを用います:

頂点バッファの作成
ID3D10Buffer *pBuffer;
if( FAILED( pDev->CreateBuffer( &BufferDesc, &SubresourceData, &pBuffer ) ) )
   return false;

 第1引数に頂点バッファの能力情報であるBufferDesc、第2引数に頂点へのポインタを保持しているSubresourceDataを与えます。すると第3引数のpBufferに生成された頂点バッファが返ってきます。

 生成した頂点バッファは描画時にID3D10Device::IASetVertexBuffersメソッドでデバイスに教えることになります:

頂点バッファを描画デバイスに教える
UINT stride = sizeof( MyVertex );
UINT offset = 0;
pDev->IASetVertexBuffers( 0, 1, &pBuffer, &stride, &offset );

 第1引数には使い始めのスロット番号を与えます。普通は0です。特殊なスロットの使い方(3,4,5番を使う)などの場合は最初のスロットを指定します。第2引数には「頂点バッファ配列」の要素数を与えます。DirectX10は複数の頂点バッファを配列として一気に登録する事ができます。今回は1バッファしかないので1を入れました。第3引数に頂点バッファ配列の先頭ポインタ、第4引数には第3引数で与えた頂点バッファ配列のそれぞれに対応するD3D10_INPUT_ELEMENT_DESC構造体のサイズが入った配列へのポインタを与えます。これはストライド(読み込み単位)として扱われるわけです。最後のoffsetには頂点バッファ配列の各頂点バッファの頭出しをするオフセット値の配列を与えます。頂点バッファによっては頂点情報の前にヘッダー情報などが入る場合があります。その場合、頂点情報の先頭までのオフセット値をここで与えます。

 色々細かくストライドを制御できるわけですが、特殊な事をしないのならば上のような設定で構いません。頂点バッファ、ストライドそしてオフセットはすべて配列でドン!っと与える事ができる点に注目です。



D プリミティブタイプをデバイスに伝える

 頂点をデバイスに教えた後は、その頂点の並び順が何を意味するかを教えます。例えば頂点が3つであってもラインポリゴンかもしれませんし、点ポリゴンかもしれません。それが三角ポリゴンであるなら、それを明確に描画デバイスに示す必要があるわけです。

 そのような「プリミティブ」を示すにはID3D10Device::IASetPrimitiveTopologyメソッドを使います:

頂点バッファを描画デバイスに教える
pDev->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST );

プリミティブタイプはD3D10_PRIMITIVE_TOPOLOGY列挙型にあります。多くはDirectX9と同じですが「Adjacency(隣接)」というややこしいのもあります。上の例では単純な三角形リストを指定しています。他のトポロジータイプについてはマニュアル(https://docs.microsoft.com/ja-jp/windows/desktop/direct3d11/d3d10-graphics-programming-guide-primitive-topologies など)をご参照下さい。いずれもそのタイプに従った頂点の並びになっていればOKです。



E シェーダ側の設定

 頂点については一先ず情報を揃えました。続いて描画方法を担うシェーダの設定に移ります。

 シェーダを扱う選択肢として2つの通り道があります。一つはd3d10.libの範囲内でやりとりする方法です。これは細かく制御ができる反面やや煩雑な作業が必要になります。もう一つはd3dx10.libの拡張機能を用いる方法です。こちらの方法は最初の方法の持つ煩雑さを隠ぺいしてくれています。ここでは後者の方法を説明します。

 まずコードをご覧下さい:

シェーダ生成
D3D10_PASS_DESC PassDesc;
ID3D10Effect *pEffect;
ID3D10EffectTechnique *pTechnique;
ID3D10EffectPass *pPass;

// シェーダファイルからエフェクトを作る
if( FAILED( D3DX10CreateEffectFromFile( "MyShader.fx", NULL, NULL,
      "fx_4_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, pDev, NULL, NULL, &pEffect, NULL, NULL ) ) )
return FALSE;

// エフェクトからテクニック情報を取得
pTechnique = pEffect->GetTechniqueByName( "SimpleRender" );

// テクニックからパス情報を取得
pPass = pTechnique->GetPassByIndex( 0 );
pPass->GetDesc( &PassDesc );

 シェーダは通常外部ファイルに記述されています。そのシェーダファイルをD3DX10CreateEffectFromFile関数で読み込むと内部でコンパイルが走り、最終的にID3D10Effectインターフェイスを取得できます。この関数で大切なのは上の例のNULLでない部分で、この設定が典型です。

 作成したID3D10Effectから、次にそこに含まれるテクニックを取得します。これはIDでも名前でも取得できます。テクニックはある描画完了するために必要な描画パスが複数格納されています。取得したテクニックインターフェイスに含まれる複数パスはID3D10EffectTechnique::GetPassByIndexメソッド(もしくはGetPassByNameメソッド)を通しパスインターフェイス(ID3D10EffectPass)として得る事ができます。その能力はID3D10EffectPass::GetDescメソッドで取得します。

 実際に描画を行うにはパスを描画デバイスに教える必要があります。これはID3D10EffectPass::Applyメソッドを用います。これについては後述致します。



F 頂点レイアウトで頂点バッファとシェーダを擦り合わせ

 これで必要な物が揃った感じもするのですが、DirectX10にはもう1つやる事が残っています。先にも説明しましたが、DirectX10では頂点が強烈に柔軟になりました。そのためプログラマが定義した頂点構造体とシェーダが必要とする情報とを擦り合わせる必要が出てきました:


 この整合性チェックを行い描画デバイスによる描画を保証するのが「頂点レイアウト(入力レイアウト)」です。入力側のセマンティクス名や型、入力値の種類など(上図の青い方)に対して受け取り側であるシェーダ変数(黄色い方)が対応していれば描画を回せます。

 頂点レイアウトが必要とするのは入力情報である「プログラマが設計した頂点宣言(D3D10_INPUT_ELEMENT_DESC)」と受け取り側である「シェーダのパスの情報(D3D10_PASS_DESC)」です。これらの情報はここまでですでに揃っていますので、そこから頂点レイアウトインターフェイスを取得します:

頂点レイアウトを作成
ID3D10InputLayout *pVertexLayout;

// 頂点レイアウトを作成
if( FAILED( pDev->CreateInputLayout(
      MyVertexDesc, sizeof(MyVertexDesc)/sizeof(D3D10_INPUT_ELEMENT_DESC),
      PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize, &pVertexLayout ) ) )
   return FALSE;

// 頂点レイアウトを描画デバイスにセット
pDev->IASetInputLayout( pVertexLayout );

 頂点レイアウトインターフェイスを得るにはID3D10Device::CreateInputLayoutメソッドを用います:

ID3D10Device::CreateInputLayoutメソッド
HRESULT CreateInputLayout(
   const D3D10_INPUT_ELEMENT_DESC  *pInputElementDescs,
   UINT                            NumElements,
   const void                      *pShaderBytecodeWithInputSignature,
   SIZE_T                          BytecodeLength,
   ID3D10InputLayout               **ppInputLayout
);

pInputElementDescsはBで説明した頂点情報を渡します。
NumElementsはその要素数です。
pShaderBytecodeWithInputSignatureにはコンパイル済みのシェーダコードへのポインタを渡します。これはすでにエフェクトインターフェイスを生成した時にできていて、上のプログラムのようにパス情報に含まれています。機械的に書ける部分です。
BytecodeLengthはコンパイル済みシェーダコードの長さですが、これも同様にパス情報に含まれていますので機械的に書けます。
メソッドが成功するとppInputLayoutに頂点レイアウトが作成されます。

 後は描画前にこの頂点レイアウトをID3D10Device::IASetInputLayoutメソッドに渡し、描画デバイスに頂点情報とシェーダに偽りが無い事を教えてあげれば完了です:

頂点レイアウトをデバイスに通知
pDev->IASetInputLayout( pVertexLayout );

 さてこれでようやくプログラム側の準備は整いました。しかし、この章の目的は板ポリゴンを描画する事。そのためには、シェーダプログラムを書かなければなりません。もうひと頑張りです(^-^;



F とりあえず表示だけするシェーダ

 固定機能パイプラインが廃止された事に伴い、DirectX10ではGPUのプログラムであるシェーダを自前で用意する必要があります。シェーダについては今後みっちりと出てくるわけですが、この章では板ポリゴンをただただ表示させる事が目的であるため、必要最低限のシェーダプログラムに留めます。

 DirectX10には3タイプのシェーダ(頂点シェーダ、ジオメトリシェーダ、ピクセルシェーダ)があります。このうち頂点シェーダとピクセルシェーダは必須です。ジオメトリシェーダはオプション扱いでして、無くても描画プログラムは動きます。

 頂点シェーダでは頂点を「射影空間」に変換するのが主な目的です(他の用途も沢山あります)。その変換された頂点はジオメトリシェーダに送られます。ジオメトリシェーダ内でごにょごにょした頂点はそこでお役御免。続く「ラスタライザ」という仕事によって1枚のポリゴンを塗る作業に移ります。これを担うのがピクセルシェーダです。

 ここでは表示を目的としますので、DirectX10のチュートリアル(Tutorial 3: Shaders and Effect System)にある簡易シェーダをそのまま引用します:

シェーダプログラム(MyShader.fx)
// 頂点シェーダ
float4 VS( float4 Pos : IN_POSITION ) : SV_POSITION
{
   return Pos;
}

// ピクセルシェーダ
float4 PS( float4 Pos : SV_POSITION ) : SV_Target
{
   // 黄色い色で塗る
   return float4( 1.0f, 1.0f, 0.0f, 1.0f );
}

// テクニック
technique10 SimpleRender
{
   pass P0
   {
      SetVertexShader( CompileShader( vs_4_0, VS() ) );
      SetGeometryShader( NULL );
      SetPixelShader( CompileShader( ps_4_0, PS() ) );
   }
}

 このシェーダプログラムは入力されてきた頂点をそのままスルーして、ジオメトリシェーダは使わず、ラスタライズ後のピクセルシェーダで黄色い色を出力して終わる大変にシンプルなソースになっています。DirectX9のシェーダプログラムと殆ど一緒ですが、頂点シェーダの入力セマンティクスがIN_POSITIONになっています。これは私が適当に付けたセマンティクスで、正しくはD3D10_INPUT_ELEMENT_DESC構造体に記述し入力レイアウトを作成するときに渡したものです。エフェクトパスであるID3D10EffectPass::GetDescメソッドで得られるセマンティクス名はこのシェーダ内の記述を元にしています。
 プログラマが独自のセマンティクスをいくらでも作れると言うのは凄い柔軟な仕様です。ただ頂点シェーダの出力はある程度決められています。SV_POSITIONという座標セマンティクスが頂点シェーダの出力として必須です。ピクセルシェーダでそのセマンティクスを受け取っている点に注目してください。
 テクニック宣言もtechnique10となり、またパス内のシェーダ宣言方法もDirectX9の時とは違っています。良く分からんという方は、とりあえずこんなもんだと思っていただいて大丈夫です。シェーダについては後の章できっと山ほど出てきます。



G さぁ描画だ!

 ようやく描画です。描画自体はDirectX9のエフェクトによる描画と殆ど一緒ですが、特筆すべきDirectX9時代から大きく変わった部分があります。嬉しいのかどうなのか、描画から「Begin」と「End」が消えました!これまでBeginとEndの間でなければ描画出来なかった垣根が取っ払われました。

 描画の手始めは、先にエフェクトから取得したID3D10EffectTechniqueインターフェイスの能力を取得します:

テクニック情報を取得
D3D10_TECHNIQUE_DESC techDesc;
pTechnique->GetDesc( &techDesc );

D3D10_TECHNIQUE_DESCに含まれているパスの数が描画のために必要になります。続いてforループで1パス単位で描画を行います。これはコードを見た方が早いでしょう:

1パスごと描画
for( UINT p = 0; p < techDesc.Passes; ++p )
{
   pTechnique->GetPassByIndex( p )->Apply(0);
   pDev->Draw( 3, 0 );
}

 BeginとEndが無くなった事で描画コードが相当にシンプルになりました。ループ内のID3D10EffectTechnique::GetPassByIndexメソッドはパス番号(インデックス)から該当するID3D10EffectPassインターフェイスを取得するメソッドです。さらにID3D10EffectPass::ApplyメソッドはそのパスをGPU側にセットします。引数の0はおまじないです(マニュアルにはUnusedとありました)。ここまでは殆ど固定的な部分です。続いて描画デバイスのDrawメソッドを呼び出します:

ID3D10Device::Drawメソッド
void Draw(
   UINT VertexCount,
   UINT StartVertexLocation
);

VertexCountはこれから描画しようとする頂点の数を設定します。上の例だと三角形ポリゴン1枚なので「3」になっています。ちなみにこれはIASetVertexBuffersで設定した頂点バッファに準拠しますので注意です。
StartVertexLocationは1つの頂点バッファに設定した頂点の描き始め位置を設定します。頂点を途中から使いたい場合などに設定しますが、頂点バッファ全部を描画するのであれば0にします。

 ID3D10Device::Drawメソッドは頂点バッファのみで描画する一番シンプルな方法です。もしインデックスバッファを用いるのであればID3D10Device::IASetIndexBufferメソッドにインデックスバッファを渡しID3D10Device::DrawIndexedメソッドを呼び出して描画します。



 後はスワップチェインにバックバッファをフリップしてもらえば、黄色い三角ポリゴンがで〜んと表示されているはずです。それにしてもDirectX10では1枚の板ポリゴンを表示させるだけでもどえらく大変になってしまいました。これは固定機能パイプラインを無くしてプログラマに描画の自由度を与えたためです。次世代を感じさせますね。この仕様は最初はかなり難しく感じてしまうかもしれませんが、何度かコードを書いているうちに頭の中でシステムが整理されてきてすんなり受け入れられるようになります(^-^;

 この章を踏まえたサンプルプログラムを用意しましたので、興味のある方は眺めてみてください。