描画処理


頂点と線と面

今回は、いよいよデバイスに図を描画してみたいと思います
GDI で、描画処理を行う時に BeginPaint() 〜 EndPaint() というような
描画の開始、及び終了を表す関数があったように
DirectX Graphics でも、開始と終了を表すメソッドが存在します

バッファへの描画を開始するには IDirect3DDevice8::BeginScene() を使い
描画を終了したなら IDirect3DDevice8::EndScene() を呼び出します

HRESULT BeginScene();
HRESULT EndScene();

それぞれ、成功すれば D3D_OK が、失敗すれば D3DERR_INVALIDCALL が返ります
これらのメソッドの間に、バッファに描画するメソッドを指定します

3次元グラフィックスにおいて、図は2次元と考え方が違います
まず最初の難関となるのが、座標の理解です
3次元グラフィックスは、奥行きを表す Z 座標が存在します
この一つの座標が増えたために、座標変換は極めて複雑になりました

3次元において、図はポリゴンで表現されます
つまり、図の基本構造は単純な線と面で構成されるのです

より分解すると、私たちが指定するのは座標を指定したの集合です
そして、この点をどのように結びつけるのかをデバイスに指定することで図形とします
問題は、この点の座標をどのように指定するかということです
この点のことを、3次元グラフィックスでは頂点と呼びます

さらに、3次元グラフィックスには物体にあたる光の反射ライティング
物体の性質マテリアルなども指定する必要があります
これらの高度なトピックスについては、後ほど少しずつ解説していきます

座標を指定するには、まず頂点フォーマットを決定します
頂点フォーマットとは、描画に必要なデータをを集め、これを定義したものです
必ず必要なものに座標がありますが、この他にも色などのデータを含めます

頂点フォーマットを定義するには
IDirect3DDevice8::SetVertexShader() メソッドを使います
図を描画するには、まずこれを用いて頂点フォーマットを決定する必要があります

HRESULT SetVertexShader(DWORD Handle);

Handle には、頂点フォーマットのフラグを指定します
この引数には、以下の定数を組み合わせて指定することができます

定数解説
D3DFVF_DIFFUSE 頂点フォーマットがディフューズ色成分を含む
D3DFVF_NORMAL 頂点フォーマットが頂点法線ベクトルを含む
このフラグを、D3DFVF_XYZRHW と共に使用することはできない
D3DFVF_PSIZE 頂点フォーマットはポイント サイズで指定されている
このサイズは、トランスフォーム済みでも
ライティング済みでもない頂点に対するカメラ空間単位
およびトランスフォーム済みでライティング済みの
頂点に対するデバイス空間単位で表されている
D3DFVF_SPECULAR 頂点フォーマットがスペキュラ色成分を含む
D3DFVF_XYZ 頂点フォーマットが未トランスフォーム頂点の位置座標を含む
このフラグを、D3DFVF_XYZRHW と共に使用することはできない
D3DFVF_XYZRHW 頂点フォーマットが、トランスフォームされた頂点の位置座標を含む
このフラグを D3DFVF_XYZ
または D3DFVF_NORMAL フラグと同時に使用することはできない
D3DFVF_XYZB1 〜
D3DFVF_XYZB5
頂点フォーマットは、位置座標データ
および複数の行列による
頂点ブレンディング処理用の加重 (ベータ) 値の対応数を含む
現在、MicrosoftR Direct3DR では最大 3 つの加重値
および 4 つのブレンディング行列でブレンディングが可能である
D3DFVF_TEX0 〜
D3DFVF_TEX8
この頂点に対応するテクスチャ座標セットの番号
これらのフラグに対応する数字は連続番号になっていない
D3DFVF_TEXTUREFORMAT1 〜
D3DFVF_TEXTUREFORMAT4
テクスチャ座標セットを定義する値の番号
D3DFVF_TEXTUREFORMAT1 は 1 次元座標を示し
D3DFVF_TEXTUREFORMAT2 は 2 次元座標を示し、という順に以下続く
このフラグが単独で使用されることはまれで
D3DFVF_TEXCOORDSIZEn マクロと共に使用される
D3DFVF_POSITION_MASK 位置座標ビットを抽出するマスク
D3DFVF_RESERVED0
D3DFVF_RESERVED2
柔軟な頂点フォーマットの予約ビットを抽出するためのマスク値
使用不可
D3DFVF_TEXCOUNT_MASK テクスチャ フラグ ビットを抽出するためのマスク値
D3DFVF_LASTBETA_UBYTE4 インデックス付き頂点ブレンディングと
固定機能 FVF 頂点シェーダを使用する場合は
頂点シェーダにこのフラグを指定しなければならない
D3DFVF_TEXCOUNT_SHIFT 頂点のテクスチャ座標数を
識別する整数値を何ビット分シフトするかを示す数

成功すれば D3D_OK が、失敗すれば D3DERR_INVALIDCALL が返ります

このメソッドで指定したフラグの組み合わせによって、頂点フォーマットが決定します
そして、頂点フォーマットによってこれを指定するための構造体のサイズが決まります
この関係は、基本的に次のようなものとなります

意味とフラグ
座標位置
D3DFVF_XYZ
x 座標 (float)
y 座標(float)
z 座標(float)
同次座標ベクトル
D3DFVF_XYZRHW
rhw (float)
ブレンディングウェイト
D3DFVF_XYZB1 〜 D3DFVF_XYZB5
1 〜 5 個のデータ (float)
頂点法線
D3DFVF_NORMAL
法線 x (float)
法線 y (float)
法線 z (float)
ディフューズ色
D3DFVF_DIFFUSE
RGBA (DWORD)
スペキュラ色
D3DFVF_SPECULAR
RGBA (DWORD)
テクスチャ座標セット
D3DFVF_TEX0 〜 D3DFVF_TEX8
テクスチャ座標データ
D3DFVF_TEXTUREFORMAT1 〜
D3DFVF_TEXTUREFORMAT4
1 〜 4 のデータ (float)

どういうことかというと、頂点フォーマットによって型を決定し
独自のその型の構造体などを定義して、描画時の位置指定でこれを利用するのです
例えば、SetVertexShader() メソッドで D3DFVF_XYZRHW と D3DFVF_DIFFUSE を指定すると
構造体は、上の図を参考に計算すると次のようなサイズになります
struct {
	float x , y , z , rhw;
	DWORD color;
};
ポリゴンを描画する時に、頂点の位置や色を指定するために
メソッドの何らかの引数で、この構造体のポインタを渡すことになるでしょう
このように、頂点の情報は私たちが柔軟に組み合わせることができるのです
自分で細かく設定したい時は多くの情報を渡せばよいですし
最小限の情報で、あとは DirectX に計算させることも可能になっているということです

ところで、この頂点フォーマットには様々な見慣れない用語が含まれています
ブレンディングウェイトや頂点法線、同次座標ベクトルなどです
これらについて、今必要の無い情報は後ほど詳しく解説します

同次座標ベクトルとは、x、y、z 座標に対して、さらに w を加えた4次元ベクトルを指します
なぜ3次元画像を表すために、4次元のベクトルを使う必要があるのでしょうか
実は、この値は座標変換を行うために必要な値なのです

3次元の座標を変換するには、4 × 4 の行列を用いるのですが
3次元の座標形では、4 × 4 の行列を乗算することができないため w が加えられます
詳しくは、座標変換について解説する時に説明します

ディフューズ色は、頂点の色そのものを表します
スペキュラ色は、頂点の反射色を表します
これらの関係についても、ライティングについて解説する特に詳しく説明します

通常の図形を表示する場合は、基本的に上で記した構造体で十分です
つまり、座標を表す x、y、z と同次座標ベクトル rhw の float 型に
頂点の色を表すディフューズ色の DWORD 型を加えたものです

頂点フォーマットが準備できれば、次はいよいよ頂点を配置します
頂点は、頂点フォーマットで指定した型の構造体の配列を頂点の数だけ用意し
それぞれの頂点の座標や色など、定義した構造体に適切に指定します

後は IDirect3DDevice8::DrawPrimitiveUP() メソッドを使って
指定した頂点フォーマットの構造体の配列を渡し、デバイスに描画するだけです
HRESULT DrawPrimitiveUP(
	D3DPRIMITIVETYPE PrimitiveType , UINT PrimitiveCount ,
	CONST void* pVertexStreamZeroData , UINT VertexStreamZeroStride
);
PrimitiveType には、D3DPRIMITIVETYPE 列挙型から、プリミティブ型を指定します
この列挙型や、プリミティブについては下記を参照してください
PrimitiveCount には、描画するプリミティブの数を指定します

pVertexStreamZeroData は、描画する頂点データ配列へのポインタを指定します
VertexStreamZeroStride は、頂点のデータサイズを指定します
成功すれば D3D_OK が、失敗すれば D3DERR_INVALIDCALL が返ります

さて、またまた新しい難関が登場しました
プリミティブ とは、頂点を結び付けてできた図形のことを意味します
プリミティブの種類はいくつかあって、単純に点として描画したり
線で結んだり、あるいは3つの頂点を1単位として三角形を描画することもできます

D3DPRIMITIVETYPE 列挙型はプリミティブの種類を提供しています
この列挙型は、次のように定義されています
typedef enum _D3DPRIMITIVETYPE {
    D3DPT_POINTLIST             = 1,
    D3DPT_LINELIST              = 2,
    D3DPT_LINESTRIP             = 3,
    D3DPT_TRIANGLELIST          = 4,
    D3DPT_TRIANGLESTRIP         = 5,
    D3DPT_TRIANGLEFAN           = 6,

    D3DPT_FORCE_DWORD           = 0x7fffffff
} D3DPRIMITIVETYPE;
この列挙型のメンバは、以下のような意味を持っています

定数解説
D3DPT_POINTLIST 頂点を別個の点の集合としてレンダリングする
D3DPT_LINELIST 別個の線分のリストとして頂点をレンダリングする
このプリミティブ タイプを使用する呼び出しは
カウントが 2 より小さいか、奇数の場合は失敗する
D3DPT_LINESTRIP 頂点を単一のポリラインとしてレンダリングする
このプリミティブ タイプを使用する呼び出しは
カウントが 2 より小さい場合は失敗する
D3DPT_TRIANGLELIST 別個の三角形の連続として指定の頂点をレンダリングする
各 3 頂点のグループは個別に三角形を定義する
背面のカリングは、現在のカリング モード
レンダリング ステートの影響を受ける
D3DPT_TRIANGLESTRIP 頂点を三角形の展開図としてレンダリングする
背面カリング フラグは、偶数番号の三角形で自動的に反転する
D3DPT_TRIANGLEFAN 頂点を三角形の扇形としてレンダリングする
D3DPT_FORCE_DWORD この列挙型を強制的に 32 ビット サイズにコンパイルする
この値は使用されていない

実際に、頂点がどのように結ばれるかは、図で見たほうが早いでしょう

D3DPT_POINTLIST D3DPT_LINELIST D3DPT_LINESTRIP
D3DPT_TRIANGLELIST D3DPT_TRIANGLESTRIP D3DPT_TRIANGLEFAN

点や線の場合は、頂点の数がプリミティブ数となりますが
三角形の場合は、三角形の数がプリミティブ数となります
#include <windows.h>
#include <d3d8.h>
#define TITLE 	TEXT("Kitty on your lap")

IDirect3D8 * pDirect3D;
IDirect3DDevice8 * pD3Device;
D3DPRESENT_PARAMETERS d3dpp;

typedef struct {
	float x , y , z , rhw;
	DWORD diff;
} D3DVERTEX;

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	static D3DVERTEX pt[3] = {
		{200 , 10 , 1 , 1 , 0xFFFF0000} ,
		{400 , 200 , 1 , 1 , 0xFFFF0000} ,
		{10 , 200 , 1 , 1 , 0xFFFF0000}
	};

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_PAINT:
		if (!pD3Device) break;
		pD3Device->Clear(0 , NULL , D3DCLEAR_TARGET ,
			D3DCOLOR_XRGB(0xFF , 0xFF , 0xFF) , 1.0 , 0);
		pD3Device->BeginScene();

		pD3Device->SetVertexShader(D3DFVF_XYZRHW | D3DFVF_DIFFUSE);
		pD3Device->DrawPrimitiveUP(
			D3DPT_TRIANGLELIST, 1 , pt , sizeof (D3DVERTEX)
		);
		pD3Device->EndScene();
		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);
		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;
}


このプログラムは、3つの頂点を1つの三角形として描画しています
WM_PAINT メッセージにおける処理に注目してください
各頂点の情報は、独自に定義した D3DVERTEX 型の pt 配列変数で表現されています

単純な三角形を表示するだけでも、ものすごい複雑なことをやっています
この章では、頂点フォーマットとプリミティブの理解が最も重要です
しっかりと基本を押さえて、今後に望んでください
因みに、各頂点の色を別々にすれば、綺麗なグラデーションになります


IDirect3DDevice8::BeginScene()

HRESULT BeginScene();

シーンを開始します
レンタリングの実行前に、必ず呼び出す必要があります

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

IDirect3DDevice8::EndScene()

HRESULT EndScene();

シーンを終了します
レンタリングを終了した時に、必ず呼び出す必要があります

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

IDirect3DDevice8::SetVertexShader()

HRESULT SetVertexShader(DWORD Handle);

以前に作成した頂点シェーダ、または頂点フォーマットの固定機能シェーダに
現在の頂点シェーダを設定します

Handle - 頂点シェーダのハンドル、または頂点フォーマットフラグを指定します

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

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

定数解説
D3DFVF_DIFFUSE 頂点フォーマットがディフューズ色成分を含む
D3DFVF_NORMAL 頂点フォーマットが頂点法線ベクトルを含む
このフラグを、D3DFVF_XYZRHW と共に使用することはできない
D3DFVF_PSIZE 頂点フォーマットはポイント サイズで指定されている
このサイズは、トランスフォーム済みでも
ライティング済みでもない頂点に対するカメラ空間単位
およびトランスフォーム済みでライティング済みの
頂点に対するデバイス空間単位で表されている
D3DFVF_SPECULAR 頂点フォーマットがスペキュラ色成分を含む
D3DFVF_XYZ 頂点フォーマットが未トランスフォーム頂点の位置座標を含む
このフラグを、D3DFVF_XYZRHW と共に使用することはできない
D3DFVF_XYZRHW 頂点フォーマットが、トランスフォームされた頂点の位置座標を含む
このフラグを D3DFVF_XYZ
または D3DFVF_NORMAL フラグと同時に使用することはできない
D3DFVF_XYZB1 〜
D3DFVF_XYZB5
頂点フォーマットは、位置座標データ
および複数の行列による
頂点ブレンディング処理用の加重 (ベータ) 値の対応数を含む
現在、MicrosoftR Direct3DR では最大 3 つの加重値
および 4 つのブレンディング行列でブレンディングが可能である
D3DFVF_TEX0 〜
D3DFVF_TEX8
この頂点に対応するテクスチャ座標セットの番号
これらのフラグに対応する数字は連続番号になっていない
D3DFVF_TEXTUREFORMAT1 〜
D3DFVF_TEXTUREFORMAT4
テクスチャ座標セットを定義する値の番号
D3DFVF_TEXTUREFORMAT1 は 1 次元座標を示し
D3DFVF_TEXTUREFORMAT2 は 2 次元座標を示し、という順に以下続く
このフラグが単独で使用されることはまれで
D3DFVF_TEXCOORDSIZEn マクロと共に使用される
D3DFVF_POSITION_MASK 位置座標ビットを抽出するマスク
D3DFVF_RESERVED0
D3DFVF_RESERVED2
柔軟な頂点フォーマットの予約ビットを抽出するためのマスク値
使用不可
D3DFVF_TEXCOUNT_MASK テクスチャ フラグ ビットを抽出するためのマスク値
D3DFVF_LASTBETA_UBYTE4 インデックス付き頂点ブレンディングと
固定機能 FVF 頂点シェーダを使用する場合は
頂点シェーダにこのフラグを指定しなければならない
D3DFVF_TEXCOUNT_SHIFT 頂点のテクスチャ座標数を
識別する整数値を何ビット分シフトするかを示す数

IDirect3DDevice8::DrawPrimitiveUP()

HRESULT DrawPrimitiveUP(
	D3DPRIMITIVETYPE PrimitiveType , UINT PrimitiveCount ,
	CONST void* pVertexStreamZeroData , UINT VertexStreamZeroStride
);
ユーザーメモリポインタで指定された配列データを
指定されたジオメトリ・プリミティブのシーケンスとしてレンタリングします

PrimitiveType - D3DPRIMITIVETYPE 列挙型から、プリミティブ型を指定します
PrimitiveCount - 描画するプリミティブの数を指定します
pVertexStreamZeroData - 描画する頂点データ配列へのポインタを指定します
VertexStreamZeroStride - 頂点のデータサイズを指定します

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



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