DirectX9

【DirectX9】3Dモデル読み込みから描画までの実践的手順

DirectX9 を利用すると、3Dモデルの読み込みと描画がスムーズに実現できます。

D3DXLoadMeshFromXなどでモデルファイルからメッシュ情報を取得し、マテリアルやテクスチャを適切に設定してデバイスに渡す仕組みです。

リソースの解放も忘れずに行い、効率的な3Dレンダリングが可能となります。

DirectX9の初期化

Direct3Dオブジェクトの生成

Direct3Dオブジェクトを作成する際は、Direct3DCreate9関数を利用してDirect3Dのインターフェースを取得します。

プログラムが正しく描画処理を行えるよう、まずはこのオブジェクトの生成を実施します。

初期化に際しては、DirectX SDKに付属のヘッダーファイルやライブラリの確認も忘れずにしてください。

デバイスパラメーターの設定

ウィンドウモードとスワップエフェクトの選択

デバイス生成時のパラメーターとしてD3DPRESENT_PARAMETERS構造体を用意します。

ウィンドウモードで動作させる場合は、WindowedメンバーにTRUEを設定し、スワップエフェクトにはD3DSWAPEFFECT_DISCARDを指定します。

設定例は以下の通りです。

#include <d3d9.h>
#include <iostream>
// サンプルコード: D3Dデバイス初期化
int main() {
    // Direct3Dオブジェクトの生成
    LPDIRECT3D9 pD3D = Direct3DCreate9(D3D_SDK_VERSION);
    if (pD3D == NULL) {
        std::cout << "Direct3Dオブジェクトの取得に失敗しました。" << std::endl;
        return -1;
    }
    // デバイス用のプレゼンテーションパラメーターの設定
    D3DPRESENT_PARAMETERS d3dpp = {};
    d3dpp.Windowed = TRUE;                     // ウィンドウモードで動作
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;   // スワップエフェクトの設定
    // hWndはウィンドウハンドルが格納されている変数(サンプルとして省略)
    // 仮のウィンドウハンドルとしてNULLを設定(実際は適切なハンドルを用いる)
    HWND hWnd = NULL;
    // デバイスの作成(ソフトウェア頂点処理を指定)
    LPDIRECT3DDEVICE9 pD3DDevice = NULL;
    HRESULT hr = pD3D->CreateDevice(D3DADAPTER_DEFAULT,
                                    D3DDEVTYPE_HAL,
                                    hWnd,
                                    D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                    &d3dpp,
                                    &pD3DDevice);
    if (FAILED(hr)) {
        std::cout << "Direct3Dデバイスの作成に失敗しました。" << std::endl;
        pD3D->Release();
        return -1;
    }
    std::cout << "Direct3D初期化成功" << std::endl;
    // 正常終了時、リソースの解放が必要です
    pD3DDevice->Release();
    pD3D->Release();
    return 0;
}
Direct3D初期化成功

このコードでは、必要な#include文を記述し、main関数内でDirect3Dオブジェクトとデバイスの生成、エラーチェックまでを実装しています。

実際にウィンドウハンドルを生成し、適切なウィンドウクラスを設定する必要がありますが、ここではシンプルにデバイス初期化部分のみを示しています。

ソフトウェア/ハードウェア頂点処理の指定

頂点処理に関しては、ディスプレイカードの性能に応じた指定が可能です。

例えば、D3DCREATE_SOFTWARE_VERTEXPROCESSINGを指定するとソフトウェアで頂点処理を行い、より広い環境での動作が期待できます。

一方、性能の高い環境ではD3DCREATE_HARDWARE_VERTEXPROCESSINGD3DCREATE_PUREDEVICEを利用することも検討してみてください。

デバイス作成時のエラーチェック

Direct3Dデバイス作成時には、返り値のHRESULTを確認し、エラーが発生していないかをチェックします。

エラーチェックを適切に行えば、問題発生時に速やかに対応できるため、プログラムの安定性が向上します。

3Dモデルファイルの準備

対応ファイル形式の確認

DirectX9では、.x形式の3Dモデルがネイティブにサポートされています。

また、他の形式を利用する場合は、エクスポート時に.x形式へ変換を行うことが推奨されます。

使用する3DモデルがDirectX9環境に適しているかを確認してください。

3Dモデリングツールからのエクスポート方法

Blenderや3ds Maxなどの3Dモデリングツールでは、エクスポート機能が提供されています。

エクスポート時に出力形式を.xに設定することで、DirectX9との互換性を確保できます。

エクスポート手順についてはツールごとのマニュアルやオンラインリソースを参考にすると良いでしょう。

メッシュの読み込み処理

D3DXLoadMeshFromX関数の利用

メッシュの読み込みには、D3DXLoadMeshFromX関数を利用します。

この関数は、指定されたファイルからメッシュデータを読み込み、描画に用いるための情報を取得します。

その際、隣接情報やマテリアル情報も一緒に取得できるため、描画処理がスムーズになります。

読み込みパラメーターの役割

D3DXLoadMeshFromX関数の第1引数には読み込むファイル名、第2引数にはメッシュの種類(例:D3DXMESH_SYSTEMMEM)を指定します。

また、第3引数にDirect3Dデバイスのポインタを渡すことで、デバイスに適したメッシュが生成されます。

その他の引数では、隣接情報バッファやマテリアルのバッファを受け取るためのポインタが指定されます。

戻り値と隣接情報バッファ

関数の戻り値はHRESULT型であり、成功時はS_OKが返ります。

隣接情報バッファはメッシュのポリゴン同士の接続情報が格納され、影やライティングの計算に利用できるため、必要に応じて保持してください。

以下にサンプルコードを示します。

このサンプルコードでは、基本的な3Dモデルの読み込み処理を実装しています。

#include <d3d9.h>
#include <d3dx9.h>
#include <iostream>
// サンプルコード: 3Dモデルの読み込みと簡単な描画ループ
int main() {
    // Direct3Dオブジェクトの生成
    LPDIRECT3D9 pD3D = Direct3DCreate9(D3D_SDK_VERSION);
    if (pD3D == nullptr) {
        std::cout << "Direct3Dオブジェクトの生成に失敗しました。" << std::endl;
        return -1;
    }
    // 仮のウィンドウハンドル(通常は実際のウィンドウハンドルを使用)
    HWND hWnd = nullptr;
    // プレゼンテーションパラメーター設定
    D3DPRESENT_PARAMETERS d3dpp = {};
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.hDeviceWindow = hWnd;
    // Direct3Dデバイスの作成
    LPDIRECT3DDEVICE9 pDevice = nullptr;
    HRESULT hr = pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
                                    D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &pDevice);
    if (FAILED(hr) || pDevice == nullptr) {
        std::cout << "Direct3Dデバイスの作成に失敗しました。" << std::endl;
        pD3D->Release();
        return -1;
    }
    // 3Dモデルの読み込み用変数定義
    LPD3DXMESH pMesh = nullptr;
    LPD3DXBUFFER pAdjacency = nullptr;
    LPD3DXBUFFER pMaterials = nullptr;
    DWORD numMaterials = 0;
    // サンプルとして"model.x"というファイル名を使用
    hr = D3DXLoadMeshFromX(L"model.x", D3DXMESH_SYSTEMMEM, pDevice,
                            &pAdjacency, &pMaterials, nullptr,
                            &numMaterials, &pMesh);
    if (FAILED(hr) || pMesh == nullptr) {
        std::cout << "3Dモデルの読み込みに失敗しました。" << std::endl;
    } else {
        std::cout << "3Dモデルの読み込み成功" << std::endl;
    }
    // ここに描画処理等を追加できる
    // 取得したリソースの解放処理
    if (pMesh) pMesh->Release();
    if (pAdjacency) pAdjacency->Release();
    if (pMaterials) pMaterials->Release();
    pDevice->Release();
    pD3D->Release();
    return 0;
}
3Dモデルの読み込み成功

サンプルコードでは、必要な#include文も記載し、main関数内でDirect3Dの初期化から3Dモデルの読み込みまでを実装しています。

実際にウィンドウハンドルやファイルパスの適切な設定を行えば、より実用的なコードになるでしょう。

メッシュデータの最適な管理方法

取得したメッシュデータは、必要なメモリ管理に注意しながら使用してください。

描画処理が終わった後は、必ずRelease関数を利用してリソースを解放することで、メモリリークの防止に努める必要があります。

また、複数のメッシュを管理する場合は、配列やコンテナを活用して整理するとよいでしょう。

マテリアルとテクスチャの適用

マテリアル情報の取得

マテリアル構造体の各項目

DirectX9では、モデルに含まれるマテリアル情報はD3DXMATERIAL構造体に格納されます。

各項目には、拡散反射や環境光、鏡面反射などの情報が含まれており、描画時にライティング効果を適用するために利用されます。

必要に応じて、各プロパティを参照して、より詳細なマテリアル制御が可能です。

テクスチャの読み込み

テクスチャファイルフォーマットの考慮

モデルにテクスチャが含まれている場合、テクスチャファイルの形式にも注意が必要です。

DirectX9の場合、BMP、JPEG、PNGなど一般的な画像形式が利用できるため、エクスポート時や準備時に適切なフォーマットを選択してください。

テクスチャの透明度情報やカラー情報が要求される場合、ファイル形式の仕様を確認することが大切です。

テクスチャ設定の最適化

テクスチャの設定は、D3DXCreateTextureFromFile関数を利用して読み込み、デバイスのSetTexture関数で適用します。

モデルごとにテクスチャが異なる場合は、サブセット毎にテクスチャの切り替え処理を実施すると、より効率的に描画処理を進めることができます。

設定後のテクスチャリソースは、使用が終了したら適切に解放してください。

描画プロセスの管理

BeginSceneとEndSceneの役割

描画処理は、シーンの開始と終了を明確にするために、BeginSceneEndSceneを利用します。

BeginSceneが呼ばれると描画コマンドの受け付けが開始され、EndSceneが呼ばれると、そのシーンの描画命令が確定されます。

これにより、複数の描画オブジェクトが混在する場合でも安定した描画が可能となります。

サブセットごとの描画処理

DrawSubset関数の動作

メッシュ内に複数のサブセットがある場合、各サブセット毎にマテリアルやテクスチャが異なります。

DrawSubset関数を用いて、個別に描画することが推奨されます。

各サブセットに対して適用すべきマテリアルやテクスチャの設定を行った上で、この関数を呼び出してください。

マテリアル・テクスチャの連携処理

各サブセット描画時には、対応するマテリアル情報の設定とテクスチャの結びつけを事前に行います。

たとえば、マテリアルのカラー指定やテクスチャの貼り付けは、描画ループ内でサブセット毎に切り替える必要があります。

設定においては、以下のような手順が考えられます。

  • 対象のサブセットごとにマテリアルを設定
  • テクスチャがある場合は、指定したテクスチャをバインド
  • DrawSubset関数で描画命令を発行

これにより、各サブセットごとの描画結果が綺麗に反映されます。

リソース管理と解放

使用オブジェクトのRelease手順

メッシュ、マテリアル、隣接情報の解放方法

取得した各種リソース(メッシュ、マテリアル、隣接情報バッファなど)は、描画処理の終了時またはプログラムの終了時に必ずRelease関数を利用して解放してください。

各オブジェクトのポインタが無効になっているかを確認した上で解放することで、不要なメモリ占有を防ぐことができます。

メモリリーク防止のポイント

  • 各リソースの参照カウント管理を徹底する
  • 描画ループ終了後、またはエラー発生時にもリソース解放処理を実施する
  • スマートポインタの利用や、独自のリソースマネージャーの実装を検討する

これらの対策により、アプリケーション全体の安定稼働が期待できます。

パフォーマンス向上とエラーハンドリング

描画パフォーマンスの最適化

バッチ処理の活用

複数のサブセットや同一テクスチャの使用がある場合、バッチ処理を適用することで描画コストが軽減されます。

可能な限り、描画命令の発行回数を減らす工夫を取り入れて、パフォーマンスの向上を図ってみてください。

不要データの削除策

読み込み後に必要のなくなったデータや、描画に使われないモデル情報は速やかにメモリから削除するように意識してください。

これにより、レンダリングの処理速度が向上し、全体的なパフォーマンスの最適化につながります。

エラー検出とデバッグ手法

ログ出力の設定

描画処理の各段階で、エラー発生時にログを出力するように設定することで、問題発生時に原因を特定しやすくなります。

ログは、システムの標準出力やファイルへの出力を活用することがおすすめです。

例外検出の実装方法

DirectXの各関数はHRESULTで結果を返すため、エラー判定を行いつつ例外処理用のコードを実装してください。

たとえば、関数呼び出しの直後にエラーチェックを実施し、エラーが発生したら適切なメッセージを出力することで、開発中のデバッグがしやすくなります。

まとめ

これまでの内容では、DirectX9を利用して3Dモデルの初期化から描画、リソースの管理およびパフォーマンス最適化に関する手順を詳しく記述しました。

各工程において、エラーチェックやリソース解放、パフォーマンス向上のための実装ポイントを丁寧に説明することで、実践的な開発の基礎が理解できるよう配慮しています。

今後の開発に役立ててもらえる情報が詰まっていると感じてもらえたら幸いです。

関連記事

Back to top button
目次へ