DirectX9

【C++】DirectX9 3Dグラフィックス入門チュートリアル:デバイス初期化からレンダリングまで詳しく解説

DirectX9を使った3D描画は、Win32ウィンドウを用意しDirect3DCreate9でインターフェース取得後デバイスを初期化すれば始められます。

頂点バッファへモデルを送りRender LoopでBeginSceneEndSceneを挟んで描画し、最後にリソースを解放すれば基本工程は完了です。

固定機能でもHLSL併用でも流れは共通です。

目次から探す
  1. DirectX9とDirect3Dの基礎概念
  2. Win32ウィンドウとメッセージループ
  3. Direct3Dインターフェースの生成
  4. デバイス初期化
  5. 頂点フォーマットとバッファ作成
  6. 行列変換の基礎
  7. レンダリングループ
  8. テクスチャマッピング
  9. ライティングとマテリアル
  10. カリングとデプステスト
  11. アルファブレンディング
  12. シェーダー入門
  13. エフェクトフレームワーク
  14. リソース管理とデバイスロスト対策
  15. デバッグとプロファイリング
  16. 高度なレンダリング例
  17. スプライトとフォント描画
  18. メッシュとアニメーション
  19. カメラ制御テクニック
  20. パーティクルシステム
  21. テレインレンダリング
  22. パフォーマンス最適化
  23. DirectX9からのアップグレードパス
  24. まとめ

DirectX9とDirect3Dの基礎概念

DirectX9は、Windows環境で3Dグラフィックスやサウンド、入力デバイスの制御を行うためのAPIセットです。

特に3Dグラフィックスの分野では、Direct3D 9が中心的な役割を果たしており、ゲームやマルチメディアアプリケーションの開発に広く利用されています。

ここでは、DirectX9の主要コンポーネントやグラフィックスパイプラインの流れ、そしてFixed Function PipelineとProgrammable Pipelineの違いについて詳しく解説します。

DirectX9が提供する主要コンポーネント

DirectX9は複数のAPI群から構成されており、主に以下のコンポーネントが含まれています。

  • Direct3D 9

3Dグラフィックスの描画を担当するAPIです。

頂点やピクセルの処理、テクスチャの管理、ライティング、シェーダーの実行などを行います。

Direct3D 9は、ハードウェアアクセラレーションを活用し、高速な3Dレンダリングを実現します。

  • DirectDraw

2Dグラフィックスの描画に特化したAPIですが、Direct3Dの登場以降は主に2D描画の補助的役割となっています。

  • DirectSound

サウンドの再生や録音、3Dサウンドの空間処理を行うAPIです。

ゲームの臨場感を高めるために利用されます。

  • DirectInput

キーボード、マウス、ゲームパッドなどの入力デバイスからの情報を取得するAPIです。

リアルタイムな入力処理に適しています。

  • DirectPlay

ネットワーク通信を簡単に扱うためのAPIですが、現在は非推奨となっています。

この中でも、3Dグラフィックスの開発においてはDirect3D 9が最も重要な役割を担っています。

Direct3D 9は、ハードウェアの機能を抽象化し、開発者が複雑なグラフィックス処理を効率的に実装できるように設計されています。

グラフィックスパイプラインの流れ

Direct3D 9のグラフィックスパイプラインは、3Dモデルの頂点データから最終的な画面表示までの一連の処理の流れを指します。

パイプラインは複数のステージに分かれており、それぞれが特定の役割を持っています。

主なステージは以下の通りです。

  1. 頂点処理(Vertex Processing)

モデルの頂点データに対して、座標変換やライティング計算を行います。

ワールド座標系からビュー座標系、さらにプロジェクション座標系への変換がここで行われます。

Direct3D 9では、固定機能パイプラインとシェーダープログラムの両方で頂点処理が可能です。

  1. プリミティブ組み立て(Primitive Assembly)

頂点データを三角形やラインなどのプリミティブに組み立てます。

これにより、GPUが描画可能な形状が形成されます。

  1. ラスタライズ(Rasterization)

プリミティブをピクセル単位に変換し、画面上のピクセルに対応させます。

ここで、テクスチャマッピングやシェーディングの準備が行われます。

  1. ピクセル処理(Pixel Processing)

各ピクセルの色や透明度を計算します。

ライティングやテクスチャのサンプリング、ブレンディングなどが含まれます。

ピクセルシェーダーを使うことで、より複雑な効果を実現できます。

  1. 出力マージ(Output Merging)

計算されたピクセルデータをフレームバッファに書き込みます。

デプスバッファやステンシルバッファを使ったテストもここで行われます。

このパイプラインの流れを理解することで、どの段階でどのような処理を行うべきかが明確になります。

Direct3D 9はこれらの処理を効率的にハードウェアに委譲し、高速な描画を実現しています。

Fixed FunctionとProgrammable Pipelineの違い

Direct3D 9では、グラフィックスパイプラインの処理方法として「Fixed Function Pipeline(固定機能パイプライン)」と「Programmable Pipeline(プログラマブルパイプライン)」の2種類が存在します。

これらはグラフィックス処理の柔軟性と制御の度合いに大きな違いがあります。

Fixed Function Pipeline(固定機能パイプライン)

固定機能パイプラインは、あらかじめ決められた処理の流れに従ってグラフィックスを描画します。

開発者はパイプラインの各ステージで利用するパラメータ(ライティングの有無、テクスチャの種類、ブレンディングモードなど)を設定するだけで、複雑なシェーダーコードを書く必要がありません。

主な特徴は以下の通りです。

  • 簡単に使える

シェーダーの知識がなくても基本的な3D描画が可能です。

  • パフォーマンスが安定

ハードウェアに最適化された固定機能のため、処理が高速です。

  • 表現力に制限がある

複雑なエフェクトやカスタムライティングは実装できません。

Programmable Pipeline(プログラマブルパイプライン)

プログラマブルパイプラインは、頂点シェーダーやピクセルシェーダーと呼ばれるプログラムを自作して、パイプラインの処理内容を自由にカスタマイズできます。

Direct3D 9ではShader Model 2.0や3.0に対応しており、HLSL(High-Level Shader Language)を使ってシェーダーを記述します。

主な特徴は以下の通りです。

  • 高い表現力

複雑なライティングモデルや特殊効果を実装可能です。

  • 柔軟な制御

頂点変換やピクセルの色計算を自由にプログラムできます。

  • 学習コストが高い

シェーダー言語の理解やGPUの動作原理の知識が必要です。

  • パフォーマンスの調整が必要

シェーダーの内容によっては処理負荷が高くなることがあります。

特徴Fixed Function PipelineProgrammable Pipeline
利用の容易さ簡単難しい
表現力制限あり高い
カスタマイズ性低い高い
パフォーマンス安定シェーダー次第
学習コスト低い高い

DirectX9の開発では、まず固定機能パイプラインで基本を押さえ、その後プログラマブルパイプラインに移行して高度な表現を目指すのが一般的です。

これにより、段階的に3Dグラフィックスの理解と技術を深められます。

Win32ウィンドウとメッセージループ

ウィンドウクラス登録

DirectX9で3Dグラフィックスを描画するには、まず描画先となるウィンドウを作成する必要があります。

Windowsアプリケーションでは、ウィンドウを作成するために「ウィンドウクラス」を登録し、そのクラスを使ってウィンドウを生成します。

ウィンドウクラスはWNDCLASSEX構造体で定義し、RegisterClassEx関数で登録します。

主なメンバーは以下の通りです。

  • cbSize:構造体のサイズ
  • style:ウィンドウのスタイル(再描画の方法など)
  • lpfnWndProc:ウィンドウプロシージャの関数ポインタ
  • hInstance:アプリケーションのインスタンスハンドル
  • hCursor:カーソルのハンドル
  • hbrBackground:背景ブラシ
  • lpszClassName:クラス名(文字列)

以下に、基本的なウィンドウクラス登録のサンプルコードを示します。

#include <windows.h>
// ウィンドウプロシージャの宣言
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow)
{
    WNDCLASSEX wc = {};
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW; // 水平方向・垂直方向の再描画を有効にする
    wc.lpfnWndProc = WndProc;            // メッセージ処理関数
    wc.hInstance = hInstance;            // インスタンスハンドル
    wc.hCursor = LoadCursor(nullptr, IDC_ARROW); // 標準の矢印カーソル
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // 背景色
    wc.lpszClassName = L"MyWindowClass"; // クラス名
    if (!RegisterClassEx(&wc)) {
        MessageBox(nullptr, L"ウィンドウクラスの登録に失敗しました", L"エラー", MB_OK);
        return 0;
    }
    // ウィンドウ作成などはここで行う
    return 0;
}
// ウィンドウプロシージャの定義
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg) {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
}

このコードでは、WNDCLASSEX構造体を初期化し、RegisterClassExで登録しています。

lpfnWndProcにはメッセージを処理する関数WndProcを指定し、ウィンドウの基本的な動作を定義しています。

CS_HREDRAWCS_VREDRAWはウィンドウのサイズ変更時に再描画を促すスタイルです。

メッセージ処理のポイント

Windowsアプリケーションは、ユーザーの操作やシステムからの通知を「メッセージ」として受け取り処理します。

メッセージはウィンドウプロシージャで受け取り、適切に処理する必要があります。

DirectX9のレンダリングループを実装する際は、メッセージループと描画処理を両立させることが重要です。

基本的なメッセージループは以下のようになります。

MSG msg = {};
while (msg.message != WM_QUIT) {
    if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    } else {
        // メッセージがない場合は描画処理を行う
        RenderFrame();
    }
}

このループでは、PeekMessageを使ってメッセージを非同期に取得し、メッセージがあれば処理します。

メッセージがない場合はRenderFrame関数で描画処理を行います。

これにより、ウィンドウの応答性を保ちつつ、継続的なレンダリングが可能になります。

ウィンドウプロシージャ内で特に重要なメッセージは以下の通りです。

  • WM_DESTROY:ウィンドウが破棄される際に呼ばれ、PostQuitMessageでメッセージループの終了を通知します
  • WM_PAINT:ウィンドウの再描画要求。DirectX9では通常、ここでの描画は行わず、メインループで描画します
  • WM_SIZE:ウィンドウサイズ変更時の処理。バックバッファのサイズ変更などに対応します
  • WM_KEYDOWN / WM_KEYUP:キーボード入力の検出

メッセージ処理は、必要に応じて拡張し、ユーザー操作やシステムイベントに対応します。

非同期入力への対処

ゲームやリアルタイム3Dアプリケーションでは、ユーザー入力をスムーズに処理することが重要です。

Windowsのメッセージ処理はイベント駆動型ですが、入力の検出には非同期的な方法も活用します。

キーボード入力の非同期処理

WM_KEYDOWNWM_KEYUPメッセージはキーの押下・離上を通知しますが、連続したキー入力や複数キー同時押しの検出にはGetAsyncKeyState関数が便利です。

この関数は指定したキーの現在の状態を即座に取得できます。

if (GetAsyncKeyState(VK_LEFT) & 0x8000) {
    // 左矢印キーが押されている間の処理
}

この方法を使うと、メッセージを待たずにリアルタイムでキーの状態を監視できるため、ゲームの操作性が向上します。

マウス入力の非同期処理

マウスの位置やボタン状態もGetCursorPosGetAsyncKeyStateで非同期に取得可能です。

例えば、マウスの移動量を計算してカメラ操作に利用できます。

POINT cursorPos;
GetCursorPos(&cursorPos);
// 取得した座標を使って処理

メッセージループと非同期入力の組み合わせ

メッセージループ内でPeekMessageを使い、メッセージ処理と描画を両立させつつ、GetAsyncKeyStateなどで入力状態を常に監視するのが一般的です。

これにより、ユーザーの操作に対して遅延なく反応できます。

  • メッセージ処理はウィンドウプロシージャで行い、PeekMessageで非同期に取得します
  • 描画処理はメッセージがないときに行い、応答性を確保します
  • キーボードやマウスの状態はGetAsyncKeyStateGetCursorPosでリアルタイムに取得します
  • これらを組み合わせることで、スムーズな入力処理と描画が可能になります

以上のポイントを押さえることで、DirectX9アプリケーションのウィンドウ管理とユーザー入力処理が安定して動作します。

Direct3Dインターフェースの生成

Direct3DCreate9でのインスタンス取得

Direct3D 9を使った3D描画を始めるには、まずDirect3Dのインターフェースを取得する必要があります。

これにはDirect3DCreate9関数を使用します。

この関数はDirect3D 9のメインインターフェースであるIDirect3D9のポインタを返します。

Direct3DCreate9の引数には、Direct3Dのバージョンを指定します。

通常はD3D_SDK_VERSIONを渡します。

これにより、SDKのバージョンに対応したインターフェースが取得されます。

以下に基本的な使い方の例を示します。

#include <windows.h>
#include <d3d9.h>
#include <iostream>
#pragma comment(lib, "d3d9.lib")
int main()
{
    // Direct3D9インターフェースの取得
    IDirect3D9* pD3D = Direct3DCreate9(D3D_SDK_VERSION);
    if (!pD3D) {
        std::cerr << "Direct3D9のインスタンス取得に失敗しました。" << std::endl;
        return -1;
    }
    std::cout << "Direct3D9インスタンスを正常に取得しました。" << std::endl;
    // 使用後は必ず解放する
    pD3D->Release();
    return 0;
}

このコードでは、Direct3DCreate9IDirect3D9のポインタを取得し、成功したかどうかをチェックしています。

取得に成功したら、後で必ずReleaseメソッドを呼んでリソースを解放してください。

アダプタとディスプレイモードの確認

Direct3D 9は複数のグラフィックスアダプタ(GPU)をサポートしています。

通常はシステムに搭載されているグラフィックスカードが1つですが、複数ある場合はどのアダプタを使うか選択できます。

アダプタはIDirect3D9のメソッドGetAdapterCountで数を取得し、GetAdapterIdentifierで詳細情報を得られます。

さらに、GetAdapterDisplayModeで現在のディスプレイモード(解像度やリフレッシュレート、ピクセルフォーマット)を取得可能です。

以下はアダプタ情報とディスプレイモードを列挙するサンプルコードです。

#include <windows.h>
#include <d3d9.h>
#include <iostream>
#pragma comment(lib, "d3d9.lib")
int main()
{
    IDirect3D9* pD3D = Direct3DCreate9(D3D_SDK_VERSION);
    if (!pD3D) {
        std::cerr << "Direct3D9のインスタンス取得に失敗しました。" << std::endl;
        return -1;
    }
    UINT adapterCount = pD3D->GetAdapterCount();
    std::cout << "利用可能なアダプタ数: " << adapterCount << std::endl;
    for (UINT i = 0; i < adapterCount; ++i) {
        D3DADAPTER_IDENTIFIER9 adapterId;
        if (SUCCEEDED(pD3D->GetAdapterIdentifier(i, 0, &adapterId))) {
            std::wcout << L"アダプタ " << i << L": " << adapterId.Description << std::endl;
        }
        D3DDISPLAYMODE displayMode;
        if (SUCCEEDED(pD3D->GetAdapterDisplayMode(i, &displayMode))) {
            std::cout << "  解像度: " << displayMode.Width << "x" << displayMode.Height << std::endl;
            std::cout << "  リフレッシュレート: " << displayMode.RefreshRate << " Hz" << std::endl;
            std::cout << "  フォーマット: " << displayMode.Format << std::endl;
        }
    }
    pD3D->Release();
    return 0;
}

このコードは、システムに存在するすべてのアダプタの名前と現在のディスプレイモードを表示します。

D3DADAPTER_IDENTIFIER9Descriptionメンバーはアダプタの名称を示します。

D3DDISPLAYMODEFormatはピクセルフォーマットを表し、例えばD3DFMT_X8R8G8B8などの定数値が返ります。

ハードウェア能力のチェック

Direct3D 9では、使用するアダプタのハードウェア能力を事前に確認することが重要です。

これにより、アプリケーションが必要とする機能がサポートされているかどうかを判断できます。

IDirect3D9GetDeviceCapsメソッドを使い、D3DCAPS9構造体に能力情報を取得します。

主なチェックポイントは以下の通りです。

  • デバイスタイプ

ハードウェアデバイスD3DDEVTYPE_HALか、ソフトウェアエミュレーションD3DDEVTYPE_REFか。

  • シェーダーモデルのサポート

頂点シェーダーやピクセルシェーダーのバージョン。

  • テクスチャの最大サイズ

GPUが扱える最大テクスチャサイズ。

  • マルチサンプリングの対応

アンチエイリアスのサポート状況。

以下にハードウェア能力をチェックする例を示します。

#include <windows.h>
#include <d3d9.h>
#include <iostream>
#pragma comment(lib, "d3d9.lib")
int main()
{
    IDirect3D9* pD3D = Direct3DCreate9(D3D_SDK_VERSION);
    if (!pD3D) {
        std::cerr << "Direct3D9のインスタンス取得に失敗しました。" << std::endl;
        return -1;
    }
    D3DCAPS9 caps;
    HRESULT hr = pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps);
    if (FAILED(hr)) {
        std::cerr << "デバイス能力の取得に失敗しました。" << std::endl;
        pD3D->Release();
        return -1;
    }
    std::cout << "デバイスタイプ: " << (caps.DeviceType == D3DDEVTYPE_HAL ? "ハードウェア" : "ソフトウェア") << std::endl;
    std::cout << "頂点シェーダーバージョン: "
              << HIWORD(caps.VertexShaderVersion) << "." << LOWORD(caps.VertexShaderVersion) << std::endl;
    std::cout << "ピクセルシェーダーバージョン: "
              << HIWORD(caps.PixelShaderVersion) << "." << LOWORD(caps.PixelShaderVersion) << std::endl;
    std::cout << "最大テクスチャ幅: " << caps.MaxTextureWidth << std::endl;
    std::cout << "最大テクスチャ高さ: " << caps.MaxTextureHeight << std::endl;
    // マルチサンプリングのサポート確認
    D3DMULTISAMPLE_TYPE msType = D3DMULTISAMPLE_NONE;
    DWORD qualityLevels = 0;
    for (int i = D3DMULTISAMPLE_2_SAMPLES; i <= D3DMULTISAMPLE_16_SAMPLES; ++i) {
        if (SUCCEEDED(pD3D->CheckDeviceMultiSampleType(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, TRUE, (D3DMULTISAMPLE_TYPE)i, &qualityLevels)) && qualityLevels > 0) {
            msType = (D3DMULTISAMPLE_TYPE)i;
            break;
        }
    }
    std::cout << "サポートされているマルチサンプリングタイプ: " << msType << std::endl;
    pD3D->Release();
    return 0;
}

このコードでは、デフォルトアダプタのハードウェアデバイスの能力を取得し、シェーダーバージョンや最大テクスチャサイズを表示しています。

また、マルチサンプリングのサポートを調べ、利用可能な最小のサンプル数を検出しています。

これらの手順を踏むことで、Direct3D 9のインターフェースを正しく取得し、使用するアダプタの情報や能力を把握した上で、適切なデバイス初期化へと進められます。

デバイス初期化

Direct3D 9で3D描画を行うためには、IDirect3DDevice9インターフェースを作成し、描画に必要な設定を行う必要があります。

この際に重要となるのがD3DPRESENT_PARAMETERS構造体の設定です。

ここでは、D3DPRESENT_PARAMETERSの主要な設定項目、特にバックバッファのフォーマットやマルチサンプリング、スワップ効果について解説し、デバイス作成時のフラグ選択やデバイスリセットの扱いについても説明します。

D3DPRESENT_PARAMETERSの設定項目

D3DPRESENT_PARAMETERSは、Direct3Dデバイスの作成時に描画環境を指定するための構造体です。

主なメンバーは以下の通りです。

  • BackBufferWidth / BackBufferHeight

バックバッファの幅と高さ(ピクセル単位)。

0を指定するとウィンドウのクライアント領域サイズに合わせます。

  • BackBufferFormat

バックバッファのピクセルフォーマット。

色深度やアルファチャンネルの有無を指定します。

  • BackBufferCount

バックバッファの数。

通常は1(ダブルバッファリング)か2(トリプルバッファリング)を指定します。

  • MultiSampleType

マルチサンプリングの種類。

アンチエイリアス効果の設定に使います。

  • SwapEffect

バックバッファとフロントバッファの交換方法を指定します。

  • hDeviceWindow

描画先のウィンドウハンドル。

  • Windowed

ウィンドウモードかフルスクリーンモードかを指定。

  • EnableAutoDepthStencil

深度バッファとステンシルバッファの自動生成を有効にするか。

  • AutoDepthStencilFormat

深度バッファのフォーマット。

  • PresentationInterval

垂直同期の設定。

バックバッファのフォーマット

バックバッファのフォーマットは、描画結果の色の表現方法を決める重要な設定です。

代表的なフォーマットは以下の通りです。

フォーマット名説明
D3DFMT_X8R8G8B832ビットカラー、アルファなし
D3DFMT_A8R8G8B832ビットカラー、8ビットアルファ付き
D3DFMT_R5G6B516ビットカラー、アルファなし
D3DFMT_A1R5G5B516ビットカラー、1ビットアルファ付き

一般的にはD3DFMT_X8R8G8B8D3DFMT_A8R8G8B8が使われます。

アルファチャンネルが必要な場合はA8R8G8B8を選択します。

バックバッファのフォーマットは、ディスプレイのフォーマットと互換性がある必要があり、IDirect3D9::GetAdapterDisplayModeで取得したフォーマットを参考に設定します。

バックバッファのフォーマット設定例

D3DPRESENT_PARAMETERS d3dpp = {};
d3dpp.BackBufferWidth = 800;
d3dpp.BackBufferHeight = 600;
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8; // 32ビットカラー、アルファなし
d3dpp.BackBufferCount = 1;
d3dpp.Windowed = TRUE; // ウィンドウモード
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hwnd; // ウィンドウハンドル
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8; // 24ビット深度、8ビットステンシル
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;

マルチサンプリングとスワップ効果

マルチサンプリング

マルチサンプリングはアンチエイリアスの一種で、エッジのギザギザを滑らかにする効果があります。

MultiSampleTypeD3DMULTISAMPLE_2_SAMPLESD3DMULTISAMPLE_4_SAMPLESなどを指定します。

マルチサンプリングを有効にするには、ハードウェアが対応しているか事前にIDirect3D9::CheckDeviceMultiSampleTypeで確認する必要があります。

スワップ効果

スワップ効果は、バックバッファとフロントバッファの交換方法を指定します。

主な値は以下の通りです。

説明
D3DSWAPEFFECT_DISCARD最も高速。バックバッファの内容は不定になる
D3DSWAPEFFECT_FLIPフルスクリーンでの高速なフリップ
D3DSWAPEFFECT_COPYバックバッファをフロントバッファにコピー

通常はD3DSWAPEFFECT_DISCARDが推奨されます。

ウィンドウモードではDISCARDCOPYを使い、フルスクリーンではFLIPが使われることが多いです。

デバイス作成フラグの選択

IDirect3D9::CreateDevice関数の第5引数には、デバイス作成時のフラグを指定します。

主なフラグは以下の通りです。

  • D3DCREATE_HARDWARE_VERTEXPROCESSING

ハードウェアで頂点処理を行います。

高速だが対応GPUが必要でしょう。

  • D3DCREATE_SOFTWARE_VERTEXPROCESSING

ソフトウェアで頂点処理を行います。

互換性は高いが遅い。

  • D3DCREATE_MIXED_VERTEXPROCESSING

ハードウェアとソフトウェアの混合処理。

  • D3DCREATE_MULTITHREADED

マルチスレッド環境での安全性を確保するがパフォーマンス低下の可能性があります。

一般的には、対応する場合はD3DCREATE_HARDWARE_VERTEXPROCESSINGを指定し、対応しない場合はD3DCREATE_SOFTWARE_VERTEXPROCESSINGにフォールバックします。

デバイスリセットの扱い

Direct3D 9のデバイスは、画面モードの変更やウィンドウの最小化・最大化、デバイスのロスト(失われる)状態になることがあります。

この場合、デバイスをリセットして再初期化する必要があります。

デバイスロストの検出

レンダリングループ内でIDirect3DDevice9::TestCooperativeLevelを呼び、戻り値をチェックします。

  • D3DERR_DEVICELOST

デバイスがロストしており、リセットできない状態。

  • D3DERR_DEVICENOTRESET

デバイスがリセット可能な状態。

  • D3D_OK

正常に動作中。

デバイスリセットの手順

  1. 描画を停止し、EndSceneBeginSceneが呼ばれていないことを確認。
  2. リソースの一部(D3DPOOL_DEFAULTで作成したテクスチャや頂点バッファなど)を解放または破棄。
  3. Resetメソッドに新しいD3DPRESENT_PARAMETERSを渡してデバイスをリセット。
  4. リセット成功後、破棄したリソースを再作成。
  5. 描画を再開。

サンプルコード(リセット処理の一部)

HRESULT hr = pDevice->TestCooperativeLevel();
if (hr == D3DERR_DEVICENOTRESET) {
    // リソースを解放
    ReleaseDefaultPoolResources();
    // デバイスリセット
    hr = pDevice->Reset(&d3dpp);
    if (SUCCEEDED(hr)) {
        // リソース再作成
        CreateDefaultPoolResources();
    }
}

デバイスリセットはDirect3D 9の開発で避けて通れない処理です。

適切に管理しないと描画が停止したりクラッシュの原因になります。

これらの設定と処理を正しく行うことで、Direct3D 9のデバイスを安定して初期化し、描画環境を整えられます。

頂点フォーマットとバッファ作成

3Dグラフィックスの描画において、頂点データの管理は非常に重要です。

Direct3D 9では、頂点の属性を定義するためにカスタム頂点構造体を作成し、それに対応するFlexible Vertex Format(FVF)を設定します。

頂点バッファを生成してデータを書き込み、さらに効率的な描画のためにインデックスバッファを併用する方法について詳しく説明します。

カスタム頂点構造体定義

頂点は位置情報だけでなく、法線やテクスチャ座標、色など様々な属性を持ちます。

Direct3D 9では、これらの属性を含むカスタム構造体を定義し、頂点バッファに格納します。

例えば、位置(x, y, z)とカラー(ARGB)を持つ頂点構造体は以下のように定義します。

struct CUSTOMVERTEX
{
    float x, y, z;    // 頂点の位置
    DWORD color;      // 頂点カラー(ARGB形式)
};

この構造体は、頂点ごとに3D空間の座標と色を保持します。

必要に応じて、法線ベクトルやテクスチャ座標を追加することも可能です。

例えば、位置とテクスチャ座標を持つ頂点は以下のように定義します。

struct CUSTOMVERTEX_TEX
{
    float x, y, z;    // 頂点の位置
    float u, v;       // テクスチャ座標
};

Flexible Vertex Format(FVF)の設定

FVFは、頂点構造体の属性をDirect3Dに伝えるためのビットフラグの組み合わせです。

これにより、Direct3Dは頂点バッファのデータレイアウトを理解し、正しく処理できます。

先ほどの位置とカラーを持つ頂点の場合、FVFは以下のように設定します。

#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE)
  • D3DFVF_XYZ:3D位置情報(float x, y, z)
  • D3DFVF_DIFFUSE:頂点カラー(DWORD color)

位置とテクスチャ座標を持つ頂点の場合は、

#define D3DFVF_CUSTOMVERTEX_TEX (D3DFVF_XYZ | D3DFVF_TEX1)
  • D3DFVF_TEX1:1セットのテクスチャ座標(float u, v)

FVFの主なフラグは以下の通りです。

フラグ名説明
D3DFVF_XYZ3D位置(float x, y, z)
D3DFVF_NORMAL法線ベクトル(float nx, ny, nz)
D3DFVF_DIFFUSE頂点カラー(DWORD)
D3DFVF_SPECULAR頂点スペキュラカラー(DWORD)
D3DFVF_TEX1テクスチャ座標1セット
D3DFVF_TEX2テクスチャ座標2セット

頂点バッファの生成と書き込み

頂点バッファは、GPUが描画に使用する頂点データを格納するメモリ領域です。

Direct3D 9ではIDirect3DDevice9::CreateVertexBufferで頂点バッファを生成し、LockUnlockでデータの書き込みを行います。

以下に、位置とカラーを持つ頂点バッファを作成し、三角形の頂点データを書き込む例を示します。

#include <d3d9.h>
#include <d3dx9.h>
struct CUSTOMVERTEX
{
    float x, y, z;
    DWORD color;
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE)
IDirect3DVertexBuffer9* pVertexBuffer = nullptr;
void CreateVertexBuffer(IDirect3DDevice9* pDevice)
{
    // 頂点数
    const int vertexCount = 3;
    // 頂点バッファの作成
    HRESULT hr = pDevice->CreateVertexBuffer(
        sizeof(CUSTOMVERTEX) * vertexCount,
        0,
        D3DFVF_CUSTOMVERTEX,
        D3DPOOL_MANAGED,
        &pVertexBuffer,
        nullptr);
    if (FAILED(hr)) {
        // エラーハンドリング
        return;
    }
    // 頂点データの書き込み
    CUSTOMVERTEX* pVertices = nullptr;
    hr = pVertexBuffer->Lock(0, 0, (void**)&pVertices, 0);
    if (FAILED(hr)) {
        // エラーハンドリング
        return;
    }
    // 三角形の頂点座標と色を設定
    pVertices[0] = { 0.0f, 1.0f, 0.0f, 0xFFFF0000 }; // 赤
    pVertices[1] = { 1.0f, -1.0f, 0.0f, 0xFF00FF00 }; // 緑
    pVertices[2] = { -1.0f, -1.0f, 0.0f, 0xFF0000FF }; // 青
    pVertexBuffer->Unlock();
}

このコードでは、3つの頂点からなる三角形を作成しています。

D3DPOOL_MANAGEDはDirect3Dがメモリ管理を行うプールで、リセット時のリソース管理が容易です。

インデックスバッファの併用

インデックスバッファは、頂点バッファの頂点を参照するための配列で、頂点の再利用を可能にし、描画効率を向上させます。

特に複雑なメッシュでは、同じ頂点を複数の三角形で共有するために必須です。

インデックスバッファはIDirect3DDevice9::CreateIndexBufferで作成し、Lock/Unlockでデータを書き込みます。

インデックスの型は16ビットD3DFMT_INDEX16か32ビットD3DFMT_INDEX32を選択します。

以下に、先ほどの三角形にインデックスバッファを使う例を示します。

IDirect3DIndexBuffer9* pIndexBuffer = nullptr;
void CreateIndexBuffer(IDirect3DDevice9* pDevice)
{
    // インデックス数(三角形1つなので3つ)
    const int indexCount = 3;
    HRESULT hr = pDevice->CreateIndexBuffer(
        sizeof(WORD) * indexCount,
        0,
        D3DFMT_INDEX16,
        D3DPOOL_MANAGED,
        &pIndexBuffer,
        nullptr);
    if (FAILED(hr)) {
        // エラーハンドリング
        return;
    }
    WORD* pIndices = nullptr;
    hr = pIndexBuffer->Lock(0, 0, (void**)&pIndices, 0);
    if (FAILED(hr)) {
        // エラーハンドリング
        return;
    }
    // 頂点バッファの頂点を参照するインデックスを設定
    pIndices[0] = 0;
    pIndices[1] = 1;
    pIndices[2] = 2;
    pIndexBuffer->Unlock();
}

描画時には、SetStreamSourceで頂点バッファをセットし、SetIndicesでインデックスバッファをセットします。

その後、DrawIndexedPrimitiveを呼びます。

pDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
pDevice->SetStreamSource(0, pVertexBuffer, 0, sizeof(CUSTOMVERTEX));
pDevice->SetIndices(pIndexBuffer);
pDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 3, 0, 1);

この方法により、頂点の重複を避けてメモリ使用量を削減し、描画パフォーマンスを向上させられます。

行列変換の基礎

3Dグラフィックスにおいて、モデルの配置やカメラの視点、投影方法を制御するために行列変換は欠かせません。

Direct3D 9では、主にWorld行列、View行列、Projection行列の3種類の行列を使って3D空間の座標変換を行います。

ここではそれぞれの役割と使い方、さらに行列の組み合わせやスケーリングについて詳しく説明します。

World行列でモデル配置

World行列は、モデルのローカル座標系からワールド座標系への変換を行います。

つまり、モデルの位置、回転、スケーリングを決定する行列です。

これにより、モデルを3D空間の任意の場所に配置できます。

Direct3D 9では、D3DXMATRIX構造体を使って行列を表現し、D3DXMatrixTranslationD3DXMatrixRotationYawPitchRollD3DXMatrixScalingなどの関数で変換行列を作成します。

例:モデルを原点から(5, 0, 10)に移動し、Y軸を中心に45度回転させる

#include <d3dx9.h>
// 平行移動行列
D3DXMATRIX matTranslate;
D3DXMatrixTranslation(&matTranslate, 5.0f, 0.0f, 10.0f);
// 回転行列(Y軸回転)
D3DXMATRIX matRotate;
float angle = D3DXToRadian(45.0f); // 45度をラジアンに変換
D3DXMatrixRotationY(&matRotate, angle);
// World行列の合成(回転→移動)
D3DXMATRIX matWorld = matRotate * matTranslate;

このmatWorldをデバイスにセットすることで、モデルは指定した位置と向きに配置されます。

pDevice->SetTransform(D3DTS_WORLD, &matWorld);

View行列でカメラ設定

View行列は、ワールド座標系からカメラ座標系への変換を行います。

カメラの位置、注視点、上方向ベクトルを指定して、視点の向きを決定します。

Direct3D 9ではD3DXMatrixLookAtLH関数を使ってView行列を作成します。

引数はカメラ位置(eye)、注視点(at)、上方向ベクトル(up)です。

例:カメラを(0, 5, -10)に置き、原点を注視し、Y軸を上方向とする

D3DXVECTOR3 eye(0.0f, 5.0f, -10.0f);
D3DXVECTOR3 at(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
D3DXMATRIX matView;
D3DXMatrixLookAtLH(&matView, &eye, &at, &up);
pDevice->SetTransform(D3DTS_VIEW, &matView);

この設定により、カメラは指定位置から原点を見つめる視点となります。

Projection行列で視錐台定義

Projection行列は、3D空間の視錐台(カメラの視野範囲)を定義し、3D座標を2Dスクリーン座標に射影します。

主に透視投影(Perspective Projection)と正射影(Orthographic Projection)がありますが、ゲームなどでは透視投影が一般的です。

Direct3D 9ではD3DXMatrixPerspectiveFovLH関数で透視投影行列を作成します。

主なパラメータは視野角(FOV)、アスペクト比、近クリップ距離、遠クリップ距離です。

例:垂直方向の視野角を45度、アスペクト比を4:3、近クリップ距離1.0、遠クリップ距離1000.0に設定

float fov = D3DXToRadian(45.0f);
float aspect = 4.0f / 3.0f;
float nearZ = 1.0f;
float farZ = 1000.0f;
D3DXMATRIX matProj;
D3DXMatrixPerspectiveFovLH(&matProj, fov, aspect, nearZ, farZ);
pDevice->SetTransform(D3DTS_PROJECTION, &matProj);

この行列により、カメラの視野が設定され、遠くのオブジェクトは小さく、近くのオブジェクトは大きく見える透視効果が得られます。

行列の組み合わせとスケーリング

3D空間での最終的な頂点変換は、World、View、Projectionの3つの行列を掛け合わせて行います。

頂点はまずWorld行列でモデル空間からワールド空間に変換され、次にView行列でカメラ空間に変換され、最後にProjection行列でスクリーン空間に射影されます。

Direct3D 9では、これらの行列を個別にセットするだけで、内部で自動的に組み合わせて処理されます。

スケーリングの追加

モデルの大きさを変更したい場合は、スケーリング行列をWorld行列に組み込みます。

D3DXMatrixScaling関数でスケーリング行列を作成し、他の変換行列と掛け合わせます。

D3DXMATRIX matScale;
D3DXMatrixScaling(&matScale, 2.0f, 2.0f, 2.0f); // 2倍に拡大
// スケーリング→回転→移動の順に合成
D3DXMATRIX matWorld = matScale * matRotate * matTranslate;
pDevice->SetTransform(D3DTS_WORLD, &matWorld);

このように行列の掛け合わせ順序は重要で、一般的にスケーリング→回転→移動の順で行います。

順序が変わると結果の変換が異なるため注意してください。

これらの行列変換を適切に設定することで、3Dモデルの配置、カメラの視点、投影方法を自在にコントロールでき、リアルな3Dシーンを構築できます。

レンダリングループ

3Dグラフィックスの描画は、フレームごとに繰り返し行われるレンダリングループの中で実行されます。

Direct3D 9では、描画の開始と終了を明示的に管理し、バックバッファのクリアやフレームの表示を適切に行う必要があります。

ここでは、クリアと描画のシーケンス、BeginSceneEndSceneの役割、Presentによるフレーム表示、そしてフレームレート計測の方法について詳しく説明します。

クリアと描画のシーケンス

レンダリングループの基本的な流れは、まずバックバッファと深度バッファをクリアし、その後に描画処理を行い、最後に画面に表示するというシーケンスです。

バックバッファのクリアは、前フレームの描画結果を消去し、新しいフレームの描画準備を整えるために行います。

深度バッファも同時にクリアすることで、奥行き情報をリセットし、正しい描画順序を保証します。

Direct3D 9では、IDirect3DDevice9::Clearメソッドを使ってクリアします。

主な引数は以下の通りです。

  • クリア対象のバッファ(バックバッファ、深度バッファ、ステンシルバッファ)
  • クリア色(ARGB形式)
  • 深度値(通常は1.0f)
  • ステンシル値(通常は0)

クリアの例

pDevice->Clear(0, nullptr, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xFF000000, 1.0f, 0);

この例では、バックバッファD3DCLEAR_TARGETと深度バッファD3DCLEAR_ZBUFFERを黒色(0xFF000000)でクリアしています。

BeginSceneとEndSceneの役割

Direct3D 9では、描画処理をBeginSceneEndSceneの間に記述します。

これらのメソッドは、描画の開始と終了を明示的に示し、GPUに対して描画コマンドのバッチ処理を効率的に行うための制御を行います。

  • BeginScene

描画開始の通知。

これ以降に描画コマンドを発行します。

  • EndScene

描画終了の通知。

これで描画コマンドのバッチが確定します。

使用例

if (SUCCEEDED(pDevice->BeginScene())) {
    // 描画処理(頂点バッファのセット、描画コマンドなど)
    pDevice->EndScene();
}

BeginSceneが失敗した場合は描画を行わず、EndSceneは必ずBeginSceneの成功後に呼びます。

Presentでフレームを表示

EndSceneの後は、IDirect3DDevice9::Presentを呼び出して、バックバッファの内容をフロントバッファに転送し、画面に表示します。

これにより、描画したフレームがユーザーに見えるようになります。

Presentの主な引数は以下の通りです。

  • ソース矩形(通常はnullptrで全画面)
  • デスト矩形(通常はnullptrで全画面)
  • ウィンドウハンドル(通常は描画先のウィンドウ)
  • クリップ矩形(通常はnullptr)

使用例

pDevice->Present(nullptr, nullptr, nullptr, nullptr);

この呼び出しで、ダブルバッファリングされたバックバッファの内容が画面に表示されます。

フレームレート計測

リアルタイム描画では、フレームレート(FPS: Frames Per Second)を計測し、パフォーマンスの指標とすることが多いです。

Direct3D 9自体にはフレームレート計測機能はありませんが、Windowsの高精度タイマーを使って簡単に実装できます。

高精度タイマーを使ったFPS計測例

#include <windows.h>
#include <iostream>
class FPSCounter {
private:
    LARGE_INTEGER frequency;
    LARGE_INTEGER lastTime;
    int frameCount;
    float fps;
public:
    FPSCounter() : frameCount(0), fps(0.0f) {
        QueryPerformanceFrequency(&frequency);
        QueryPerformanceCounter(&lastTime);
    }
    void Frame() {
        frameCount++;
        LARGE_INTEGER currentTime;
        QueryPerformanceCounter(&currentTime);
        double elapsed = double(currentTime.QuadPart - lastTime.QuadPart) / frequency.QuadPart;
        if (elapsed >= 1.0) {
            fps = frameCount / static_cast<float>(elapsed);
            frameCount = 0;
            lastTime = currentTime;
            std::cout << "FPS: " << fps << std::endl;
        }
    }
    float GetFPS() const { return fps; }
};

このクラスをレンダリングループ内で毎フレーム呼び出すことで、1秒ごとにFPSを計算して表示します。

レンダリングループ内での使用例

FPSCounter fpsCounter;
while (msg.message != WM_QUIT) {
    if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    } else {
        pDevice->Clear(0, nullptr, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xFF000000, 1.0f, 0);
        if (SUCCEEDED(pDevice->BeginScene())) {
            // 描画処理
            pDevice->EndScene();
        }
        pDevice->Present(nullptr, nullptr, nullptr, nullptr);
        fpsCounter.Frame();
    }
}

このように、ClearBeginScene→描画→EndScenePresentの流れを繰り返しつつ、FPSCounterでフレームレートを計測します。

これらの手順を正しく実装することで、Direct3D 9のレンダリングループが安定し、スムーズな描画とパフォーマンスの監視が可能になります。

テクスチャマッピング

3Dグラフィックスにおいて、テクスチャマッピングはモデルの表面に画像を貼り付けてリアルな見た目を実現する重要な技術です。

Direct3D 9ではテクスチャのロードや管理、サンプラーステートの設定、ミップマップやフィルタリングの制御、さらにはアニメーションテクスチャやUVスクロールによる動的表現も可能です。

ここではそれぞれのポイントを詳しく解説します。

テクスチャのロードと管理

Direct3D 9でテクスチャを扱うには、IDirect3DDevice9::CreateTextureD3DXCreateTextureFromFileなどの関数を使ってテクスチャオブジェクトを生成します。

特にD3DXCreateTextureFromFileは画像ファイル(BMP、PNG、JPEGなど)から簡単にテクスチャを作成できる便利な関数です。

テクスチャのロード例

#include <d3dx9.h>
IDirect3DTexture9* pTexture = nullptr;
HRESULT LoadTexture(IDirect3DDevice9* pDevice, const char* filename)
{
    HRESULT hr = D3DXCreateTextureFromFileA(pDevice, filename, &pTexture);
    if (FAILED(hr)) {
        // ロード失敗時の処理
        return hr;
    }
    return S_OK;
}

この例では、filenameで指定した画像ファイルからテクスチャを作成し、pTextureに格納します。

ロードに失敗した場合はHRESULTでエラーコードが返ります。

テクスチャの管理

テクスチャはGPUメモリ上に存在するため、不要になったら必ずReleaseして解放します。

また、デバイスのリセット時にはD3DPOOL_DEFAULTで作成したテクスチャは再作成が必要になるため、D3DPOOL_MANAGEDを使うとDirect3Dが自動管理してくれます。

pTexture->Release();
pTexture = nullptr;

サンプラーステート設定

サンプラーステートは、テクスチャのサンプリング方法やフィルタリング、ラッピングモードなどを制御する設定です。

これにより、テクスチャの見た目や描画品質を調整できます。

主なサンプラーステートは以下の通りです。

サンプラーステート名説明
D3DSAMP_ADDRESSUU方向(横方向)のテクスチャ座標のラッピングモード
D3DSAMP_ADDRESSVV方向(縦方向)のテクスチャ座標のラッピングモード
D3DSAMP_MINFILTERテクスチャ縮小時のフィルタリング方法
D3DSAMP_MAGFILTERテクスチャ拡大時のフィルタリング方法
D3DSAMP_MIPFILTERミップマップ使用時のフィルタリング方法

ラッピングモードの例

  • D3DTADDRESS_WRAP:テクスチャ座標が0~1の範囲を超えた場合に繰り返す(タイル状に表示)
  • D3DTADDRESS_CLAMP:範囲外は端のピクセルを伸ばす
  • D3DTADDRESS_BORDER:範囲外は境界色で塗りつぶす

フィルタリングの例

  • D3DTEXF_POINT:最近傍補間(高速だがジャギーが目立つ)
  • D3DTEXF_LINEAR:線形補間(滑らか)
  • D3DTEXF_ANISOTROPIC:異方性フィルタリング(高品質)

サンプラーステート設定コード例

pDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
pDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
pDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
pDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
pDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

この例では、0番目のサンプラーに対してテクスチャ座標のラッピングを繰り返し、フィルタリングは線形補間を指定しています。

ミップマップとフィルタリング

ミップマップは、テクスチャの縮小表示時に画質を向上させるために、元のテクスチャを段階的に小さくした複数の画像を用意する技術です。

これにより、遠くのオブジェクトに貼られたテクスチャがぼやけたりジャギーが発生するのを防ぎます。

ミップマップの生成

D3DXCreateTextureFromFileなどの関数は、画像にミップマップが含まれていない場合でも自動的に生成します。

手動で生成する場合はD3DXFilterTextureを使います。

ミップマップの使用設定

ミップマップを有効にするには、サンプラーステートのD3DSAMP_MIPFILTERD3DTEXF_LINEARD3DTEXF_POINTを設定します。

無効にする場合はD3DTEXF_NONEを指定します。

フィルタリングの違い

  • ポイントフィルタリングD3DTEXF_POINT

最も単純で高速ですが、ジャギーが目立ちやすい。

  • 線形フィルタリングD3DTEXF_LINEAR

周囲のピクセルを補間し、滑らかな見た目になります。

  • 異方性フィルタリングD3DTEXF_ANISOTROPIC

斜め方向のテクスチャの歪みを抑え、高品質な表示を実現。

アニメーションテクスチャとUVスクロール

テクスチャマッピングは静的な画像だけでなく、動的な表現にも使えます。

アニメーションテクスチャやUVスクロールは、テクスチャ座標を時間とともに変化させることで、動きのある表現を実現します。

UVスクロールの実装例

UVスクロールは、頂点のテクスチャ座標にオフセットを加算し、テクスチャが流れるように見せる技術です。

Direct3D 9では、頂点シェーダーや固定機能パイプラインのテクスチャ変換行列を使って実装します。

固定機能パイプラインでのUVスクロール
D3DXMATRIX matTex;
float scrollSpeed = 0.5f; // 1秒あたりのスクロール速度
static float offset = 0.0f;
offset += scrollSpeed * deltaTime; // deltaTimeは前フレームからの経過時間
if (offset > 1.0f) offset -= 1.0f;
D3DXMatrixTranslation(&matTex, offset, 0.0f, 0.0f);
pDevice->SetTransform(D3DTS_TEXTURE0, &matTex);

このコードは、テクスチャ座標をX方向にスクロールさせています。

deltaTimeはフレーム間の時間差で、これを使ってスクロール速度を一定に保ちます。

アニメーションテクスチャ

複数のテクスチャをフレームごとに切り替えることでアニメーションを表現します。

例えば、スプライトシートからUV座標を切り替えたり、複数のテクスチャを順番にセットして描画します。

UV座標を切り替える例
// フレームごとのUVオフセットを計算
int frame = (int)(totalTime * frameRate) % frameCount;
float uOffset = frame * frameWidth;
D3DXMATRIX matTex;
D3DXMatrixTranslation(&matTex, uOffset, 0.0f, 0.0f);
pDevice->SetTransform(D3DTS_TEXTURE0, &matTex);

この方法で、1枚のテクスチャ内の異なる領域を順に表示し、アニメーション効果を出せます。

これらのテクスチャマッピング技術を活用することで、Direct3D 9の3Dシーンにリアリティや動きを加え、より魅力的なグラフィックス表現が可能になります。

ライティングとマテリアル

3Dグラフィックスにおいて、ライティング(光源)とマテリアル(材質)の設定は、シーンのリアリティや雰囲気を大きく左右します。

Direct3D 9では複数のライトタイプをサポートし、マテリアルの各種プロパティを細かく調整できます。

ここでは、ディレクショナルライト、ポイントライト、スポットライトの特徴と設定方法、さらにマテリアルプロパティの基本と環境光・エミッシブカラーについて詳しく解説します。

ディレクショナルライト

ディレクショナルライト(方向光)は、無限遠から一定方向に平行に照射される光源です。

太陽光のように、光源の位置は考慮せず、光の方向だけが重要です。

シーン全体に均一な光を当てるのに適しています。

設定例

D3DLIGHT9 dirLight = {};
dirLight.Type = D3DLIGHT_DIRECTIONAL;
// 光の方向(単位ベクトル)
dirLight.Direction = D3DXVECTOR3(-1.0f, -1.0f, -1.0f);
D3DXVec3Normalize(&dirLight.Direction, &dirLight.Direction);
// 光の色(拡散光)
dirLight.Diffuse = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
// 環境光やスペキュラは必要に応じて設定可能
dirLight.Ambient = D3DXCOLOR(0.2f, 0.2f, 0.2f, 1.0f);
dirLight.Specular = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
// ライトをデバイスにセット(ライト番号0を使用)
pDevice->SetLight(0, &dirLight);
pDevice->LightEnable(0, TRUE);

この例では、斜め下方向から白色の光を照射しています。

ディレクショナルライトは位置を持たないため、Positionは設定しません。

ポイントライト

ポイントライト(点光源)は、特定の位置から全方向に光を放射する光源です。

電球やランプのような光源を表現します。

距離に応じて光の強さが減衰します。

設定例

D3DLIGHT9 pointLight = {};
pointLight.Type = D3DLIGHT_POINT;
// 光源の位置
pointLight.Position = D3DXVECTOR3(0.0f, 5.0f, 0.0f);
// 光の色(拡散光)
pointLight.Diffuse = D3DXCOLOR(1.0f, 0.8f, 0.6f, 1.0f);
// 減衰パラメータ(距離による光の減衰)
pointLight.Attenuation0 = 1.0f;   // 定数減衰
pointLight.Attenuation1 = 0.1f;   // 線形減衰
pointLight.Attenuation2 = 0.01f;  // 二次減衰
// 光の範囲(影響範囲)
pointLight.Range = 20.0f;
pDevice->SetLight(1, &pointLight);
pDevice->LightEnable(1, TRUE);

ポイントライトは位置を持ち、距離に応じて光の強さが変化します。

Attenuation0Attenuation2で減衰の度合いを調整します。

スポットライト

スポットライトは、特定の位置から特定の方向に円錐状に光を照射する光源です。

懐中電灯や舞台照明のような効果を表現できます。

設定例

D3DLIGHT9 spotLight = {};
spotLight.Type = D3DLIGHT_SPOT;
// 光源の位置
spotLight.Position = D3DXVECTOR3(0.0f, 10.0f, 0.0f);
// 光の方向(単位ベクトル)
spotLight.Direction = D3DXVECTOR3(0.0f, -1.0f, 0.0f);
D3DXVec3Normalize(&spotLight.Direction, &spotLight.Direction);
// 拡散光の色
spotLight.Diffuse = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
// 減衰パラメータ
spotLight.Attenuation0 = 1.0f;
spotLight.Attenuation1 = 0.05f;
spotLight.Attenuation2 = 0.0f;
// 光の範囲
spotLight.Range = 30.0f;
// スポットライトの角度(ラジアン)
spotLight.Theta = D3DXToRadian(15.0f);  // 内側の円錐角度
spotLight.Phi = D3DXToRadian(30.0f);    // 外側の円錐角度
// スポットライトの減衰指数(光の集中度)
spotLight.Falloff = 1.0f;
pDevice->SetLight(2, &spotLight);
pDevice->LightEnable(2, TRUE);

スポットライトはThetaPhiで光の中心角度と外側角度を設定し、Falloffで光の減衰の鋭さを調整します。

マテリアルプロパティの設定

マテリアルは、オブジェクトの表面の光の反射特性を定義します。

Direct3D 9のD3DMATERIAL9構造体で設定し、拡散反射、環境光反射、鏡面反射、エミッシブカラーなどを指定できます。

マテリアルの主なメンバー

メンバー名説明
Diffuse拡散反射色(光源の色を反射)
Ambient環境光反射色(周囲の光の影響)
Specular鏡面反射色(光沢の反射)
Emissive自己発光色(光を放つ色)
Power鏡面反射の鋭さ(光沢の強さ)

マテリアル設定例

D3DMATERIAL9 material = {};
material.Diffuse = D3DXCOLOR(0.8f, 0.7f, 0.6f, 1.0f);
material.Ambient = D3DXCOLOR(0.3f, 0.3f, 0.3f, 1.0f);
material.Specular = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
material.Emissive = D3DXCOLOR(0.0f, 0.0f, 0.0f, 1.0f);
material.Power = 20.0f;
pDevice->SetMaterial(&material);

環境光とエミッシブカラー

  • 環境光(Ambient)

シーン全体に均一に存在する光で、影になった部分でも完全に暗くならないようにします。

マテリアルのAmbientとライトのAmbientが組み合わさって効果を発揮します。

  • エミッシブカラー(Emissive)

オブジェクト自身が光を放つ色です。

ライトの影響を受けずに常に一定の色で発光しているように見えます。

例えば、光るディスプレイやランプの表現に使います。

これらのライティングとマテリアルの設定を組み合わせることで、Direct3D 9のシーンに多彩な光の表現を加え、リアルで魅力的な3Dグラフィックスを実現できます。

カリングとデプステスト

3Dレンダリングにおいて、不要なポリゴンの描画を省略したり、正しい奥行き関係を保つための技術としてカリングとデプステストがあります。

Direct3D 9ではこれらを効率的に制御でき、描画パフォーマンスの向上や正確な描画結果の実現に役立ちます。

ここではバックフェイスカリング、Zバッファの有効化、深度比較関数、そしてステンシルバッファの活用例について詳しく説明します。

バックフェイスカリング

バックフェイスカリングは、カメラから見て裏側(背面)にある三角形を描画しない処理です。

これにより、視界に映らないポリゴンの描画を省略し、レンダリング負荷を軽減できます。

Direct3D 9では、IDirect3DDevice9::SetRenderStateでカリングモードを設定します。

主なカリングモードは以下の通りです。

定数名説明
D3DCULL_NONEカリングなし(両面描画)
D3DCULL_CW時計回りのポリゴンをカリング
D3DCULL_CCW反時計回りのポリゴンをカリング

三角形の頂点の並び順(頂点インデックスの順序)によって、表面か裏面かが判定されます。

通常、Direct3DのデフォルトはD3DCULL_CCWで、反時計回りの頂点が表面とみなされます。

バックフェイスカリング設定例

pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);

これにより、反時計回りに頂点が並ぶ三角形のみが描画され、時計回りの三角形はカリングされます。

Zバッファ有効化

Zバッファ(深度バッファ)は、各ピクセルの奥行き情報を保持し、正しい描画順序を保証するためのバッファです。

これを有効にすることで、手前のオブジェクトが奥のオブジェクトを隠す正確な描画が可能になります。

Direct3D 9でZバッファを有効にするには、デバイス作成時にD3DPRESENT_PARAMETERSEnableAutoDepthStencilTRUEにし、AutoDepthStencilFormatに適切なフォーマット(例:D3DFMT_D24S8)を指定します。

さらに、レンダリング時に以下のレンダーステートを設定します。

pDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
pDevice->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
  • D3DRS_ZENABLE:Zバッファの使用を有効化
  • D3DRS_ZWRITEENABLE:Zバッファへの書き込みを有効化

これにより、ピクセルごとに深度テストが行われ、正しい奥行きの描画が実現します。

深度比較関数

深度比較関数は、描画しようとするピクセルの深度値とZバッファに保存されている値を比較し、描画の可否を決定します。

Direct3D 9ではD3DRS_ZFUNCで設定します。

主な比較関数は以下の通りです。

定数名説明
D3DCMP_NEVER常に描画しない
D3DCMP_LESS新しい深度値が小さい場合に描画
D3DCMP_EQUAL深度値が等しい場合に描画
D3DCMP_LESSEQUAL新しい深度値が小さいか等しい場合に描画
D3DCMP_GREATER新しい深度値が大きい場合に描画
D3DCMP_NOTEQUAL深度値が異なる場合に描画
D3DCMP_GREATEREQUAL新しい深度値が大きいか等しい場合に描画
D3DCMP_ALWAYS常に描画

通常はD3DCMP_LESSEQUALが使われます。

これは、手前にあるピクセルが描画され、奥のピクセルは隠される動作を実現します。

深度比較関数設定例

pDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);

ステンシルバッファの活用例

ステンシルバッファは、ピクセルごとに追加のマスク情報を保持し、描画の可否を制御するためのバッファです。

Zバッファと組み合わせて、複雑な描画効果やマスク処理に利用されます。

Direct3D 9では、D3DPRESENT_PARAMETERSAutoDepthStencilFormatにステンシルバッファ付きのフォーマット(例:D3DFMT_D24S8)を指定することで、深度バッファとステンシルバッファを同時に有効化できます。

ステンシルバッファの基本的な使い方

  • ステンシルテストの有効化
pDevice->SetRenderState(D3DRS_STENCILENABLE, TRUE);
  • ステンシルの比較関数設定
pDevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS);
  • ステンシルの参照値設定
pDevice->SetRenderState(D3DRS_STENCILREF, 1);
  • ステンシルマスク設定
pDevice->SetRenderState(D3DRS_STENCILMASK, 0xFF);
  • ステンシル操作設定(描画成功時、失敗時など)
pDevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE);
pDevice->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP);
pDevice->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP);

ステンシルバッファ活用例:鏡面反射の実装

  1. 鏡面の形状をステンシルバッファにマークするために描画し、ステンシル値を設定。
  2. ステンシルテストで鏡面領域のみ描画を許可し、反射オブジェクトを描画。
  3. ステンシルテストを無効化して通常描画に戻します。

このように、ステンシルバッファを使うことで、特定の領域だけに描画を制限したり、複雑なエフェクトを実現できます。

これらのカリングとデプステストの技術を適切に活用することで、Direct3D 9の描画効率を高めつつ、正確で美しい3Dシーンを構築できます。

アルファブレンディング

アルファブレンディングは、透明度や半透明効果を表現するための重要な技術です。

Direct3D 9では、ブレンドファクタの組み合わせを細かく設定でき、様々な合成効果を実現できます。

ここでは、ブレンドファクタの組み合わせ、半透明オブジェクトの描画順序の注意点、そして加算合成と減算合成の使い分けについて詳しく解説します。

ブレンドファクタの組み合わせ

アルファブレンディングは、ソースピクセル(描画対象のピクセル)とデストピクセル(既にフレームバッファにあるピクセル)の色を特定の比率で合成します。

Direct3D 9では、SetRenderStateでブレンドファクタを指定します。

主なレンダーステートは以下の通りです。

  • D3DRS_ALPHABLENDENABLE

アルファブレンディングの有効化(TRUE/ FALSE)

  • D3DRS_SRCBLEND

ソースブレンドファクタの指定

  • D3DRS_DESTBLEND

デストブレンドファクタの指定

主なブレンドファクタ一覧

定数名説明
D3DBLEND_ZERO0(影響なし)
D3DBLEND_ONE1(そのまま)
D3DBLEND_SRCCOLORソースカラー
D3DBLEND_INVSRCCOLOR1 – ソースカラー
D3DBLEND_SRCALPHAソースのアルファ値
D3DBLEND_INVSRCALPHA1 – ソースのアルファ値
D3DBLEND_DESTALPHAデストのアルファ値
D3DBLEND_INVDESTALPHA1 – デストのアルファ値

典型的なアルファブレンディング設定例(透過)

pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
pDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
pDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

この設定は、ソースのアルファ値に応じて透過度を調整し、背景色と適切に合成します。

計算式は以下のようになります。

出力色=ソース色×α+デスト色×(1α)

半透明オブジェクト描画順序

半透明オブジェクトの描画では、描画順序が非常に重要です。

正しい順序で描画しないと、透過表現が崩れたり、描画結果が不自然になります。

描画順序の基本ルール

  1. 不透明オブジェクトを先に描画する

Zバッファを使って正確に描画し、背景を確定させます。

  1. 半透明オブジェクトを後に描画する

半透明オブジェクトはZバッファの書き込みを無効にし、Zテストは有効にして描画します。

  1. 半透明オブジェクトはカメラから遠い順に描画する

奥から手前へ描画することで、正しいブレンド結果が得られます。

半透明オブジェクトの描画設定例

// Zバッファはテストのみ有効、書き込みは無効
pDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
pDevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
// アルファブレンディング有効
pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
pDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
pDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
// 半透明オブジェクトの描画処理

このように描画順序とZバッファの設定を適切に管理することで、半透明表現の破綻を防げます。

加算合成と減算合成

アルファブレンディングには、透過以外にも加算合成や減算合成といった特殊な合成方法があります。

これらは光の効果や特殊エフェクトに使われます。

加算合成(Additive Blending)

加算合成は、ソース色とデスト色を単純に加算します。

光の輝きや炎、爆発などのエフェクトに適しています。

pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
pDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
pDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);

計算式は以下の通りです。

出力色=ソース色+デスト色

色が1.0を超えるとクリッピングされるため、明るい効果が得られます。

減算合成(Subtractive Blending)

減算合成は、デスト色からソース色を減算します。

暗い影や特殊な光の効果に使われます。

pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
pDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_INVDESTCOLOR);
pDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO);

計算式は以下の通りです。

出力色=デスト色ソース色

減算合成は使い方に注意が必要で、色が負になると黒にクリップされます。

これらのアルファブレンディングの設定と描画順序の管理を適切に行うことで、Direct3D 9で多彩な透明表現や光のエフェクトを実現できます。

シェーダー入門

Direct3D 9では、固定機能パイプラインに加えてプログラマブルパイプラインが導入され、HLSL(High-Level Shader Language)を使ったシェーダープログラミングが可能です。

シェーダーを活用することで、頂点変換やピクセルの色計算を自由にカスタマイズでき、より高度でリアルな表現が実現できます。

ここではHLSLの基本構文、Shader Model 2.0と3.0の違い、頂点シェーダーでの頂点変換、ピクセルシェーダーでのライティング、そして定数バッファのバインド方法について詳しく解説します。

HLSLの基本構文

HLSLはC言語に似た構文を持つシェーダー言語で、頂点シェーダーやピクセルシェーダーのプログラムを記述します。

基本的な構造は関数定義で、入力と出力の構造体を使ってデータを受け渡します。

頂点シェーダーの簡単な例

struct VS_INPUT {
    float4 position : POSITION;
    float4 color : COLOR0;
};
struct VS_OUTPUT {
    float4 position : POSITION;
    float4 color : COLOR0;
};
VS_OUTPUT main(VS_INPUT input) {
    VS_OUTPUT output;
    output.position = input.position; // 位置をそのまま通す
    output.color = input.color;       // 色もそのまま通す
    return output;
}
  • structで入力・出力のデータ形式を定義
  • POSITIONCOLOR0はセマンティクスと呼ばれ、データの意味を示す
  • main関数がシェーダーのエントリーポイント

ピクセルシェーダーの簡単な例

struct PS_INPUT {
    float4 color : COLOR0;
};
float4 main(PS_INPUT input) : COLOR {
    return input.color; // 入力色をそのまま出力
}

HLSLでは型としてfloat, float2, float3, float4などのベクトル型が使われます。

Shader Model 2.0と3.0の違い

Shader Model(SM)はシェーダーの機能仕様を示すバージョンで、Direct3D 9では主にSM2.0とSM3.0が使われます。

項目Shader Model 2.0Shader Model 3.0
命令数制限64命令(頂点シェーダー)256命令(頂点シェーダー)
条件分岐制限あり条件分岐が可能
ループ制限あり動的ループが可能
テクスチャサンプル数最大4最大16
定数バッファの数少ない多い
パフォーマンス低め高め

SM3.0はより複雑なシェーダーを記述でき、リアルタイムで高度なエフェクトを実現しやすくなっています。

ただし、対応GPUが必要です。

頂点シェーダーでの頂点変換

頂点シェーダーは、頂点の位置や属性を変換する役割を持ちます。

通常はワールド行列、ビュー行列、プロジェクション行列を使って頂点座標を変換し、スクリーン空間に投影します。

頂点シェーダーの例(ワールド・ビュー・プロジェクション変換)

cbuffer MatrixBuffer {
    float4x4 world;
    float4x4 view;
    float4x4 projection;
};
struct VS_INPUT {
    float4 position : POSITION;
    float4 color : COLOR0;
};
struct VS_OUTPUT {
    float4 position : POSITION;
    float4 color : COLOR0;
};
VS_OUTPUT main(VS_INPUT input) {
    VS_OUTPUT output;
    float4 worldPos = mul(input.position, world);
    float4 viewPos = mul(worldPos, view);
    output.position = mul(viewPos, projection);
    output.color = input.color;
    return output;
}
  • cbufferで定数バッファを定義し、行列を渡す
  • mul関数で行列とベクトルの乗算を行う
  • 変換後の座標をPOSITIONセマンティクスで出力

ピクセルシェーダーでのライティング

ピクセルシェーダーは、ピクセル単位で色や光の計算を行います。

簡単なディレクショナルライトによるライティング例を示します。

cbuffer LightBuffer {
    float3 lightDir;
    float4 lightColor;
    float4 ambientColor;
};
struct PS_INPUT {
    float4 color : COLOR0;
    float3 normal : NORMAL;
};
float4 main(PS_INPUT input) : COLOR {
    float NdotL = saturate(dot(normalize(input.normal), -lightDir));
    float4 diffuse = lightColor * NdotL;
    float4 ambient = ambientColor;
    float4 finalColor = input.color * (diffuse + ambient);
    return saturate(finalColor);
}
  • dot関数で法線と光の方向の内積を計算
  • saturateで値を0~1にクランプ
  • 拡散光と環境光を合成して最終色を算出

定数バッファのバインド

定数バッファは、シェーダーに定数データ(行列やライト情報など)を効率的に渡すための仕組みです。

Direct3D 9ではID3DXEffectSetVertexShaderConstantFSetPixelShaderConstantFを使って定数をセットします。

定数バッファのセット例(Direct3D 9)

// 行列をfloat配列に変換
float worldMatrix[16];
D3DXMatrixTranspose((D3DXMATRIX*)worldMatrix, &matWorld);
pDevice->SetVertexShaderConstantF(0, worldMatrix, 4); // レジスタ0~3にセット
float viewMatrix[16];
D3DXMatrixTranspose((D3DXMATRIX*)viewMatrix, &matView);
pDevice->SetVertexShaderConstantF(4, viewMatrix, 4); // レジスタ4~7にセット
float projMatrix[16];
D3DXMatrixTranspose((D3DXMATRIX*)projMatrix, &matProj);
pDevice->SetVertexShaderConstantF(8, projMatrix, 4); // レジスタ8~11にセット
  • 行列はHLSLのfloat4x4と互換性を持つため、転置して渡す必要がある
  • SetVertexShaderConstantFで頂点シェーダーの定数レジスタにセット
  • ピクセルシェーダー用はSetPixelShaderConstantFを使う

これらの基礎を押さえることで、Direct3D 9のHLSLシェーダープログラミングを始められ、より自由で高度なグラフィックス表現が可能になります。

エフェクトフレームワーク

Direct3D 9のエフェクトフレームワークは、HLSLシェーダーの管理や複雑なレンダリングパスの制御を簡素化する仕組みです。

.fxファイルにシェーダーコードやレンダリング設定をまとめて記述し、APIを通じて効率的に切り替えやパラメータ制御が可能です。

ここでは、.fxファイルの構成、テクニックとパスの切り替え、そしてマルチパスレンダリングについて詳しく解説します。

.fxファイルの構成

.fxファイルは、エフェクトフレームワークで使用されるシェーダーやレンダリングステート、定数の定義をまとめたテキストファイルです。

主に以下の要素で構成されます。

  • シェーダーコード

頂点シェーダー(Vertex Shader)やピクセルシェーダー(Pixel Shader)のHLSLコードを含みます。

関数として定義し、後述のテクニックで使用します。

  • パラメータ宣言

シェーダーに渡す定数やテクスチャなどのパラメータを宣言します。

float4x4 WorldViewProj;texture DiffuseMap;など。

  • テクニック(technique)

複数のレンダリングパスをまとめた単位で、描画方法の切り替えに使います。

  • パス(pass)

テクニック内での個別のレンダリングステップ。

シェーダーの設定やレンダーステートを指定します。

.fxファイルの例

// パラメータ宣言
float4x4 WorldViewProj;
texture DiffuseMap;
sampler2D DiffuseSampler = sampler_state {
    Texture = <DiffuseMap>;
    MinFilter = Linear;
    MagFilter = Linear;
};
// 頂点シェーダー
struct VS_INPUT {
    float4 Position : POSITION;
    float2 TexCoord : TEXCOORD0;
};
struct VS_OUTPUT {
    float4 Position : POSITION;
    float2 TexCoord : TEXCOORD0;
};
VS_OUTPUT VSMain(VS_INPUT input) {
    VS_OUTPUT output;
    output.Position = mul(input.Position, WorldViewProj);
    output.TexCoord = input.TexCoord;
    return output;
}
// ピクセルシェーダー
float4 PSMain(VS_OUTPUT input) : COLOR {
    return tex2D(DiffuseSampler, input.TexCoord);
}
// テクニック定義
technique BasicTechnique {
    pass P0 {
        VertexShader = compile vs_3_0 VSMain();
        PixelShader = compile ps_3_0 PSMain();
    }
}

この例では、行列やテクスチャをパラメータとして宣言し、頂点シェーダーとピクセルシェーダーを定義。

BasicTechniqueの中に1つのパスP0を持ち、シェーダーをコンパイルして指定しています。

テクニックとパスの切り替え

エフェクトフレームワークでは、複数のテクニックを定義しておき、描画時に切り替えることができます。

これにより、異なるシェーダーやレンダリング設定を簡単に切り替えられます。

テクニックの切り替え例(C++)

ID3DXEffect* pEffect = nullptr;
D3DXCreateEffectFromFile(pDevice, L"effect.fx", nullptr, nullptr, 0, nullptr, &pEffect, nullptr);
UINT numTechniques = pEffect->GetNumTechniques();
for (UINT i = 0; i < numTechniques; ++i) {
    D3DXHANDLE hTechnique = pEffect->GetTechnique(i);
    pEffect->SetTechnique(hTechnique);
    UINT numPasses = 0;
    pEffect->Begin(&numPasses, 0);
    for (UINT pass = 0; pass < numPasses; ++pass) {
        pEffect->BeginPass(pass);
        // 描画処理
        pEffect->EndPass();
    }
    pEffect->End();
}
  • SetTechniqueで使用するテクニックを指定
  • Beginでパス数を取得し、BeginPass/EndPassで各パスを実行
  • 複数のテクニックを切り替えて異なる描画を行うことが可能

マルチパスレンダリング

マルチパスレンダリングは、1つのテクニック内で複数のパスを使い、段階的に描画処理を行う手法です。

例えば、1パス目でジオメトリの描画、2パス目でエフェクトの合成、3パス目でポストプロセスを行うなど、複雑な表現が可能になります。

マルチパスの例(.fxファイル)

technique MultiPassTechnique {
    pass P0 {
        VertexShader = compile vs_3_0 VSMain();
        PixelShader = compile ps_3_0 PSMain();
        // レンダーステート設定例
        AlphaBlendEnable = FALSE;
        ZEnable = TRUE;
    }
    pass P1 {
        VertexShader = compile vs_3_0 VSMain();
        PixelShader = compile ps_3_0 PSGlow();
        AlphaBlendEnable = TRUE;
        SrcBlend = SRCALPHA;
        DestBlend = INVSRCALPHA;
        ZEnable = FALSE;
    }
}
  • P0パスで通常の描画を行い、Zバッファを有効にして不透明描画
  • P1パスで加算ブレンドを使い、グロー効果などのエフェクトを重ねる
  • 複数パスを順に実行することで複雑な効果を実現

マルチパスレンダリングのC++での実行例

UINT numPasses = 0;
pEffect->Begin(&numPasses, 0);
for (UINT pass = 0; pass < numPasses; ++pass) {
    pEffect->BeginPass(pass);
    // 描画処理(頂点バッファのセット、DrawPrimitiveなど)
    pEffect->EndPass();
}
pEffect->End();

マルチパスレンダリングは、1フレーム内で複数回描画を行うためパフォーマンスに影響しますが、表現力を大幅に高めることができます。

エフェクトフレームワークを活用することで、シェーダーの管理や複雑なレンダリング処理を効率化し、Direct3D 9で高度なグラフィックス表現を実現できます。

リソース管理とデバイスロスト対策

Direct3D 9の開発において、リソース管理とデバイスロスト(デバイスの一時的な無効化)への対策は非常に重要です。

GPUリソースの効率的な管理や、デバイスがロストした際の適切な復旧処理を行わないと、アプリケーションの安定性やパフォーマンスに悪影響を及ぼします。

ここでは、テクスチャプールとメモリ戦略、OnLostDeviceOnResetDeviceの処理フロー、そしてCOM参照カウントと解放タイミングについて詳しく解説します。

テクスチャプールとメモリ戦略

Direct3D 9では、リソースの管理に「プール」という概念があり、リソースの種類や寿命に応じて適切なプールを選択することが推奨されます。

特にテクスチャはメモリ消費が大きいため、プールの選択がパフォーマンスや安定性に直結します。

主なプールの種類

プール名説明
D3DPOOL_DEFAULTGPUメモリ上に作成され、デバイスロスト時にリセットが必要でしょう。高速だが管理が難しい。
D3DPOOL_MANAGEDシステムメモリとGPUメモリの両方に保持され、Direct3Dが自動的に管理。デバイスロストに強い。
D3DPOOL_SYSTEMMEMシステムメモリ上に作成され、GPUから直接アクセスできない。主にリソースのバックアップ用。

メモリ戦略のポイント

  • 頻繁に更新するリソースはD3DPOOL_DEFAULTに作成

頂点バッファやインデックスバッファ、動的テクスチャなど。

  • 静的なリソースはD3DPOOL_MANAGEDに作成

テクスチャやメッシュなど、変更が少ないもの。

  • D3DPOOL_MANAGEDは自動的にデバイスロストを吸収

開発者がリセット処理を意識する必要が少ない。

  • D3DPOOL_DEFAULTのリソースはOnLostDeviceで解放し、OnResetDeviceで再作成が必要

このようにプールを使い分けることで、メモリ使用効率と安定性を両立できます。

OnLostDeviceとOnResetDeviceのフロー

Direct3D 9のデバイスは、画面モードの変更やウィンドウの最小化などで「ロスト」状態になることがあります。

この状態ではD3DERR_DEVICELOSTが返され、描画やリソース操作ができなくなります。

復旧にはデバイスのリセットが必要で、その際にリソースの再初期化が求められます。

OnLostDeviceの役割

  • D3DPOOL_DEFAULTで作成したリソース(頂点バッファ、テクスチャなど)を解放します
  • エフェクトID3DXEffectOnLostDeviceメソッドを呼び、内部リソースを解放します
  • 描画処理を停止し、デバイスのリセット準備を行います

OnResetDeviceの役割

  • D3DPRESENT_PARAMETERSを適切に設定し、IDirect3DDevice9::Resetを呼びデバイスをリセットします
  • 解放したリソースを再作成します
  • エフェクトのOnResetDeviceを呼び、内部リソースを再初期化します
  • 描画処理を再開します

フロー例

HRESULT hr = pDevice->TestCooperativeLevel();
if (hr == D3DERR_DEVICELOST) {
    // デバイスロスト中は何もしない
    return;
}
if (hr == D3DERR_DEVICENOTRESET) {
    // OnLostDevice処理
    ReleaseDefaultPoolResources();
    pEffect->OnLostDevice();
    // デバイスリセット
    hr = pDevice->Reset(&d3dpp);
    if (SUCCEEDED(hr)) {
        // OnResetDevice処理
        CreateDefaultPoolResources();
        pEffect->OnResetDevice();
    }
}

このように、デバイスの状態を監視し、適切にリソース管理を行うことが安定動作の鍵です。

COM参照カウントと解放タイミング

Direct3D 9のインターフェースはCOM(Component Object Model)ベースで実装されており、リソースは参照カウント方式で管理されています。

AddRefで参照カウントを増やし、Releaseで減らします。

参照カウントが0になるとリソースは解放されます。

解放タイミングの注意点

  • リソースを使い終わったら必ずReleaseを呼ぶ

メモリリーク防止の基本。

  • 複数のオブジェクトで同じリソースを共有する場合は参照カウントに注意

共有先すべてでReleaseされるまで解放されない。

  • デバイスロスト時はD3DPOOL_DEFAULTのリソースを明示的に解放し、再作成する

これらは自動管理されないため。

例:頂点バッファの解放

if (pVertexBuffer) {
    pVertexBuffer->Release();
    pVertexBuffer = nullptr;
}

例:エフェクトの解放

if (pEffect) {
    pEffect->Release();
    pEffect = nullptr;
}

COMの参照カウント管理を正しく行うことで、リソースの過剰消費やクラッシュを防ぎ、安定したアプリケーションを構築できます。

これらのリソース管理とデバイスロスト対策を適切に実装することで、Direct3D 9アプリケーションの安定性とパフォーマンスを維持しやすくなります。

デバッグとプロファイリング

Direct3D 9の開発では、描画の不具合やパフォーマンスの問題を効率的に発見・解決するために、デバッグツールやプロファイリングツールの活用が欠かせません。

ここでは、DirectX Debug Runtimeの使い方、PIX for Windowsによるフレーム解析、そしてよくあるコモンエラーとその解決策について詳しく解説します。

DirectX Debug Runtimeの活用

DirectX Debug Runtimeは、Direct3DのAPI呼び出しを監視し、エラーや警告を詳細にレポートしてくれる開発者向けのランタイムです。

通常のリリース版ランタイムよりも多くのチェックを行い、問題の早期発見に役立ちます。

有効化方法

  1. DirectX SDKのインストール

DirectX SDK(June 2010など)をインストールすると、Debug Runtimeが含まれます。

  1. DirectX Control Panelの起動

dxdiagコマンドやSDKのツールフォルダからdxcpl.exeを起動。

  1. Debug Layerの設定

「Direct3D 9」タブで「Use Debug Version of Direct3D 9」をチェックし、デバッグレベルを設定(通常は「Maximum」)。

  1. アプリケーションの再起動

Debug Runtimeが有効になった状態でアプリを実行。

主な機能

  • API呼び出しの検証

不正なパラメータや状態での呼び出しを検出。

  • リソースリークの警告

解放されていないCOMオブジェクトを報告。

  • パフォーマンス警告

非推奨APIの使用やパフォーマンス低下の原因を指摘。

出力例(Visual Studioの出力ウィンドウ)

D3D9: WARNING: SetTexture: Invalid texture pointer.
D3D9: ERROR: CreateVertexBuffer: Invalid usage flags.

これらのメッセージを参考にコードを修正することで、バグの早期解決が可能です。

PIX for Windowsでのフレーム解析

PIX for Windowsは、Microsoftが提供するDirect3Dアプリケーションのパフォーマンス解析ツールです。

フレーム単位でGPUの動作をキャプチャし、詳細なレンダリングコールやリソース使用状況を可視化できます。

主な機能

  • フレームキャプチャ

実行中のアプリケーションから特定フレームをキャプチャし、GPUコマンドを解析。

  • ドローコールの詳細表示

各描画コールのパラメータや使用シェーダー、頂点・インデックスバッファの内容を確認。

  • パフォーマンスカウンタ

GPU負荷やメモリ使用量、パイプラインステージのボトルネックを特定。

  • リソースビューア

テクスチャやバッファの状態を詳細に調査。

使い方の概要

  1. PIXを起動し、対象の実行ファイルを指定して起動。
  2. キャプチャしたいフレームで「Capture Frame」を実行。
  3. キャプチャ後、フレームのレンダリングコマンドを詳細に解析。
  4. 問題のある描画コールやリソースを特定し、コード修正に活かす。

PIXはDirect3D 9だけでなく、Direct3D 11や12にも対応しており、幅広い環境で利用可能です。

コモンエラーと解決策

Direct3D 9開発でよく遭遇するエラーとその対処法をいくつか紹介します。

D3DERR_DEVICELOST(デバイスロスト)

  • 原因

フルスクリーン切替やウィンドウの最小化などでデバイスがロスト状態になります。

  • 対策

TestCooperativeLevelで状態を監視し、OnLostDeviceでリソース解放、Resetでデバイス再初期化、OnResetDeviceでリソース再作成を行います。

E_OUTOFMEMORY(メモリ不足)

  • 原因

テクスチャやバッファのサイズが大きすぎる、またはリソースリーク。

  • 対策

リソースのサイズを見直し、不要なリソースは確実にReleaseします。

Debug Runtimeでリークを検出。

シェーダーコンパイルエラー

  • 原因

HLSLコードの文法ミスや非対応の機能使用。

  • 対策

コンパイルログを確認し、エラー箇所を修正。

Shader Modelのバージョンを適切に設定。

頂点フォーマット不一致

  • 原因

頂点バッファのフォーマットとFVFやシェーダーの入力が合っていない。

  • 対策

頂点構造体、FVF、シェーダーの入力構造体を一致させます。

Debug Runtimeの警告を参照。

描画が真っ黒または表示されない

  • 原因

ライトやマテリアルの設定ミス、行列の設定忘れ、テクスチャの未設定など。

  • 対策

ライトの有効化、マテリアルの設定、SetTransformの確認、テクスチャのバインドを見直します。

これらのデバッグ・プロファイリング手法とエラー対策を活用することで、Direct3D 9アプリケーションの品質向上と開発効率の改善が期待できます。

高度なレンダリング例

Direct3D 9では、基本的な描画に加えて、複数のテクスチャを組み合わせるマルチテクスチャリングや、環境反射を表現するキューブマッピング、さらにレンダーターゲットを活用したポストプロセス効果など、高度なレンダリング技術が利用可能です。

ここではそれらの技術について具体的な例を交えて解説します。

マルチテクスチャリング

マルチテクスチャリングは、複数のテクスチャを同時に使用して、より複雑な表面表現を実現する技術です。

例えば、基本のディフューズテクスチャに加えて、ライトマップやノーマルマップを重ねることができます。

Direct3D 9では、複数のテクスチャステージを使い、SetTextureSetTextureStageStateで各テクスチャの設定や合成方法を指定します。

マルチテクスチャリングの設定例

pDevice->SetTexture(0, pDiffuseTexture);  // ステージ0にディフューズテクスチャ
pDevice->SetTexture(1, pLightMapTexture); // ステージ1にライトマップ
// ステージ0の設定(基本テクスチャ)
pDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
pDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
// ステージ1の設定(ライトマップを乗算)
pDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE);
pDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
// テクスチャステージの終了
pDevice->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_DISABLE);

この例では、ステージ0でディフューズカラーと頂点カラーを乗算し、ステージ1でライトマップをさらに乗算しています。

これにより、光の影響をリアルに表現できます。

キューブマッピングでの環境反射

キューブマッピングは、6面のテクスチャ(キューブマップ)を使って環境の反射を表現する技術です。

鏡面や金属のような反射効果をリアルに描画できます。

キューブマップの作成と設定

キューブマップはIDirect3DCubeTexture9インターフェースで管理し、6つの面にそれぞれテクスチャをセットします。

IDirect3DCubeTexture9* pCubeTexture = nullptr;
HRESULT hr = D3DXCreateCubeTextureFromFile(pDevice, L"env_map.dds", &pCubeTexture);

シェーダーでの反射計算例(HLSL)

samplerCUBE EnvMapSampler;
struct VS_OUTPUT {
    float4 Position : POSITION;
    float3 ReflectDir : TEXCOORD0;
};
VS_OUTPUT VSMain(float3 position : POSITION, float3 normal : NORMAL, float3 eyePos : TEXCOORD1) {
    VS_OUTPUT output;
    output.Position = mul(float4(position, 1.0f), WorldViewProj);
    float3 I = normalize(position - eyePos);
    output.ReflectDir = reflect(I, normalize(normal));
    return output;
}
float4 PSMain(VS_OUTPUT input) : COLOR {
    return texCUBE(EnvMapSampler, input.ReflectDir);
}

このシェーダーでは、視線ベクトルと法線から反射ベクトルを計算し、キューブマップから反射色をサンプリングしています。

RTT(Render To Texture)とポストプロセス

RTTは、レンダーターゲットとしてテクスチャを指定し、シーンの一部または全体をテクスチャに描画する技術です。

これを利用して、ポストプロセスエフェクト(画面全体の効果)を実現します。

RTTの基本的な流れ

  1. レンダーターゲット用のテクスチャを作成
IDirect3DTexture9* pRenderTargetTexture = nullptr;
pDevice->CreateTexture(width, height, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &pRenderTargetTexture, nullptr);
  1. レンダーターゲットサーフェスを取得
IDirect3DSurface9* pRenderTargetSurface = nullptr;
pRenderTargetTexture->GetSurfaceLevel(0, &pRenderTargetSurface);
  1. レンダーターゲットを切り替え
IDirect3DSurface9* pOldRenderTarget = nullptr;
pDevice->GetRenderTarget(0, &pOldRenderTarget);
pDevice->SetRenderTarget(0, pRenderTargetSurface);
  1. シーンを描画
  2. レンダーターゲットを元に戻す
pDevice->SetRenderTarget(0, pOldRenderTarget);
pOldRenderTarget->Release();
pRenderTargetSurface->Release();
  1. ポストプロセス用にテクスチャを使用

このテクスチャをスクリーンに描画し、シェーダーでエフェクトをかけます。

ブルーム効果

ブルームは、明るい部分がにじんで輝いて見える効果です。

RTTでシーンをレンダリングし、明るい部分だけを抽出してぼかし、元のシーンに加算合成します。

ブルームの簡単な手順
  • シーンをRTTに描画
  • 明るい部分を抽出(閾値処理)
  • ガウシアンブラーでぼかし
  • 元のシーンに加算合成

ガウシアンブラー

ガウシアンブラーは、画像をぼかすためのフィルタで、ポストプロセスで多用されます。

横方向と縦方向の2パスに分けて処理することで効率的に実装できます。

ガウシアンブラーのHLSL例(横方向)
sampler2D inputTexture;
float2 texelSize; // 1.0 / テクスチャサイズ
float4 PSBlurH(float2 texCoord : TEXCOORD) : COLOR {
    float4 color = float4(0,0,0,0);
    color += tex2D(inputTexture, texCoord + float2(-2.0 * texelSize.x, 0)) * 0.1216216;
    color += tex2D(inputTexture, texCoord + float2(-1.0 * texelSize.x, 0)) * 0.2332432;
    color += tex2D(inputTexture, texCoord) * 0.290913;
    color += tex2D(inputTexture, texCoord + float2(1.0 * texelSize.x, 0)) * 0.2332432;
    color += tex2D(inputTexture, texCoord + float2(2.0 * texelSize.x, 0)) * 0.1216216;
    return color;
}

縦方向はfloat2(0, texelSize.y)のオフセットで同様に処理します。

これらの高度なレンダリング技術を組み合わせることで、Direct3D 9でリアルで美しいグラフィックス表現が可能になります。

スプライトとフォント描画

Direct3D 9では、2Dグラフィックスの描画やテキスト表示を簡単に行うための便利なインターフェースが用意されています。

ID3DXSpriteは2Dスプライトの描画を効率化し、ID3DXFontはフォントを使ったテキスト描画をサポートします。

ここではこれらの使い方と、ゲームなどでよく使われるHUD(ヘッドアップディスプレイ)の実装手順について詳しく解説します。

ID3DXSpriteで2Dオーバーレイ

ID3DXSpriteは、2Dテクスチャ(スプライト)を簡単に描画するためのインターフェースです。

複数のスプライトをバッチ処理で描画できるため、パフォーマンスも良好です。

スプライトの作成と描画の流れ

  1. スプライトインターフェースの作成
ID3DXSprite* pSprite = nullptr;
D3DXCreateSprite(pDevice, &pSprite);
  1. 描画開始
pSprite->Begin(D3DXSPRITE_ALPHABLEND);
  1. スプライトの描画
pSprite->Draw(pTexture, nullptr, nullptr, &position, D3DCOLOR_ARGB(255, 255, 255, 255));
  • pTexture:描画するテクスチャ
  • 第2引数:テクスチャの描画領域(nullptrで全体)
  • 第3引数:回転や拡大縮小の中心点(nullptrで左上)
  • 第4引数:描画位置(スクリーン座標)
  • 第5引数:色(アルファ含む)
  1. 描画終了
pSprite->End();
#include <d3dx9.h>
void DrawSprite(ID3DXSprite* pSprite, IDirect3DTexture9* pTexture, float x, float y)
{
    pSprite->Begin(D3DXSPRITE_ALPHABLEND);
    D3DXVECTOR3 position(x, y, 0.0f);
    pSprite->Draw(pTexture, nullptr, nullptr, &position, D3DCOLOR_ARGB(255, 255, 255, 255));
    pSprite->End();
}

この関数は指定位置にテクスチャを描画します。

アルファブレンディングを有効にしているため、透明部分も正しく描画されます。

ID3DXFontでテキスト表示

ID3DXFontは、Windowsのフォントを利用してDirect3D上にテキストを描画するためのインターフェースです。

フォントの種類やサイズ、スタイルを指定して簡単にテキストを表示できます。

フォントの作成

ID3DXFont* pFont = nullptr;
D3DXCreateFont(
    pDevice,
    24,             // フォントサイズ
    0,              // フォント幅(0で自動)
    FW_BOLD,        // フォントの太さ
    1,              // ミップマップレベル
    FALSE,          // イタリック
    DEFAULT_CHARSET, // 文字セット
    OUT_DEFAULT_PRECIS,
    DEFAULT_QUALITY,
    DEFAULT_PITCH | FF_DONTCARE,
    L"Arial",       // フォント名
    &pFont);

テキストの描画

RECT rect = { 10, 10, 300, 50 };
pFont->DrawText(nullptr, L"Hello, Direct3D9!", -1, &rect, DT_LEFT | DT_TOP, D3DCOLOR_ARGB(255, 255, 255, 255));
  • nullptrはスプライトインターフェースの指定(ID3DXSpriteを使う場合は指定可能)
  • -1は文字列の長さ(-1でヌル終端まで)
  • rectは描画領域
  • フラグでテキストの配置を指定
  • 色はARGB形式

フォントの解放

if (pFont) {
    pFont->Release();
    pFont = nullptr;
}

HUDの実装手順

HUD(ヘッドアップディスプレイ)は、ゲーム画面に重ねて表示する2D情報(スコア、体力バー、ミニマップなど)です。

Direct3D 9では、ID3DXSpriteID3DXFontを組み合わせて効率的に実装します。

基本的な手順

  1. スプライトとフォントの初期化

ゲーム開始時にID3DXSpriteID3DXFontを作成しておきます。

  1. レンダリングループ内での描画
  • 3Dシーンの描画が終わった後に、スプライトのBeginを呼び、HUD要素を描画
  • テクスチャを使ったアイコンやバーはID3DXSprite::Drawで描画
  • 文字情報はID3DXFont::DrawTextで描画
  • 描画終了後にスプライトのEndを呼びます
  1. 描画順序の管理

HUDは常に画面の最前面に表示したいため、3D描画後に描画し、Zバッファや深度テストは無効にします。

pDevice->SetRenderState(D3DRS_ZENABLE, FALSE);
pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
pSprite->Begin(D3DXSPRITE_ALPHABLEND);
// HUD描画(スプライト、テキスト)
pSprite->End();
pDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
  1. リソースの解放

ゲーム終了時にスプライトとフォントを解放します。

これらの手法を活用することで、Direct3D 9で効率的かつ美しい2Dオーバーレイやテキスト表示が可能となり、ゲームのユーザーインターフェースを充実させられます。

メッシュとアニメーション

3Dゲームやアプリケーションでキャラクターや複雑なオブジェクトを扱う際、メッシュデータとアニメーションは欠かせません。

Direct3D 9では、.Xファイル形式を使ってメッシュとアニメーションを管理し、スキニングやボーンアニメーションを実装できます。

ここでは、.Xファイルの読み込み方法、スキニングとボーンアニメーションの仕組み、そしてキーフレーム補間について詳しく解説します。

.Xファイル読み込み

.XファイルはMicrosoftが定めた3Dメッシュとアニメーションのファイルフォーマットで、メッシュの頂点情報、マテリアル、テクスチャ、アニメーションデータなどを含めることができます。

Direct3D 9のD3DXライブラリには、.Xファイルを簡単に読み込むための関数が用意されています。

メッシュの読み込み例

#include <d3dx9.h>
ID3DXMesh* pMesh = nullptr;
ID3DXAnimationController* pAnimController = nullptr;
HRESULT LoadMesh(LPCWSTR filename, IDirect3DDevice9* pDevice)
{
    ID3DXBuffer* pAdjacency = nullptr;
    ID3DXBuffer* pMaterials = nullptr;
    DWORD numMaterials = 0;
    HRESULT hr = D3DXLoadMeshHierarchyFromX(
        filename,
        D3DXMESH_MANAGED,
        pDevice,
        nullptr,
        nullptr,
        &pMesh,
        &pAnimController);
    if (FAILED(hr)) {
        return hr;
    }
    // マテリアルやテクスチャの処理はここで行う
    return S_OK;
}
  • D3DXLoadMeshHierarchyFromXはメッシュ階層とアニメーションコントローラを読み込みます
  • pMeshにメッシュデータ、pAnimControllerにアニメーション制御用インターフェースが格納されます

注意点

  • .Xファイルは階層構造を持つため、複数のメッシュやボーンを含むことが可能です
  • マテリアルやテクスチャの情報も同時に読み込めます

スキニングとボーンアニメーション

スキニングは、メッシュの頂点を複数のボーン(骨格)に重み付けして変形させる技術です。

ボーンアニメーションは、ボーンの変換行列を時間経過で変化させ、メッシュを滑らかに動かします。

スキニングの仕組み

  • 各頂点は複数のボーンに対して重み(ウェイト)を持ちます
  • ボーンの変換行列を頂点に適用し、重み付きで合成することで頂点の最終位置を計算
  • これにより、関節の動きに合わせてメッシュが自然に変形します

ボーンアニメーションの制御

ID3DXAnimationControllerを使って、アニメーションセットの切り替えや再生、停止、時間の進行を制御します。

アニメーションの更新例

void UpdateAnimation(ID3DXAnimationController* pAnimController, float elapsedTime)
{
    if (pAnimController) {
        pAnimController->AdvanceTime(elapsedTime, nullptr);
    }
}
  • AdvanceTimeに経過時間を渡すことで、アニメーションが進行します

キーフレーム補間

アニメーションは通常、キーフレームと呼ばれる特定の時間点でのボーンの変換情報を持ちます。

キーフレーム補間は、これらのキーフレーム間の変換を滑らかに補完し、連続的な動きを実現します。

補間の種類

  • 線形補間(Linear Interpolation)

キーフレーム間を直線的に補完。

計算が簡単で高速。

  • 球面線形補間(Slerp)

回転の補間に使われます。

クォータニオンを用いて滑らかな回転を実現。

補間の流れ

  1. 現在のアニメーション時間に最も近い前後のキーフレームを特定。
  2. キーフレームの変換行列やクォータニオンを取得。
  3. 補間係数を計算(時間の割合)。
  4. 係数に基づき変換を補完。
  5. 補完結果をボーンの最終変換行列として使用。

例:クォータニオンの球面線形補間(擬似コード)

Quaternion Slerp(Quaternion q1, Quaternion q2, float t)
{
    float dot = Dot(q1, q2);
    float theta = acos(dot);
    float sinTheta = sin(theta);
    float w1 = sin((1 - t) * theta) / sinTheta;
    float w2 = sin(t * theta) / sinTheta;
    return q1 * w1 + q2 * w2;
}

Direct3DのD3DXライブラリには、D3DXQuaternionSlerp関数が用意されており、これを使うことで簡単に補間できます。

これらの技術を組み合わせることで、Direct3D 9でリアルなキャラクターアニメーションや複雑なメッシュ変形を実現できます。

カメラ制御テクニック

3Dアプリケーションにおけるカメラ制御は、ユーザーの視点を自在に操作し、シーンの見え方を決定する重要な要素です。

Direct3D 9では、視点行列(View行列)を適切に計算・更新することで、様々なカメラ動作を実現します。

ここでは、FPSスタイルカメラ、オービットカメラ、そして視点行列の再計算方法について詳しく解説します。

FPSスタイルカメラ

FPS(First-Person Shooter)スタイルカメラは、プレイヤーの目線でシーンを見渡すカメラ制御方式です。

主に前後左右の移動と、マウス操作による視点回転を組み合わせて実装します。

基本的なパラメータ

  • 位置ベクトル(Position)

カメラの現在位置。

  • 向きベクトル(Forward)

カメラが向いている方向の単位ベクトル。

  • 上方向ベクトル(Up)

通常はワールドのY軸方向(0,1,0)。

  • 右方向ベクトル(Right)

ForwardとUpの外積で計算。

移動処理の例

void MoveCamera(D3DXVECTOR3& position, const D3DXVECTOR3& forward, const D3DXVECTOR3& right, float forwardAmount, float rightAmount)
{
    position += forward * forwardAmount; // 前後移動
    position += right * rightAmount;     // 左右移動
}

視点回転の例(マウス入力によるYawとPitch)

void RotateCamera(float& yaw, float& pitch, float deltaX, float deltaY, float sensitivity)
{
    yaw += deltaX * sensitivity;
    pitch += deltaY * sensitivity;
    // ピッチの制限(上下の回転角度制限)
    const float pitchLimit = D3DXToRadian(89.0f);
    if (pitch > pitchLimit) pitch = pitchLimit;
    if (pitch < -pitchLimit) pitch = -pitchLimit;
}

View行列の再計算

D3DXVECTOR3 forward;
forward.x = cosf(pitch) * sinf(yaw);
forward.y = sinf(pitch);
forward.z = cosf(pitch) * cosf(yaw);
D3DXVec3Normalize(&forward, &forward);
D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
D3DXVECTOR3 target = position + forward;
D3DXMATRIX view;
D3DXMatrixLookAtLH(&view, &position, &target, &up);
pDevice->SetTransform(D3DTS_VIEW, &view);

このように、Yaw(水平回転)とPitch(垂直回転)を使って視線方向を計算し、D3DXMatrixLookAtLHでView行列を作成します。

オービットカメラ

オービットカメラは、特定の注視点(ターゲット)を中心にカメラが回転し、距離を変えられるカメラ制御方式です。

3Dモデリングツールや戦略ゲームの視点操作でよく使われます。

基本的なパラメータ

  • 注視点(Target)

カメラが常に注目する位置。

  • 距離(Radius)

注視点からカメラまでの距離。

  • 角度(Yaw, Pitch)

注視点を中心に回転する角度。

カメラ位置の計算

D3DXVECTOR3 CalculateOrbitCameraPosition(const D3DXVECTOR3& target, float radius, float yaw, float pitch)
{
    D3DXVECTOR3 position;
    position.x = target.x + radius * cosf(pitch) * sinf(yaw);
    position.y = target.y + radius * sinf(pitch);
    position.z = target.z + radius * cosf(pitch) * cosf(yaw);
    return position;
}

View行列の設定

D3DXVECTOR3 position = CalculateOrbitCameraPosition(target, radius, yaw, pitch);
D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
D3DXMATRIX view;
D3DXMatrixLookAtLH(&view, &position, &target, &up);
pDevice->SetTransform(D3DTS_VIEW, &view);

操作例

  • マウスのドラッグでYawとPitchを変更し、カメラを回転
  • マウスホイールでRadiusを増減し、ズームイン・アウト

視点行列の再計算

視点行列(View行列)は、カメラの位置と向きを元に毎フレーム再計算する必要があります。

これにより、ユーザーの操作やアニメーションに応じて視点が動的に変化します。

再計算のポイント

  • カメラ位置(Eye)

現在のカメラ座標。

  • 注視点(At)

カメラが見ているポイント。

  • 上方向ベクトル(Up)

通常はワールドのY軸方向。

再計算例

void UpdateViewMatrix(IDirect3DDevice9* pDevice, const D3DXVECTOR3& eye, const D3DXVECTOR3& at, const D3DXVECTOR3& up)
{
    D3DXMATRIX view;
    D3DXMatrixLookAtLH(&view, &eye, &at, &up);
    pDevice->SetTransform(D3DTS_VIEW, &view);
}

注意点

  • Upベクトルはカメラの傾きに応じて変化させることも可能だが、通常は(0,1,0)で問題ない
  • 視点行列の更新は、カメラの移動や回転操作の後に必ず行います

これらのカメラ制御テクニックを組み合わせることで、Direct3D 9の3Dシーンにおいて多彩で直感的な視点操作を実現できます。

パーティクルシステム

パーティクルシステムは、炎、煙、爆発、魔法エフェクトなど、多数の小さな粒子を使って自然現象や特殊効果を表現する技術です。

Direct3D 9では頂点バッファを活用してパーティクルの状態を管理し、ビルボードレンダリングで常にカメラに向けて描画します。

さらに、フェードアウトやサイズ変化を加えることでリアルな表現が可能です。

ここでは、頂点バッファリングによるパーティクル更新、ビルボードレンダリング、フェードアウトとサイズ変化の実装方法を詳しく解説します。

頂点バッファリングによるパーティクル更新

パーティクルは多数の頂点データで構成されるため、効率的な管理が必要です。

Direct3D 9では、頂点バッファを使ってパーティクルの位置、色、サイズなどの情報を格納し、CPU側で更新してGPUに送ります。

カスタム頂点構造体例

struct PARTICLE_VERTEX
{
    D3DXVECTOR3 position;  // パーティクルの位置
    D3DCOLOR color;        // パーティクルの色(アルファ含む)
    float size;            // パーティクルのサイズ
};
#define D3DFVF_PARTICLE (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_PSIZE)
  • D3DFVF_PSIZEは頂点サイズを指定するためのFVFフラグです

頂点バッファの作成と更新

IDirect3DVertexBuffer9* pParticleVB = nullptr;
const int MAX_PARTICLES = 1000;
// 頂点バッファ作成
pDevice->CreateVertexBuffer(
    sizeof(PARTICLE_VERTEX) * MAX_PARTICLES,
    D3DUSAGE_DYNAMIC | D3DUSAGE_POINTS | D3DUSAGE_WRITEONLY,
    D3DFVF_PARTICLE,
    D3DPOOL_DEFAULT,
    &pParticleVB,
    nullptr);
// パーティクルデータの更新
void UpdateParticles(PARTICLE_VERTEX* particles, int count)
{
    PARTICLE_VERTEX* pVertices = nullptr;
    pParticleVB->Lock(0, 0, (void**)&pVertices, D3DLOCK_DISCARD);
    for (int i = 0; i < count; ++i) {
        pVertices[i] = particles[i];
    }
    pParticleVB->Unlock();
}
  • D3DUSAGE_DYNAMICD3DLOCK_DISCARDを使い、効率的に頂点バッファを更新
  • パーティクルの位置や色、サイズはCPU側で計算し、頂点バッファに書き込みます

ビルボードレンダリング

ビルボードレンダリングは、パーティクルを常にカメラに正面を向けて描画する技術です。

これにより、2Dのパーティクルが3D空間で自然に見えます。

実装方法

  • 頂点シェーダーや固定機能パイプラインで、頂点の回転をカメラの向きに合わせて調整
  • Direct3D 9のD3DRS_POINTSPRITEENABLEを有効にし、ポイントスプライトとして描画する方法が簡単

ポイントスプライトの有効化例

pDevice->SetRenderState(D3DRS_POINTSPRITEENABLE, TRUE);
pDevice->SetRenderState(D3DRS_POINTSCALEENABLE, TRUE);
pDevice->SetRenderState(D3DRS_POINTSIZE, FtoDW(1.0f)); // 基本サイズ
pDevice->SetRenderState(D3DRS_POINTSIZE_MIN, FtoDW(0.1f));
pDevice->SetRenderState(D3DRS_POINTSCALE_A, FtoDW(0.0f));
pDevice->SetRenderState(D3DRS_POINTSCALE_B, FtoDW(0.0f));
pDevice->SetRenderState(D3DRS_POINTSCALE_C, FtoDW(1.0f));
  • FtoDWはfloatをDWORDに変換するマクロ

描画例

pDevice->SetFVF(D3DFVF_PARTICLE);
pDevice->SetStreamSource(0, pParticleVB, 0, sizeof(PARTICLE_VERTEX));
pDevice->DrawPrimitive(D3DPT_POINTLIST, 0, particleCount);

フェードアウトとサイズ変化

パーティクルの寿命に応じて透明度を下げたり、サイズを変化させることで、自然な消滅や成長を表現できます。

フェードアウトの実装例

struct Particle
{
    D3DXVECTOR3 position;
    float age;       // 経過時間
    float lifetime;  // 寿命
    D3DCOLOR color;
    float size;
};
void UpdateParticle(Particle& p, float deltaTime)
{
    p.age += deltaTime;
    float lifeRatio = p.age / p.lifetime;
    // 透明度を徐々に下げる
    BYTE alpha = static_cast<BYTE>(255 * (1.0f - lifeRatio));
    p.color = D3DCOLOR_ARGB(alpha, 255, 255, 255);
    // サイズを徐々に小さくする
    p.size = initialSize * (1.0f - lifeRatio);
    // 位置の更新などもここで行う
}
  • lifeRatioは0から1まで変化し、1でパーティクルが消滅
  • アルファ値を減少させてフェードアウト
  • サイズも同様に減少させることで自然な消滅感を演出

頂点バッファへの反映

更新したParticleの情報をPARTICLE_VERTEXに変換し、頂点バッファに書き込みます。

これらの技術を組み合わせることで、Direct3D 9でリアルで動的なパーティクルエフェクトを効率的に実装できます。

テレインレンダリング

広大な地形をリアルに表現するテレインレンダリングは、ゲームやシミュレーションで重要な技術です。

Direct3D 9では、高さマップを用いた地形生成、描画負荷を抑えるためのLOD(Level of Detail)切り替え、そして法線マップを活用した細部表現が効果的に使われます。

ここではそれぞれの技術について詳しく解説します。

高さマップ生成

高さマップは、グレースケールの画像データを利用して地形の高さ情報を表現する方法です。

画像の各ピクセルの明るさが地形の高さに対応し、これを元に頂点のY座標を決定します。

高さマップの読み込みと頂点生成例

#include <d3dx9.h>
#include <vector>
struct Vertex {
    float x, y, z;
    float nx, ny, nz; // 法線
    float u, v;       // テクスチャ座標
};
bool LoadHeightMap(const char* filename, std::vector<Vertex>& vertices, int& width, int& height)
{
    // D3DXで画像を読み込み
    IDirect3DTexture9* pHeightMapTex = nullptr;
    HRESULT hr = D3DXCreateTextureFromFileA(pDevice, filename, &pHeightMapTex);
    if (FAILED(hr)) return false;
    D3DSURFACE_DESC desc;
    pHeightMapTex->GetLevelDesc(0, &desc);
    width = desc.Width;
    height = desc.Height;
    // ロックしてピクセルデータ取得
    D3DLOCKED_RECT lockedRect;
    pHeightMapTex->LockRect(0, &lockedRect, nullptr, D3DLOCK_READONLY);
    vertices.resize(width * height);
    for (int z = 0; z < height; ++z) {
        BYTE* row = (BYTE*)lockedRect.pBits + z * lockedRect.Pitch;
        for (int x = 0; x < width; ++x) {
            BYTE heightValue = row[x * 4]; // 例:RGBAのR成分を高さに使用
            float y = heightValue / 255.0f * maxHeight; // maxHeightは最大高さ
            Vertex& v = vertices[z * width + x];
            v.x = (float)x;
            v.y = y;
            v.z = (float)z;
            v.u = (float)x / (width - 1);
            v.v = (float)z / (height - 1);
            // 法線は後で計算
        }
    }
    pHeightMapTex->UnlockRect(0);
    pHeightMapTex->Release();
    // 法線計算は別途行う
    return true;
}
  • ピクセルの明るさを高さに変換し、頂点のY座標に設定
  • テクスチャ座標は地形全体に均等に割り当て
  • 法線は後述の方法で計算

LOD(Level of Detail)切り替え

広大な地形を高精細に描画するとパフォーマンスが低下するため、距離に応じて描画の詳細度を変えるLOD技術が使われます。

近い部分は高詳細、遠い部分は低詳細のメッシュを描画し、負荷を軽減します。

LODの基本的な考え方

  • 地形を複数のグリッド(チャンク)に分割
  • 各チャンクに対して複数の解像度のメッシュを用意
  • カメラからの距離に応じて適切な解像度のメッシュを選択して描画

LOD切り替えの例

int SelectLOD(float distance)
{
    if (distance < 50.0f) return 0;  // 高詳細
    else if (distance < 150.0f) return 1; // 中詳細
    else return 2;                   // 低詳細
}
void RenderChunk(const Chunk& chunk, const D3DXVECTOR3& cameraPos)
{
    float dist = D3DXVec3Length(&(chunk.center - cameraPos));
    int lod = SelectLOD(dist);
    chunk.meshes[lod]->DrawSubset(0);
}
  • 距離に応じてLODレベルを決定
  • 選択したLODのメッシュを描画

注意点

  • LOD間の切り替えでポップアップ(急な変化)が起きないようにフェードやトランジションを入れることもあります
  • チャンクの境界でジオメトリの不整合が起きないように工夫が必要でしょう

法線マップでの細部表現

法線マップは、テクスチャに法線情報を格納し、光の当たり方を細かく変化させる技術です。

これにより、ポリゴン数を増やさずに凹凸感や細かなディテールを表現できます。

法線マップの利用方法

  • 法線マップテクスチャを用意し、ピクセルシェーダーでサンプリング
  • 法線マップの値を元にライティング計算を行い、表面の凹凸を表現

ピクセルシェーダーでの法線マップ利用例(HLSL)

sampler2D NormalMapSampler;
struct PS_INPUT {
    float2 TexCoord : TEXCOORD0;
    float3 LightDir : TEXCOORD1;
    float3 ViewDir : TEXCOORD2;
};
float4 PSMain(PS_INPUT input) : COLOR
{
    // 法線マップから法線を取得(0~1の範囲を-1~1に変換)
    float3 normal = tex2D(NormalMapSampler, input.TexCoord).xyz * 2.0f - 1.0f;
    normal = normalize(normal);
    // ライティング計算(例:ディフューズ)
    float NdotL = saturate(dot(normal, normalize(input.LightDir)));
    float4 diffuse = float4(NdotL, NdotL, NdotL, 1.0f);
    return diffuse;
}
  • 法線マップのRGB値を法線ベクトルに変換
  • ライティング計算に利用し、凹凸感を表現

法線マップの作成

  • 専用のツールや3Dモデリングソフトで生成
  • 高解像度モデルからベイクする方法が一般的

これらのテレインレンダリング技術を組み合わせることで、Direct3D 9で広大かつリアルな地形表現が可能になります。

パフォーマンス最適化

Direct3D 9で高品質な3Dグラフィックスを実現するには、描画パフォーマンスの最適化が不可欠です。

ここでは、ドローコール削減とバッチング、頂点キャッシュヒット率の向上、フラスタムカリングとオクルージョンクエリ、そしてマルチスレッドレンダリングの注意点について詳しく解説します。

ドローコール削減とバッチング

ドローコールとは、GPUに対して描画命令を送るAPI呼び出しのことです。

ドローコールが多いとCPU負荷が増大し、パフォーマンスが低下します。

したがって、ドローコールの削減は重要な最適化手法です。

バッチングの基本

バッチングとは、複数の描画対象をまとめて一度のドローコールで描画する技術です。

これにより、API呼び出し回数を減らし、CPUとGPU間の通信コストを削減します。

バッチングの種類

  • ジオメトリバッチング

複数のメッシュの頂点・インデックスデータを一つのバッファにまとめて描画。

  • テクスチャバッチング

同じテクスチャを使うオブジェクトをまとめて描画。

  • 状態バッチング

同じレンダーステートやシェーダー設定のオブジェクトをまとめる。

実装例

// 複数オブジェクトの頂点を一つの頂点バッファにまとめる
pDevice->SetStreamSource(0, pCombinedVertexBuffer, 0, sizeof(Vertex));
pDevice->SetIndices(pCombinedIndexBuffer);
pDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, totalVertices, 0, totalTriangles);
  • 頂点バッファとインデックスバッファを統合し、一括描画
  • 状態変更(テクスチャやシェーダー切り替え)を減らす

頂点キャッシュヒット率向上

GPUは頂点キャッシュを持ち、同じ頂点を再利用する際に高速化します。

頂点キャッシュヒット率を上げることで、頂点処理の負荷を軽減できます。

頂点キャッシュの特徴

  • 頂点バッファ内の頂点が連続して描画されるとキャッシュヒット率が高まります
  • インデックスバッファの順序が重要でしょう

最適化手法

  • インデックスバッファの再配置

頂点の使用順序を工夫し、同じ頂点が近い位置で再利用されるようにします。

  • アルゴリズムの活用

Forsythアルゴリズムなどの頂点キャッシュ最適化アルゴリズムを使います。

効果

  • 頂点シェーダーの実行回数削減
  • GPUのパイプライン効率向上

フラスタムカリングとオクルージョンクエリ

フラスタムカリング

フラスタムカリングは、カメラの視錐台(フラスタム)外にあるオブジェクトを描画しない技術です。

これにより、描画負荷を大幅に削減できます。

実装例
  • オブジェクトのバウンディングボックスや球の座標を取得
  • 視錐台との交差判定を行い、外れていれば描画をスキップ

Direct3D 9には視錐台判定用の関数はないため、自前で計算するか、D3DXの補助関数を利用します。

オクルージョンクエリ

オクルージョンクエリは、あるオブジェクトが他のオブジェクトに隠れて画面に描画されないかをGPUに問い合わせる機能です。

描画不要なオブジェクトを判定し、無駄な描画を防ぎます。

使用手順
  1. オクルージョンクエリオブジェクトを作成。
  2. 隠れているか判定したいオブジェクトの描画をクエリで囲みます。
  3. クエリ結果を取得し、描画の有無を決定。
注意点
  • クエリ結果の取得は非同期で行うため、1フレーム遅れの判定になります
  • 過度なクエリは逆にパフォーマンス低下を招くため、適切な使用が必要でしょう

マルチスレッドレンダリングの注意点

Direct3D 9はマルチスレッドレンダリングに対応していますが、注意が必要です。

注意点

  • デバイスの作成時にD3DCREATE_MULTITHREADEDフラグを指定する必要があります。
  • マルチスレッド対応はCPU負荷分散に有効だが、オーバーヘッドも増加します
  • COMオブジェクトのスレッドセーフ性に注意

共有リソースのアクセスは排他制御が必要でしょう。

  • レンダリングコマンドの順序管理が複雑になります。

実装例

pD3D->CreateDevice(
    D3DADAPTER_DEFAULT,
    D3DDEVTYPE_HAL,
    hWnd,
    D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED,
    &d3dpp,
    &pDevice);

推奨事項

  • マルチスレッドレンダリングは必要な場合に限定して使用
  • スレッド間の同期やリソース管理を慎重に設計

これらの最適化技術を適切に組み合わせることで、Direct3D 9アプリケーションの描画パフォーマンスを大幅に向上させることが可能です。

DirectX9からのアップグレードパス

DirectX9は長年にわたり多くのゲームやアプリケーションで利用されてきましたが、より高度なグラフィックス機能やパフォーマンス向上を求める場合、DirectX11への移行が検討されます。

ここでは、DirectX11への移行における主要なポイントと、既存のDirectX9ベースのエンジンアーキテクチャを再利用する方法について解説します。

DirectX11への移行ポイント

DirectX11はDirectX9に比べて多くの新機能と改善点を備えていますが、APIの設計が大きく異なるため、単純な置き換えではなく設計の見直しが必要です。

デバイスとコンテキストの分離

  • DirectX9ではIDirect3DDevice9が描画やリソース管理を一手に担っていましたが、DirectX11ではID3D11Device(リソース作成)とID3D11DeviceContext(描画コマンド発行)が分離されています
  • これにより、マルチスレッドレンダリングの効率化が図られています

シェーダーモデルの進化

  • DirectX11はShader Model 5.0をサポートし、より高度なシェーダープログラミングが可能です
  • コンピュートシェーダーの導入により、GPUでの汎用計算が可能になりました

リソース管理の変更

  • リソースの作成やバインディング方法がDirectX9と異なり、リソースビュー(Shader Resource View、Render Target Viewなど)を使ってアクセスします
  • マルチサンプラーやマルチスレッド対応が強化されています

レンダリングパイプラインの柔軟性

  • 入力アセンブラ、ラスタライザ、出力マージャーなどのステージが明確に分かれ、パイプラインの制御が細かく可能です
  • マルチレンダーターゲット(MRT)やテッセレーションシェーダーなど新機能も追加

APIの非互換性

  • DirectX9のAPIはDirectX11で直接使えないため、ラッパーや移行レイヤーを使うか、コードの大幅な書き換えが必要でしょう
  • 特に固定機能パイプラインは廃止され、すべてシェーダーで実装する必要があります

移行のステップ例

  • 既存のDirectX9コードをモジュール化し、描画部分を抽象化
  • DirectX11の初期化コードを追加し、段階的に描画処理を置き換え
  • シェーダーコードをHLSLの新仕様に対応させます
  • リソース管理や状態管理をDirectX11スタイルに移行

エンジンアーキテクチャの再利用

DirectX9からDirectX11への移行時に、既存のエンジンアーキテクチャを活かすことは開発効率を高める上で重要です。

抽象化レイヤーの設計

  • グラフィックスAPIに依存しない抽象化レイヤー(Renderer Interface)を設計し、DirectX9とDirectX11の実装を切り替え可能にします
  • これにより、将来的なAPI変更にも柔軟に対応可能です

リソース管理の共通化

  • テクスチャ、メッシュ、シェーダーなどのリソース管理は共通化し、API固有の処理はラッパークラスで対応
  • 例えば、テクスチャクラスはDirectX9版とDirectX11版で内部実装を切り替えます

シェーダー管理の移行

  • HLSLコードは可能な限り共通化し、DirectX9用とDirectX11用でコンパイルターゲットを分けます
  • 固定機能パイプラインの機能はすべてシェーダーで再実装する必要があるため、シェーダー管理を強化

レンダリングパイプラインの再構築

  • DirectX11のパイプラインはステージごとに明確に分かれているため、パイプライン構成を柔軟に管理できる設計に変更
  • 既存の描画ロジックをモジュール化し、パイプラインステージに対応した処理を割り当てる

マルチスレッド対応の強化

  • DirectX11はマルチスレッドレンダリングを強力にサポートしているため、スレッドセーフな設計に見直します
  • コマンドリストやコンテキストの分割を活用し、CPU負荷分散を図ります

段階的移行の推奨

  • 既存のDirectX9コードを一気に書き換えるのではなく、機能単位やモジュール単位で段階的にDirectX11対応を進める
  • これにより、動作確認やデバッグが容易になり、リスクを低減できます

DirectX9からDirectX11への移行は大規模な作業ですが、上記のポイントを押さえ、既存のエンジンアーキテクチャを活かしつつ段階的に進めることで、最新のグラフィックス技術を活用した高品質なアプリケーション開発が可能になります。

まとめ

本記事では、DirectX9を用いた3Dグラフィックスプログラミングの基礎から高度な技術まで幅広く解説しました。

ウィンドウ作成やデバイス初期化、頂点・テクスチャ管理、シェーダーの基礎、レンダリングループの流れ、ライティングやカリング、アルファブレンディング、パーティクルやテレインレンダリング、パフォーマンス最適化、さらにはDirectX11への移行ポイントまで網羅しています。

これにより、DirectX9の実践的な開発スキルを体系的に習得でき、安定した3Dアプリケーションの構築や最新技術へのスムーズな移行が可能となります。

関連記事

Back to top button
目次へ