落書きノート

ふと自分が気になった事を書いてます

DirectX9 3Dモデルの表示とマテリアル適用

頂点バッファを使って表示する方法もありますが、複雑なモデルを表示するときには3Dモデリングソフトを使ってXファイルを作り、プログラムからそのファイルを読み込むようにすることが一般的です。

まずは、Xファイルを読み込む方法について載せます。Xファイルを読み込むには、D3DLoadMeshFromX()関数を使用します。SceneクラスのCreateメソッドでは、次のようにして、robo0.xというXファイルを読み込んでいます。

//--------------------------------------
// Xファイルの読み込み
//--------------------------------------
    if(FAILED( D3DXLoadMeshFromX( "robo0.x",// Xファイルのファイル名
                        D3DXMESH_SYSTEMMEM, // メッシュをシステムメモリに展開する
                        pD3DDevice,         // D3Dデバイスオブジェクト
                        NULL,
                        NULL,  // 属性バッファの設定
                        NULL,
                        &m_numMesh,         // メッシュの分割数
                        &m_pMesh ))) {      // 格納先のメッシュ
        return E_FAIL;
    }

D3DXLoadMeshFromX()関数の第1引数に指定しているのはXファイルのファイル名、第8引数に指定しているのは、メッシュを格納する変数のアドレス(&)です。また、第7引数にしているのは、Xファイルに含まれるメッシュの数を格納するための変数のアドレスです。Xファイルに格納されているモデルは、部分ごとのメッシュに分割されていることがあります。このような場合には分割されたメッシュごとに描画を行わなければならないため、ここでその数を取得しています。

Xファイルを描画するには、メッシュオブジェクトに用意されたID3Dmesh9:DrawSubsetメソッドを使用します。今回のプログラムでは、SceneクラスのDraw()メソッドで次のようにして表示を行っています。

for( DWORD i =0; i<m_numMesh; i++){    
        // マテリアルのコピー     
        mMaterial = mD3DXMaterials[i].MatD3D;   
        // マテリアルのセット     
        pD3DDevice->SetMaterial( &mMaterial);
        // 分割されたメッシュの描画
        m_pMesh->DrawSubset( i );
}

DrawSubsetの「Subset」(サブセット)とは、分割されたモデルの部分、一つ一つのメッシュを意味しています。つまり、モデル全体を描画するためには、読み込み時に取得しておいたメッシュの数の分だけループを回して、分割部分ごとに表示しなければならないというわけです。

以上のように、Xファイルの読み込みと表示は、非常に簡単です。しかし、3Dモデリングツールなどを使って作成したモデルには、ポリゴンの色を表すマテリアルやテクスチャ画像のファイル名といった情報が格納されていることもあります。これらの情報を利用するにはどうすればよいのでしょうか。

マテリアルやテクスチャに関しては次回以降の記事に載せますので、ここではXファイルからそれらの情報を読みだして、モデルに設定する方法だけを簡単に説明します。

まず、Xファイルに格納されているマテリアル情報、テクスチャ情報を取り出すには、Xファイルを読み込む際に、D3DXLoadMeshFromX関数の第5引数に、それら情報が書き込まれる「属性バッファ」を示すポインタ変数(pD3DXMtrlBuffer)を指定しておきます。すると、属性バッファには、メッシュごとにマテリアル情報とテクスチャ情報が順番に格納されることになります。今回のプログラムで利用するのはマテリアル情報だけなので、SceneクラスのCreate()メソッドで、次のように処理しています。

//--------------------------------------
// Xファイルの読み込み
//--------------------------------------
    if(FAILED( D3DXLoadMeshFromX( "robo0.x",// Xファイルのファイル名
                        D3DXMESH_SYSTEMMEM, // メッシュをシステムメモリに展開する
                        pD3DDevice,         // D3Dデバイスオブジェクト
                        NULL,
                        &m_pD3DXMtrlBuffer, // 属性バッファの設定
                        NULL,
                        &m_numMesh,         // メッシュの分割数
                        &m_pMesh ))) {      // 格納先のメッシュ
        return E_FAIL;
    }

属性バッファに格納されたマテリアル情報をモデルに適用するには、分割されたメッシュの番号に応じたマテリアル情報を属性バッファから読み出し、それらをIDirect3DDevice9::SetMaterial()メソッドを使って、レンダリングパイプラインにセットします。今回のプログラムでは、SceneクラスのDraw()メソッドで次のように処理を行っています。

//--------------------------------------
// モデルの描画
//--------------------------------------
D3DMATERIAL9    mMaterial;
// マテリアル情報を取り出す
D3DXMATERIAL*   mD3DXMaterials = (D3DXMATERIAL*)m_pD3DXMtrlBuffer->GetBufferPointer();
for( DWORD i =0; i<m_numMesh; i++){   
        // マテリアルのコピー     
        mMaterial = mD3DXMaterials[i].MatD3D;   
        // マテリアルのセット 
        pD3DDevice->SetMaterial( &mMaterial);
    // 分割されたメッシュの描画
        m_pMesh->DrawSubset( i );
}

このプログラムを実行すると以下のようになります。マテリアルが適用されて、モデルの各部が塗り分けられているのが分かると思います。

directx_chap03_02

次回は、モデルの配置、行列を使った座標変換処理です。もしかすると飛ばして書くかも?