ホーム < ゲームつくろー! < DirectX技術編 < 初期化なんて怖くないぜ


その1 初期化なんて怖くないぜ!


 Direct3Dの初期化については多くのサイトでも紹介されていますが、一応基本なのでここでも掲載する事にします。対象はDirectX9です。 Direct3Dを使うには、専用のコンポーネントを取得する必要があります。この取得作業がそのまま初期化につながります。「初期化は難しい」などと言われたのは過去の話。今や初期化は非常に簡単にできるようになりました。

 Direct3Dの初期化の最初は必ずIDIRECT3D9コンポーネントの取得から始まります。

// IDirect3D9コンポーネントの取得
if( NULL == ( m_lpDirect3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
   return FALSE;


IDirect3D9コンポーネントはDirect3Dを扱うためのいくつかのコンポーネントを生成する大元のコンポーネントに当たります。またマシンの描画能力を調べることができます。これがないとDirect3Dは何もできないので、必ず必要になりますが、一度取得してしまうと殆ど使わない物にもなってしまいます。
 
 このコンポーネントから、次に実際の描画を担当するIDirect3DDevice9コンポーネントを生成します。IDirect3DDevice9コンポーネントはビデオカードとのやり取りをしてくれるインターフェイスで、描画のすべてはこのコンポーネントのインターフェイスを駆使して行われます。初期化で一番面倒なのはここですから、詳しく見ていきます。

 一昔前のDirect3D7の時代、このインターフェイスの取得は泣きそうなほど大変でした。しかし、今はD3DPRESENT_PARAMETERS構造体に必要な情報を格納するだけでOKとなりました。早速この構造体のメンバを見てみましょう。

D3DPRESENT_PARAMETERS
UINT BackBufferWidth
UINT BackBufferHeight
D3DFORMAT BackBufferFormat
UINT BackBufferCount
D3DMULTISAMPLE_TYPE MultiSampleType
DWORD MultiSampleQuality
D3DSWAPEFFECT SwapEffect
HWND hDeviceWindow
BOOL Windowed
BOOL EnableAutoDepthStencil
D3DFORMAT AutDepthStencilFormat
DWORD Flags
UINT FullScreen_RefreshRateInHz
UINT PresentationInterval


BackBuffer〜というのはバックバッファ、すなわち裏面の設定値を定義します。
BackBufferWidthBackBufferHeightは画面の大きさです。良く使われるのが640×480です。ビデオカードがサポートしていない画面の大きさは設定できません。
BackBufferFormatは画面のフォーマット情報を格納します。フォーマット情報とは1ピクセルの色の定義情報の事です。これはIDirect3D9::GetDispkayMode関数で現在のディスプレイモードを取得しておけばOK。また、D3DFMT_UNKNOWNと楽する事も出来ます。ディスプレイモードは沢山ありますので、DirectXのマニュアルでD3DFORMATで確認してみてください。
BackBufferCountはバックバッファの数を指定します。特別なことをしない限りは1枚あれば十分です。
MultiSampleTypeはマルチサンプルの数をD3DMULTISAMPLE_TYPE列挙型のメンバで指定します。マルチサンプルを有効にすると「ジェギ消し効果」「アンチエイリアス」が掛かり、この質を上げるほど綺麗な画像になります。しかし、アンチエイリアス描画サポートをしていないビデオカードもありますので、D3DMULTISAMPLE_NONEをデフォルトで設定しておいた方が良いでしょう。アンチエイリアスのサポートを調べるにはIDirect3D9::CheckDeviceMultiSampleType関数を使います。
MultiSampleQualityはマルチサンプルの品質レベルという物で、具体的にはIDirect3D9::CheckDeviceMultiSampleType関数の引数に返される値を入れるとなっています。MultiSampleTypeにD3DMULTISAMPLE_NONEを指定した場合にはここは0でOKです。
SwapEffectとはフロントバッファとバックバッファの切り替え方法を定義する部分です。D3DSWAPEFFECT_DISCARDを指定しておくと、ディスプレイドライバが自動判断してくれます。「それ以外を指定する必要なんてないじゃん」と思ってしまいますが、この設定だとαブレンディングの効果が保障されません。これを回避するには、D3DCAPS9構造体(D3Dの能力を格納する構造体)のD3DCAPS3_ALPHA_FULLSCREEN_FLIP_OR_DISCARDフラグをチェックします。これがTRUEならば、ブレンド結果が保証されます。FALSEの場合はこのメンバ変数にD3DSWAPEFFECT_COPYを設定しなければなりません。しかし、この設定はバックバッファをフロントバッファにコピーするため処理が重たくなります。とは言うものの、昨今のビデオカードにおいてαブレンドをサポートしていないものは多分無いでしょうから、D3DSWAPEFFECT_DISCARDで問題ないでしょう。
hDeviceWindowは画面を描画するウィンドウハンドルを指定します。NULLを指定した場合は現在フォーカスのあるウィンドウが使用されます。実はここをNULLにして後でデバイスのオブジェクトを取得する時に指定できます。
Windowedはスクリーンモードを定義します。TRUEの場合はウィンドウモード、FALSEはフルスクリーンモードです。一昔前どえらく大変だったのは、この切り替えでした。
EnableAutoDepthStencilは「深度ステンシルバッファ」というバッファの有無を設定します。TRUEだとステンシルバッファを作成します。深度ステンシルバッファというのは2つの機能を備えた特別なバッファです。1つは「Zバッファ」などの3D空間におけるオブジェクトの奥行きを保持するバッファで、これが無いと3D描画が殆どめちゃくちゃになります。もう1つは「ステンシルバッファ」という画面にマスクをかけて描画領域を制限するバッファです。フロントバッファに描かれない部分を作りたい時に威力を発します。詳しい事は別に設けます。そういう事をしなくてもいい場合はFALSEにしても良いでしょうが、3Dを扱うならTRUEにするのが普通です。
AutDepthStencilFormatはステンシルバッファのフォーマットを指定します。EnableAutoDepthStencilがFALSEの場合はこの値は無視されます。TRUEの場合はそのピクセルフォーマットをD3DFORMAT列挙型で指定する必要があります。指定の仕方は沢山ありますので、D3DFORMAT列挙型のマニュアルを参照して下さい。
Flagsはバックバッファからフロントバッファへ転送する時の機能のオプションを指定する部分ですが、今は(説明が難しいので)0を設定しておきます。
FullScreen_RefreshRateInHzは名前の通り、フルスクリーンでのリフレッシュレートを指定します。ただし、適当な値は設定できません。IDirect3D9::EnumAdapterModes関数で取得できるリフレッシュレートの値だけが指定できます。また、ウィンドウモードの場合は0にする必要があります。 D3DPRESENT_RATE_DEFAULTを指定すると現状の速度になります。
PresentationIntervalはスワップの書き換えタイミングを設定します。タイミングには大きく2つ、垂直帰線間隔を待つのと待たないのがあります。垂直帰線間隔を待つ場合、描画時に「テアリング」が出るのを防ぐ事が出来ます。テアリングとはフォアバッファに画面を描画している最中に書き換えが行われてしまった場合に、前回の画面とのギャップが見えてしまう現象です。D3DPRESENT_INTERVAL_DEFAULTを指定すると、垂直帰線間隔を待ち、最大1回の描画をしてくれます。D3DPRESENT_INTERVAL_IMMEDIATEを設定すると垂直帰線間隔を待ちません。しかし、描画は最速になります。画面のちらつきを抑えるためにも、ここをきちんと設定する必要があります。


・・・という感じで、まぁ随分色々ありますが、基本的な設定は次の通りです。

D3DPRESENT_PARAMETERS d3dpp;
d3dpp.BackBufferWidth = 640;
d3dpp.BackBufferHeight = 480;
d3dpp.BackBufferCount = 1;
d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
d3dpp.MultiSampleQuality = 0;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = NULL;
d3dpp.Windowed = TRUE;
d3dpp.EnableAutoDepthStencil = FALSE;
d3dpp.AutoDepthStencilFormat = 0;
d3dpp.Flags = 0;
d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;

よく見ると殆どがデフォルト設定ですね。

次にこの構造体をIDirect3D9::CreateDevice関数に渡す事でIDirect3DDevice9コンポーネントを取得する事が出来ます。

HRESULT CreateDevice(
UINT Adapter;
D3DDEVTYPE DeviceType;
HWND hFocusWindow;
DWORD BehaviorFlags;
D3DPRESENT_PARAMETERS* pPresentationParameters;
IDirect3DDevice9** ppReturnedDeviceInterface;
);

Adapterはディスプレイアダプタの番号を指定します。ディスプレイアダプタは表示できるディスプレイごとに付けられていまして、マルチモニター対応のビデオカードだったり2枚以上挿している場合は1,2,3...と序列が付けられます。万人へのゲームを作る時に、ここにD3DADAPTER_DEFAULT以外を指定する事はまず無いでしょうが、マルチスクリーンでデバッグ作業をする時には、変更する必要が出てくるかもしれません。
DeviceTypeにはD3DDEVTYPE列挙型のメンバを指定します。D3DDEVTYPE_HALを指定すると、ハードウェアを最大限に使用したパフォーマンスを得る事が出来ます。D3DDEVTYPE_REFを指定すると、描画等の処理をソフトウェアで行います。処理は当然重たくなってしまいます。D3DDEVTYPE_SWというフラグもありますが、ソフトウェアデバイスを開発しなければならないので、ここでは指定しません。D3DDEVTYPE_HALでいいじゃないかと思うのですが、ハードウェアがサポートしていない機能を使おうとするとエラーになるときがあるので、過信してはいけない部分です。
hFocusWindowは描画を行うウィンドウハンドルを指定します。
BehaviorFlagsはデバイスの作成を制御するオプションフラグです。色々と設定できるのですが、少なくとも
・D3DCREATE_HARDWARE_VERTEXPROCESSING
・D3DCREATE_MIXED_VERTEXPROCESSING
・D3DCREATE_SOFTWARE_VERTEXPROCESSING
のいずれかは指定する必要があります。これらは頂点処理をハードウェアでするかソフトウェアで行うかを指定するフラグです。頂点処理とはポリゴンの頂点を移動させたり行列演算する処理の事で、当然ハードウェアの方が格段に早いのですが、サポートしていない場合はソフトウェア頂点処理に設定しなければなりません。
pPresentationParametersは先ほど設定したD3DPRESENT_PARAMETERS構造体へのポインタを指定します。
ppReturnedDeviceInterfaceへはIDirect3DDevice9コンポーネントへのポインタを指定します。関数が成功するとここにコンポーネントへのポインタが格納されます。

CreateDevice関数はDeviceType及びBehaviorFlagsでハードウェアかソフトウェア処理のどちらかになるように設定しなければなりません。ハードウェアパフォーマンス×ハードウェア頂点処理がもっとも良い組合せですが、サポートしていない事もあります。ですから、速度の速い順番にテストする必要があります。

HRESULT hr;
hr = lpd3d->CreateDevice(
   D3DADAPTER_DEFAULT,
   D3DDEVTYPE_HAL,     // ハードウェア処理
   hWnd,
   D3DCREATE_HARDWARE_VERTEXPROCESSING  // HW頂点処理
   &d3dpp,
   &lpD3ddev
);


上の設定で戻り値のhrがD3D_OKでは無い場合、設定がサポートされていません。その場合、次の順で繰り返し試します。

・ハードウェア処理×SW頂点処理
・ソフトウェア処理×HW頂点処理
・ソフトウェア処理×SW頂点処理

ここまでやってもダメな場合は、Direct3Dそれ自体が使えません。大抵はハードウェア処理×SW頂点処理くらいで引っかかります。

これで最低限の初期化が終了です。取得したIDirect3DDevice9オブジェクトを通して画面に描画する事が可能になります。ざっと通すと行った事はたった3つしかありません。

 @ IDirect3D9オブジェクトの生成
 A D3DPRESENT_PARAMETERSの設定
 B IDirect3DDevice9オブジェクトの生成

実際はサポートされている機能を適切に選択する初期化をするべきです。あと、ウィンドウモードとフルスクリーンモードで初期化が微妙に違います。しかし初期化の大筋は同じですから、アプリケーション全体を管理するクラスにまとめてしまえば楽です。