ホーム < ゲームつくろー! < DirectX10技術編
その3 板ポリをワールドへ
DirectX10技術編その2でDirectX10の土壌で板ポリゴンを1枚出す方法を見てきました。基本的な部分はその章に集約されています。ただ、示したコードは板ポリゴンを直接スクリーンに出す(射影空間の座標を指定した)ものでして、面白味も何もありません。
やっぱりポリゴンはローカル座標に定義して、それをちゃんとワールドに置きたいわけです。そのためには頂点座標の定義やワールド変換行列の設定の仕方について知る必要があります。
この章では前章の板ポリゴンをローカル座標として定義し直し、ワールド変換行列やビュー行列そして射影変換行列を通してスクリーンへ映す方法を見てみましょう。
@ ローカル座標として頂点を宣言
前章の頂点宣言をもう一度見てみます:
射影空間座標として宣言 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 )
};
この座標はDirectX10のチュートリアルにあったものを流用していますが、これは射影空間を想定しています。チュートリアルなので余計な行列計算を入れたくなかったわけです。
この章ではこの座標を「ローカル空間」にあると捉え直します。
A ワールド・ビュー・射影変換はGPU内で計算
DirectX9の場合、ローカル座標として定義し頂点バッファに格納した頂点を描画デバイスに通知した後、IDirect3DDevice9::SetTransformメソッドを呼び出してワールド・ビュー・射影変換行列をそれぞれ設定していました。これは固定機能パイプラインを使っている状態です。
DirectX10では固定機能パイプラインは廃止されていますので、このメソッド自体存在しません。では行列の変換は何時どこで行うのか?一つの方法としてはメインプログラム上でローカル座標にある頂点を行列演算して返還する手があります。これはCPUの力を使っています。しかし、シェーダプログラムが基本であるDirectX10ではこの流儀は勧められません。
DirectX10では「シェーダにワールド・ビュー・射影変換行列を渡してGPUに計算させる」方法が取られます。なぜか?それは計算スピードがCPUよりも圧倒的に速いからです。行列演算のような並列処理はGPUの大得意分野で、行列演算を超高速に行うための命令セットがちゃんと入っています。それを使うわけです。
B シェーダに行列を渡す
GPUを使うのはシェーダです。前章では頂点情報が流れ込んで、そのままスルーするシェーダを書きました。この場合、行列やその他の変数を一つも使っていません。しかしながら、今回はシェーダ内で行列計算をする必要があります。そのためにシェーダに行列を渡す処理が追加されます。
シェーダ内では渡されたワールド・ビュー・射影変換行列を用いて頂点を射影空間にまで変換します。シェーダの中身は次のようになります:
行列変換シェーダ float4x4 g_World;
float4x4 g_View;
float4x4 g_Proj;
float4 VS( float4 Pos : POSITION_IN ) : SV_POSITION
{
// 射影空間へ変換
Pos = mul(Pos, g_World);
Pos = mul(Pos, g_View);
Pos = mul(Pos, g_Proj);
return Pos;
}
シェーダ内にグローバルスコープのfloat4x4型の行列を3つ設定しました。意味は言わずもがなですね。頂点シェーダ内には引数に入ってくるローカル座標をワールド・ビュー・射影の順番に変換しています。
さて、このシェーダをプログラム内で読み込むとID3D10Effectインターフェイスが生成されます。この生成方法は前章で詳しく述べました。上のような変数に対して値を代入するには、ID3D10Effectインターフェイスが持つGetVariableByNameメソッドを用います:
GetVariableByNameメソッドで行列設定インターフェイスを取得 ID3D10EffectMatrixVariable *pWorldMatVar;
ID3D10EffectMatrixVariable *pViewMatVar;
ID3D10EffectMatrixVariable *pProjMatVar;
pWorldMatVar = pEffect->GetVariableByName( "g_World" )->AsMatrix();
pViewMatVar = pEffect->GetVariableByName( "g_View" )->AsMatrix();
pProjMatVar = pEffect->GetVariableByName( "g_Proj" )->AsMatrix();
ID3D10Effect::GetVariableByNameメソッドは引数の名前と自身が管理するシェーダで定義されている変数とを照合し、その変数が存在したらID3D10EffectVariableインターフェイスを返してくれます。このインターフェイスは「As○○」というメソッドを沢山持っています。これはこの抽象インターフェイスを指定の子インターフェイスにキャストするメソッドです。上の例ではAsMatrixメソッドが呼ばれていて、ID3D10EffectMatrixVariableインターフェイスが戻されます。行列はこのメソッドを通して設定します。
ID3D10EffectMatrixVariableインターフェイスで行列設定 D3DXMATRIX World;
D3DXMATRIX View;
D3DXMATRIX Proj;
// 行列をここで初期化・・・ //
// 行列をシェーダに設定
pWorldMatVar->SetMatrix( (float*)&World );
pViewMatVar->SetMatrix( (float*)&View );
pProjMatVar->SetMatrix( (float*)&Proj );
後の描画は前章と同じコードでいけます。またワールド行列、ビュー行列、射影変換行列の作り方はDirectX9と同じです。
行列の設定方法と同様に、浮動小数点や整数値などもシェーダに渡す事ができます。これはID3D10EffectVariable::As系メソッドでID3D10Effect○○Variableインターフェイスを取得して設定します。この章を踏まえたサンプルプログラムを作成しましたのでご参照下さい。