サイトアイコン GAMEWORKS LAB

初めてのDirectX9

DirectXとは?

DirectXとは昨今では主にゲームの開発で使用されるMicrosoftの開発したAPIです。
最近は主にグラフィック(2Dや3Dの描画)関連に使用されています。
グラフィック以外でも算術処理やゲームパッドの入力制御、サウンド制御、ネットワーク通信なども備わっています。

既にDirectX12も出ているので知っている人には今更という感じもあるかと思いますが、
学校の講師でDirectX9を教えることになったので基礎から順番に記事を書いていこうかと思います。

ちなみに2018年の9月現在。
ゲーム業界のPC環境下ではDirectX9は現役です。

コンシューマーや最新のPCゲームの開発現場は殆どDirectX11以上の技術が主流ですが、
移植の案件やアーケード等ではまだまだ活躍しています。

又、DirectX9は情報量が多いので、描画の基礎知識を学ぶ上では有効だと考えています。

それでは今回はDirectXのインストール方法~初期化までを書いていこうかと思います。

 

DirectXのインストール手順

今回使うDirectXのバージョンはDXSDK June2010です。
最近のWindowsではWindows SDKの中にDirectXが入っているので何もしなくても使えるのですが、
いくつか使えない機能があるのでDirectX9を使用する場合は別途インストールした方がいいです。

※既にインストール済みの場合は次のページから進めて問題ありません。

先ず下記のリンクからDXSDK June2010をダウンロードします。
https://www.microsoft.com/en-us/download/confirmation.aspx?id=6812

[Download]をクリックするとDXSDK_Jun10.exeがダウンロードできます。

ダウンロードが完了したら次はインストールです。

DXSDK_Jun10.exeを実行して下記の手順でインストールしてください。

1.[次へ]を選択します。

2.[同意します]にチェックを入れて[次へ]を選択します。

3.[No, I would…]にチェックを入れて次へを選択します。

4.[次へ]を選択します。

5.インストールが開始されるので終了するまで数分程度掛かります。

6.インストールが終了すると完了のボタンが表示されますので[完了]を選択して終了します。

インストールはこれで完了です!

Visual Studioの設定手順

DirectXを使用するためにはプロジェクトにDirectXを使用する設定をする必要があります。
このページでは設定方法の手順を記載しています。

この記事は『初めてのVisual Studio ~ウィンドウの作成~』で作ったプロジェクトを元に解説しています。

1.『DirectX9』と書かれたプロジェクト上で右クリックをして表示されるメニューから[Properties]を選択します。

2.[VC++ Directories]の項目を選択して、[Include Directories]の右側のボタンから編集画面を開きます。

3.[Include Directories]のダイアログ上にある項目に[$(DXSDK_DIR)include]を追加して[OK]を選択する

4.2と同じ画面で[Library Directories]の右側のボタンから編集画面を開きます。

5.[Library Directories]のダイアログ上にある項目に[$(DXSDK_DIR)lib\x86]を追加して[OK]を選択する

6.現状で下記のようになっていれば問題ありません

これでDirectX9を使用する準備が整いました。

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の場合はプログラムが終了するとリークしていても解放してくれるようになっていますが、基本的に開放漏れの無いようにしましょう。

折角作ったDirectXの初期化と解放関数なのでmain.cppで実際に呼び出してみましょう。

#include "Window.h"
#include "DirectX.h"

// エントリーポイント
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	int width = 640;
	int height = 480;
	if( !CreateSimpleWindow(TEXT("DirectX9"), 0, 0, width, height) )
	{
		return false;
	}

	InitializeDirectX(GetWindowHandle(), width, height, false);
	// メインループ
	while( !IsQuitMessage() )
	{
		UpdateWindowMessage();
	}

	FinalizeDirectX();

	return 0;
}


関数化しているので追加した箇所は少しだけです。

ウィンドウを生成した後にウィンドウの設定をDirectXに渡して初期化をする。
プログラムが終了する際にDirectXの解放処理を呼び出す。

これでDirectXの初期化は完了です!
ただ今回はDirectXの初期化をしただけなので見た目は何も変わりません。

次回は実際にDirectXを使用して画面に絵を出してみましょう!

モバイルバージョンを終了