DirectX9の初期化
ではここからDirectXのプログラムを書いていきます。
このページのタイトルにもある通りDirectXを使用するには初期化をする必要があります。
又、基本的に初期化したものは終了するときに破棄する必要があります。
では実際に処理を書いてみましょう。
Windowを関数化した時と同じように先ずはDirectX.cppとDirectX.hを作成しましょう。
今回もヘッダーの定義から作成していきます。
DirectX.h
#pragma once
// DirectX9のヘッダーを含める
#include <d3d9.h>
// 解放処理用のマクロを定義
#define SAFE_RELEASE(x) { if(x) { (x)->Release(); (x) = NULL; } }
// DirectXの初期化
bool InitializeDirectX(HWND hWnd, int width, int height, bool isFullscreen);
// DirectXの終了処理
void FinalizeDirectX(void);
// Direct3Dオブジェクトの取得
IDirect3D9* GetDirect3D(void);
// Direct3DDeviceオブジェクトの取得
IDirect3DDevice9* GetDirect3DDevice(void);
SAFE_RELEASEのように解放処理はマクロ等を記述しておくことが一般的です。
DirectXのクラスは基本的にIUnknownインターフェイスが継承されています。
IUnknownインターフェイスは参照カウンタで生存判定を行っています。
参照カウンタは生成された時点では1になっており、AddRefでインクリメント、Releaseでデクリメントされます。
そしてReleaseのタイミングで参照カウンタが0になった場合にのみ実体を破棄する仕組みになっています。
その為、DirectXで取得したクラスは基本的に上記で記述した解放処理を使用することになります。
間違ってもdelete等で直接解放してはいけません。
DirectX.cpp
#include "DirectX.h"
#include "Window.h"
// DirectX9の静的ライブラリをリンク
#pragma comment(lib, "d3d9.lib")
IDirect3D9* g_pD3D = NULL;
IDirect3DDevice9* g_pDevice = NULL;
// DirectXの初期化
bool InitializeDirectX(HWND hWnd, int width, int height, bool isFullscreen)
{
// IDirect3D9オブジェクトの作成
if( (g_pD3D = Direct3DCreate9(D3D_SDK_VERSION)) == NULL )
{
return false;
}
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS));
// Deviceの設定
d3dpp.Windowed = !isFullscreen;
d3dpp.hDeviceWindow = hWnd;
d3dpp.BackBufferWidth = width;
d3dpp.BackBufferHeight = height;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
d3dpp.MultiSampleQuality = 0;
d3dpp.Flags = 0;
d3dpp.BackBufferCount = 1;
d3dpp.EnableAutoDepthStencil = true;
d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
// VSYNC待ち有り)
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
// Direct3Dデバイスの生成
if( FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
d3dpp.hDeviceWindow,
D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED,
&d3dpp,
&g_pDevice)) )
{
SAFE_RELEASE(g_pD3D);
return false;
}
return true;
}
// DirectXの終了処理
void FinalizeDirectX(void)
{
SAFE_RELEASE(g_pDevice);
SAFE_RELEASE(g_pD3D);
}
// Direct3Dオブジェクトの取得
IDirect3D9* GetDirect3D(void) { return g_pD3D; }
// Direct3DDeviceオブジェクトの取得
IDirect3DDevice9* GetDirect3DDevice(void) { return g_pDevice; }
DirectX9の初期化には2つの手順が必要です。
まず最初に必要なのがIDirect3D9オブジェクトの作成です。
IDirect3D9オブジェクトではPCのモニタやグラフィック性能を取得することができます。
次にIDirect3DDevice9オブジェクトの作成を行います。
IDirect3DDevice9オブジェクトは描画制御やリソース作成を行う為に必要になります。
DirectXでの基本操作は大体がここから始まることになります。
初期化時の設定について
ここではDirect3Dデバイスの生成時の設定について説明していきます。
普段はtrueで問題ないがフルスクリーンモードにする場合はfalseに設定する必要がある。
ゲーム等ではコンフィグ画面で動的に切り替えられるようにしている場合が多いです。
d3dpp.Windowed = true;
バックバッファのサイズ(解像度)の設定
d3dpp.BackBufferWidth = iWidth; d3dpp.BackBufferHeight = iHeight;
DepthStencilバッファを使用する際に必要な設定
使用しない場合はEnableAutoDepthStencilをfalseにしておく必要があります。
またDepthバッファのみ使用する場合はD3DFMT_D16等で作成することが出来ます。
2Dゲームでは必要ない場合もありますが、一般的にはD3DFMT_D24S8を設定します。
d3dpp.EnableAutoDepthStencil = true; d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
PresentationIntervalではVSyncの有無を設定することが出来ます。
VSyncはD3DPRESENT_INTERVAL_ONEでオン、D3DPRESENT_INTERVAL_IMMEDIATEでオフにできます。
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
バックバッファとは?
バックバッファとは描画した内容を一時的に保持して置く場所です。
描画した内容はすぐにモニターに出るようなイメージもあるかもしれませんが、それでは描画途中の絵が見えてします可能性があります。
書きかけの絵がユーザーに見えてしまっては不格好ですよね。
なので画面を完成させるまでの間はバックバッファという場所に絵を描いていきます。
そして全ての絵が完成したタイミングで初めてモニターに表示させるようになっています。
VSyncとは?
モニタは映像を表示する際に実際に動いているものを表示することはできません。
その為、連続した静止画を短い時間で更新していくことで動いていると目に錯覚させています。
この更新を行う間隔を表したものリフレッシュレートといいます。
VSyncとはこの間隔と描画の処理を同期させる仕組みの事です。
リフレッシュレートが60Hzの時にVSyncを有効にすると60FPSが最大フレームレートとなります。
もしVSyncを無効にした状態で実行すると60FPS以上のフレームレートを出すことが可能になります。
しかしモニタはリフレッシュレートを超える速度で画面の更新ができません。
その為、更新中の中途半端な画面が表示されてしまい、ティアリングが発生することになります。
他にもいくつか設定はありますが、必要になった時に調べた方が頭に残ると思うので割愛します。
気になった方はMicroSoftのドキュメント辺りを調べてみるのがいいと思います。
最後に解放用の関数を用意しています。
FinalizeDirectXでは作成したオブジェクトを破棄しています。
これをしていないとメモリリークが発生してしまします。
メモリリークとはOSから借りたメモリを返さずに持ち続けてしまうことです。
Windowsの場合はプログラムが終了するとリークしていても解放してくれるようになっていますが、基本的に開放漏れの無いようにしましょう。