バッファを表示


画面のクリアと描画

さて、この章では作成したデバイスに描画を行いたいと思います
ただし、図形などは GDI に比べて非常に複雑で難しいことを覚悟してください
なぜならば、従来の2次元ではなく、Z 座標が加わった3次元で処理されるからです

そこで、今回は極めて基本的な画面のクリアと表示に絞りたいと思います
画面のクリアは、バッファの描画領域全体が指定色でクリアされます
ここで重要なのは、描画の対象は常にバッファであり、スクリーンではないということです

通常、DirectX Graphics の描画処理は、バッファに対し描画処理を行い
描画処理が終了した時点で、バッファを画面に描画するという過程を辿ります

バッファをクリアするには IDirect3DDevice8::Clear() を使います
このメソッドは、矩形を指定した色でクリアし、深度バッファをクリア
さらに、ステンシルバッファを削除します
HRESULT Clear(
	DWORD Count , CONST D3DRECT* pRects ,
	DWORD Flags , D3DCOLOR Color ,
	float Z , DWORD Stencil
);
Count には、pRects で指定する矩形の数を指定します
pRects は、クリアする領域を示した矩形配列へのポインタを指定します
この引数に NULL を指定すれば、全体をクリア対象とします

Flags には、クリアする対象を指定します
この引数には、以下の定数を組み合わせて指定することができます

定数解説
D3DCLEAR_STENCIL ステンシル バッファをクリアして、Stencil パラメータの値にする
D3DCLEAR_TARGET レンダリング ターゲットをクリアして、Color パラメータの色にする
D3DCLEAR_ZBUFFER 深度バッファをクリアして、Z パラメータの値にする

Color には、クリア時に画面を塗りつぶす色を指定します
DirectX Graphics において、色は D3DCOLOR 型で表されます
これは、実質上は DWORD 型です(色の表現方法については下記参照)

Z は、0.0 〜 1.0 までの深度バッファに保存する新しい z 値を指定します
0.0 が最もビューワに近く、1.0 が最も遠い値となります

Stencil は、各ステンシルバッファのエントリに保存する整数値を指定します
この値は、常に 0 〜 2n - 1 までの値になります (n = ステンシルバッファビット深度)
メソッドが成功すれば D3D_OK が、失敗すれば D3DERR_INVALIDCALL. が返ります

D3DRECT 構造体は次のように定義されています
この構造体は Win32 API における RECT 構造体と意味は同じです
typedef struct _D3DRECT {
    LONG x1;
    LONG y1;
    LONG x2;
    LONG y2;
} D3DRECT;
x1メンバには、矩形の左上を表す x 座標、y1 には y 座標を指定します
x2メンバには、矩形の右下を表す x 座標、y2 には y 座標を指定します

DirectX Graphics における色は、Windows GDI に比べ「透明度」をサポートしています
透明度、すなわちアルファ値が存在することで、ARGB 形式のフォーマットとなります
アルファ値は、0 に近いほど透明に、255 に近いほど不透明になります

カラー値の実体は DWORD 型の 32 ビット整数値です
下位24ビットは、RGB の順番に各色素の強さが 8 ビットずつで表現され
これに加え、最上位8ビットが 0 〜 255 までのアルファ値になります

しかし、色を指定するたびに DWORD 型に各色素をビット演算していては面倒です
やはり DirectX Graphics も Win32 API のようにマクロをもってこれを解決してくれます
アルファ値を指定する場合 D3DCOLOR_ARGB() を使います
D3DCOLOR_ARGB(a,r,g,b) \
    ((D3DCOLOR)((((a)&0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff)))
a にはアルファ値を、r には赤、g には緑、b には青の色素値を指定します
定義を見れば、それぞれが、適切なビット位置にシフトされていることがわかりますね
また、D3DCOLOR_RGBA() マクロというのも定義されています

D3DCOLOR_RGBA(r,g,b,a) D3DCOLOR_ARGB(a,r,g,b)

定義を見ればわかりますが、単純に D3DCOLOR_ARGB() マクロに
引数の順番を入れ換えて渡しているだけにすぎません
アルファ値に興味が無ければ D3DCOLOR_XRGB() マクロを使います

D3DCOLOR_XRGB(r,g,b) D3DCOLOR_ARGB(0xff,r,g,b)

これは、アルファ値のパラメータに 0xFF をデフォルトで指定しているだけです
後は、マクロ引数に指定した RGB 値が D3DCOLOR_ARGB() マクロにそっくり渡されます

さて、バッファをクリアしたならば、次はバッファを画面に表示します
こうすれば、画面を指定した色で塗りつぶすことができるでしょう
バッファを表示するには IDirect3DDevice8::Present() を使います
HRESULT Present(
	CONST RECT* pSourceRect , CONST RECT* pDestRect ,
	HWND hDestWindowOverride , CONST RGNDATA* pDirtyRegion
);
pSourceRect には転送元の矩形を表す RECT 構造体、
pDestRect は転送先のクライアント座標を含む RECT 構造体へのポインタを指定します
これらの引数に NULL を指定した場合、全体が対象となります
D3DSWAPEFFECT_COPY か D3DSWAPEFFECT_COPY_VSYNC 以外の
スワップエフェクトを指定している場合、これらの引数は常に NULL を指定します

hDestWindowOverride には、転送先のウィンドウハンドルを指定します
ウィンドウモードの時に指定する引数ですが NULL を指定することもできます
NULL の場合、D3DPRESENT_PARAMETERS の hWndDeviceWindow メンバが使われます

pDirtyRegion は常に NULL を指定します
この引数は将来のために予約された引数で、現在は未使用です
メソッドが成功すれば D3D_OK が、失敗すればエラー値が返ります
このメソッドが返す可能性のあるエラー値は、以下のものが考えられます

定数解説
D3DERR_INVALIDCALL メソッドの呼び出しが無効である
D3DERR_DEVICELOST デバイスが失われている
復元できないためレンタリングは不可能である

これらのメソッドを使って、バッファを指定色でクリアし
それをウィンドウに描画すれば、実際に指定した色でクライアント領域が塗りつぶされます
#include <windows.h>
#include <d3d8.h>
#define TITLE 	TEXT("Kitty on your lap")

IDirect3D8 * pDirect3D;
IDirect3DDevice8 * pD3Device;
D3DPRESENT_PARAMETERS d3dpp;

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_PAINT:
		if (!pD3Device) break;
		pD3Device->Clear(0 , NULL , D3DCLEAR_TARGET ,
			D3DCOLOR_XRGB(0 , 0 , 0) , 1.0 , 0);

		pD3Device->Present(NULL,NULL,NULL,NULL);
		ValidateRect(hWnd , NULL);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow) {
	MSG msg;
	HWND hWnd;
	WNDCLASS winc;
	D3DDISPLAYMODE d3ddm;

	pDirect3D = Direct3DCreate8(D3D_SDK_VERSION);
	pDirect3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT , &d3ddm);

	ZeroMemory(&d3dpp , sizeof(D3DPRESENT_PARAMETERS));
	d3dpp.BackBufferFormat = d3ddm.Format;
	d3dpp.BackBufferCount = 1;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.Windowed = TRUE;

	winc.style 		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra		= winc.cbWndExtra = 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return 0;

	hWnd = CreateWindow(
		TEXT("KITTY") , TITLE , WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
		CW_USEDEFAULT , CW_USEDEFAULT , CW_USEDEFAULT , CW_USEDEFAULT ,
		NULL , NULL , hInstance , NULL
	);
	if (!hWnd) return 0;

	pDirect3D->CreateDevice(
		D3DADAPTER_DEFAULT , D3DDEVTYPE_HAL , hWnd ,
		D3DCREATE_SOFTWARE_VERTEXPROCESSING , &d3dpp , &pD3Device
	);

	while (GetMessage(&msg , NULL , 0 , 0 )) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	pDirect3D->Release();
	pD3Device->Release();
	return msg.wParam;
}


このプログラムは、WM_PAINT メッセージが発行されると
バッファを黒く塗りつぶし、それを Present() メソッドで画面に描画しています
ウィンドウのクライアント領域は白ですが、図を見てわかるように黒く塗りつぶされています
WM_PAINT メッセージで行っている、クリアと表示処理に注目してください

しかし、このプログラムは未完成な部分があります
ウィンドウのサイズを変更すると、奇妙にも動作が遅くなってしまいます

バッファのサイズはデバイスを初期化した時に決定されていますが
バッファと描画矩形のサイズが異なる場合 Present() メソッドは
バッファを拡大縮小して、クライアント領域に描画するのです

つまり、このプログラムのウィンドウのサイズを変更すると
バッファのビットマップが拡大縮小され、ウィンドウに表示されているということになり
これは基本的に無意味であり、無駄なパフォーマンスの低下を招くことになります

バッファのサイズを動的にウィンドウサイズに合わせるには
WM_SIZE が発行される度に、スワップチェーンをリセットする必要があります
そのためには IDirect3DDevice8::Reset() メソッドを使います
HRESULT Reset(
	D3DPRESENT_PARAMETERS* pPresentationParameters
);
pPresentationParameters は、新しいデバイスの状態を表す
D3DPRESENT_PARAMETERS 構造体へのポインタを指定します
メソッドは、この情報に基いてスワップチェーンを新しく設定します

成功すれば D3D_OK が、そうでなければエラー値が返ります
このメソッドは、以下のエラー値を返す可能性があります

定数解説
D3DERR_INVALIDCALL メソッドの呼び出しが無効である
D3DERR_OUTOFVIDEOMEMORY 処理を行うのに必要となる
十分なディスプレイメモリが無い
E_OUTOFMEMORY 十分なメモリを割り当てることができなかった
#include <windows.h>
#include <d3d8.h>
#define TITLE 	TEXT("Kitty on your lap")

IDirect3D8 * pDirect3D;
IDirect3DDevice8 * pD3Device;
D3DPRESENT_PARAMETERS d3dpp;

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_PAINT:
		if (!pD3Device) break;
		pD3Device->Clear(0 , NULL , D3DCLEAR_TARGET ,
			D3DCOLOR_XRGB(0 , 0 , 0) , 1.0 , 0);

		pD3Device->Present(NULL,NULL,NULL,NULL);
		ValidateRect(hWnd , NULL);
		return 0;
	case WM_SIZE:
		if (!pD3Device) return 0;
		d3dpp.BackBufferWidth = LOWORD(lp);
		d3dpp.BackBufferHeight = HIWORD(lp);
		pD3Device->Reset(&d3dpp);
		InvalidateRect(hWnd , NULL , TRUE);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow) {
	MSG msg;
	HWND hWnd;
	WNDCLASS winc;
	D3DDISPLAYMODE d3ddm;

	pDirect3D = Direct3DCreate8(D3D_SDK_VERSION);
	pDirect3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT , &d3ddm);

	ZeroMemory(&d3dpp , sizeof (D3DPRESENT_PARAMETERS));
	d3dpp.BackBufferFormat = d3ddm.Format;
	d3dpp.BackBufferCount = 1;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.Windowed = TRUE;

	winc.style 		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra		= winc.cbWndExtra = 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return 0;

	hWnd = CreateWindow(
		TEXT("KITTY") , TITLE , WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
		CW_USEDEFAULT , CW_USEDEFAULT , CW_USEDEFAULT , CW_USEDEFAULT ,
		NULL , NULL , hInstance , NULL
	);
	if (!hWnd) return 0;

	pDirect3D->CreateDevice(
		D3DADAPTER_DEFAULT , D3DDEVTYPE_HAL , hWnd ,
		D3DCREATE_SOFTWARE_VERTEXPROCESSING , &d3dpp , &pD3Device
	);

	while (GetMessage(&msg , NULL , 0 , 0 )) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	pDirect3D->Release();
	pD3Device->Release();
	return msg.wParam;
}
さて、これで DirectX Graphics の基本を押さえることができました
一般に DirectX Graphics のスケルトンプログラムは、このような形となるでしょう

WM_SIZE メッセージを受けると、ウィンドウサイズを構造体に設定し
それを Reset() メソッドに渡して、バッファを初期化していることがわかりますね


IDirect3DDevice8::Clear()

HRESULT Clear(
	DWORD Count , CONST D3DRECT* pRects ,
	DWORD Flags , D3DCOLOR Color ,
	float Z , DWORD Stencil
);
指定した色でバッファをクリアします

Count - pRects で指定する矩形の数を指定します
pRects - クリアする領域を示した矩形配列へのポインタを指定します
Flags - クリアする対象を指定します
Color - クリア時に画面を塗りつぶす色を指定します
Z - 0.0 〜 1.0 までの深度バッファに保存する新しい z 値を指定します
Stencil - 各ステンシルバッファのエントリに保存する整数値を指定します

戻り値 - 成功すれば D3D_OK、失敗すれば D3DERR_INVALIDCALL.

Flags には、以下の定数を組み合わせて指定することができます

定数解説
D3DCLEAR_STENCIL ステンシル バッファをクリアして、Stencil パラメータの値にする
D3DCLEAR_TARGET レンダリング ターゲットをクリアして、Color パラメータの色にする
D3DCLEAR_ZBUFFER 深度バッファをクリアして、Z パラメータの値にする

D3DCOLOR_ARGB()

D3DCOLOR_ARGB(a,r,g,b)

アルファ値、赤、緑、青の色素値からカラー値を生成します

a - アルファ値を指定します
r - 赤色要素の強さを指定します
g - 緑色要素の強さを指定します
b - 青色要素の強さを指定します

戻り値 - 指定した色を表す D3DCOLOR 型の値

D3DCOLOR_RGBA)

D3DCOLOR_RGBA(r,g,b,a)

アルファ値、赤、緑、青の色素値からカラー値を生成します

r - 赤色要素の強さを指定します
g - 緑色要素の強さを指定します
b - 青色要素の強さを指定します
a - アルファ値を指定します

戻り値 - 指定した色を表す D3DCOLOR 型の値

D3DCOLOR_XRGB()

D3DCOLOR_XRGB(r,g,b)

赤、緑、青の色素値からカラー値を生成します

r - 赤色要素の強さを指定します
g - 緑色要素の強さを指定します
b - 青色要素の強さを指定します

戻り値 - 指定した色を表す D3DCOLOR 型の値

IDirect3DDevice8::Present()

HRESULT Present(
	CONST RECT* pSourceRect , CONST RECT* pDestRect ,
	HWND hDestWindowOverride , CONST RGNDATA* pDirtyRegion
);
デバイスが所有するバックバッファを表示します

pSourceRect - 転送先の矩形を表す RECT 構造体をへのポインタ指定します
pDestRect - 転送先の矩形を表す RECT 構造体へのポインタを指定します
hDestWindowOverride - 転送先のウィンドウハンドルを指定します
pDirtyRegion - 常に NULL を指定します

戻り値 - 成功すれば D3D_OK、失敗すればエラー値

このメソッドが返す可能性のあるエラー値は、以下のものが考えられます

定数解説
D3DERR_INVALIDCALL メソッドの呼び出しが無効である
D3DERR_DEVICELOST デバイスが失われている
復元できないためレンタリングは不可能である

IDirect3DDevice8::Reset()

HRESULT Reset(
	D3DPRESENT_PARAMETERS* pPresentationParameters
);
スワップチェーンのタイプ、サイズ、フォーマットをリセットします

pPresentationParameters - 新しいデバイスの状態を指定します

戻り値 - 成功すれば D3D_OK、そうでなければエラー値

定数解説
D3DERR_INVALIDCALL メソッドの呼び出しが無効である
D3DERR_OUTOFVIDEOMEMORY 処理を行うのに必要となる
十分なディスプレイメモリが無い
E_OUTOFMEMORY 十分なメモリを割り当てることができなかった



前のページへ戻る次のページへ