DirectXプログラミング04 三角形ポリゴンの表示

̃Gg[͂ĂȃubN}[Nɒlj

本シリーズ、待望(?)の3D物体の表示に入ります。
まずは基本単位である三角形のポリゴンを表示させます。

三角形のポリゴンを表示

WinMain関数、ウィンドウプロシージャ関数

メッセージループでPeekMessage結果がNULLの時今回追加する描画関数をコールするようにしておきます。

Direct3D初期化関数

描画関数内で扱う「頂点バッファ」情報を生成するコードが必要になります。
■頂点データの初期化
参考にした文献では、頂点を表すデータ型を以下のように定義しています。

座標(x, y, z)で色情報がcolorの頂点ですね。

struct CUSTOMVERTEX
{
	FLOAT x, y, z;
	DWORD color;
};

Direct3D初期化関数内で上記型の配列を以下のように初期化します。

	/*	頂点データの初期化	*/
	CUSTOMVERTEX vertices[]	=
	{
		{	-1.0f, -1.0f, 0.0f, 0xffff0000,	},
		{	 1.0f, -1.0f, 0.0f, 0xff00ff00,	},
		{	 0.0f,  1.0f, 0.0f, 0xff0000ff,	},
	};

3つの頂点情報が格納されており、z座標はそろっているので、
頂点データverticesが表すのは、
x-y平面上にある原点(0,0,0)を中心とする正三角形の頂点になりますね。
■頂点バッファ
作成した頂点データverticesを頂点バッファ型変数に格納します。
頂点バッファはLPDIRECT3DVERTEXBUFFER9型の変数(pVB)です。

LPDIRECT3DVERTEXBUFFER9 pVB = NULL;	/*	頂点バッファ	*/

Direct3D初期化関数内でCreateVertexBufferメソッドにより頂点バッファを生成します。

	/*	頂点バッファを作成	*/
	if(pDevice->CreateVertexBuffer(sizeof(vertices),			/*	バッファのサイズ	*/
								   0,							/*	当面は0でいいらしい	*/
								   D3DFVF_XYZ | D3DFVF_DIFFUSE,	/*	FVF定数(頂点の属性)を指定	*/
								   D3DPOOL_DEFAULT,				/*	当面はdefaultでいいらしい	*/
								   &pVB,						/*	頂点バッファへのポインタ	*/
								   NULL))						/*	常にNULL	*/
	{
		MessageBox(0, L"頂点バッファの作成に失敗しました", L"", MB_OK);
		return E_FAIL;
	}

上記でまだpVBは空です。頂点データverticesを渡す処理は以下になります。
pVBのロック時に渡すpVerticesが頂点データverticesを指すポインタになります。

	/*	この時点でまだpVBは空。以下でvertices構造体をpVBに格納する	*/
	VOID* pVertices;	/*	vertices構造体を格納する変数宣言		*/
	/*	頂点バッファをロック(アクセス権獲得)	*/
	if(FAILED(pVB->Lock(0,
						sizeof(vertices),		/*	全体サイズ	*/
						(void**)&pVertices,		/*	vertices構造体を格納する変数	*/
						0)))					/*	defaultの0を指定	*/
	{
		MessageBox(0, L"頂点バッファのロックに失敗しました", L"", MB_OK);
		return E_FAIL;
	}
	/*	pVerticesにvertices構造体を格納する変数宣言	*/
	memcpy(pVertices, vertices, sizeof(vertices));
	/*	頂点バッファをアンロック	*/
	pVB->Unlock();

若干の表示調整が必要なようです。
DirectX初期化関数内でレンダリングステートを以下のように設定します。

	/*	レンダリングステートの設定	*/
	/*	カリングしない(=ポリゴンの背面はレンダリングする)	*/
	pDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE);
	/*	ライトをオフにする(=現時点ではオンだと真っ黒にレンダリングされる)	*/
	pDevice->SetRenderState( D3DRS_LIGHTING, FALSE);
描画関数

メッセージループでPeekMessage結果がNULLの時コールするようにしておきます。
3つの変換処理と、レンダリング処理を記述します。

【3つの変換】
いずれも、Direct3DデバイスオブジェクトのSetTransformメソッドで実施します。

①ワールドトランスフォーム(絶対座標変換)
ジオメトリ(ポリゴン(やポリゴンから成るメッシュ))のローカル座標のワールド座標(絶対)座標への変換らしいです。
Direct3Dデバイスオブジェクト->SetTransform(D3DTS_WORLD, ワールドトランスフォーム用行列)

②ビュートランスフォーム
カメラ位置の設定らしいです。
Direct3Dデバイスオブジェクト->SetTransform(D3DTS_VIEW, ビュートランスフォーム用行列)

③プロジェクショントランスフォーム(射影変換)
カメラレンズのズーム等の設定らしいです。
Direct3Dデバイスオブジェクト->SetTransform(D3DTS_PROJECTION, プロジェクショントランスフォーム用行列)

※上記①~③は今回のように、固定視点で三角形を一つ表示するだけならば不要らしいです
※だけど知っておく必要はあるそうです。
※各行列の設定については、今回は取り上げません。

【レンダリング】
BeginSceneとEndSceneの間に以下を行います。
■頂点バッファ・サイズを通知
Direct3DデバイスオブジェクトのSetStreamSourceメソッドに頂点バッファへのポインタとサイズを渡します。
■頂点の性質を通知
Direct3DデバイスオブジェクトのSetFVFメソッドに頂点の性質を表すFVF定数を渡します。
■レンダリング関数コール
Direct3DデバイスオブジェクトのDrawPrimitiveメソッドをコールします。

	if(SUCCEEDED(pDevice->BeginScene())){
		/*	頂点バッファとサイズをDirect3Dに通知	*/
		pDevice->SetStreamSource(0,
								 pVB,	
								 0,
								 sizeof(CUSTOMVERTEX));
		/*	頂点の性質をDirect3Dに通知	*/
		pDevice->SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE);
		/*	レンダリング(D3DPT_TRIANGLELISTは連続した三角形の描画)	*/
		pDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);
		pDevice->EndScene();
	}
	pDevice->Present(NULL,
					 NULL,
					 NULL,
					 NULL); 

実行結果

※全体コードは記事の末尾に載せています。
directx04_window01

、、、むう、確かに三角形は表示されていますが、、
ほんとに3Dとしての表示になっているのでしょうか????

そこでカメラの位置をキーボードで変更できるようにしてみます。
ビュートランスフォーム処理で、カメラの位置を変数に変更します。

D3DXVECTOR3 vecEyePt( CamX, CamY, CamZ);	/*	カメラ(視点)位置	*/

ウィンドウプロシージャをキーボード入力メッセージに対応させます。
矢印キーでx, y座標、テンキーの8と0でz座標を変更するようにします。

	switch(iMsg){
		case WM_DESTROY :
			PostQuitMessage(0);
		break;
		case WM_KEYDOWN :
			switch((CHAR)wParam){
				case VK_ESCAPE:
				PostQuitMessage(0);
				break;
				case VK_LEFT:
					CamX += 0.5;
				break;
				case VK_RIGHT:
					CamX -= 0.5;
				break;
				case VK_UP:
					CamY += 0.5;
				break;
				case VK_DOWN:
					CamY -= 0.5;
				break;
				case VK_NUMPAD8:
					CamZ += 0.5;
				break;
				case VK_NUMPAD2:
					CamZ -= 0.5;
				break;
			}
		break;
	}

directx04_pic01なお、各座標軸の向きは図のように左手系です。

カメラ位置を変更したスクリーンショットを以下に掲載します。
(上段:x軸移動、中断:y軸移動、下段;z軸移動)。

、、、なんとなく、3D空間に描画されていることが分かります。
directx04_window02

ソースコード

#include <windows.h>
#include <d3dx9.h>
#define SAFE_RELEASE(p) {if(p){(p)->Release(); (p)=NULL;}}

struct CUSTOMVERTEX
{
	FLOAT x, y, z;
	DWORD color;
};
FLOAT CamX=0, CamY=0, CamZ=3;
/*	Direct3D関連	*/
LPDIRECT3D9 pD3d;
LPDIRECT3DDEVICE9 pDevice;
/*	ポリゴン関連	*/
LPDIRECT3DVERTEXBUFFER9 pVB = NULL;	/*	頂点バッファ	*/

/*	プロトタイプ宣言	*/
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HRESULT InitD3d(HWND);
VOID Render();
VOID FreeDx();

/*	エントリ関数	*/
INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR szStr, INT iCmdShow)
{
	HWND hWnd = NULL;
	MSG msg;
	
	/*	ウィンドウの初期化	*/
	LPCWSTR szAppName = L"DirectXプログラミング04 三角形ポリゴンの表示";
	WNDCLASSEX wndclass;
	wndclass.cbSize			= sizeof(wndclass);
	wndclass.style			= CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc	= WndProc;
	wndclass.cbClsExtra		= 0;
	wndclass.cbWndExtra		= 0;
	wndclass.hInstance		= hInst;
	wndclass.hIcon			= LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground	= (HBRUSH)GetStockObject(BLACK_BRUSH);
	wndclass.lpszMenuName	= NULL;
	wndclass.lpszClassName	= szAppName;
	wndclass.hIconSm		= LoadIcon(NULL, IDI_APPLICATION);
	RegisterClassEx(&wndclass);
	hWnd = CreateWindow(szAppName,
						szAppName,
						WS_OVERLAPPEDWINDOW,
						0,
						0,
						640,
						480,
						NULL,
						NULL,
						hInst,
						NULL);
	ShowWindow(hWnd, SW_SHOW);
	UpdateWindow(hWnd);
	
	/*	Direct3Dの初期化	*/
	if(FAILED(InitD3d(hWnd))){
		return 0;
	}
	/*	メッセージループ	*/
	ZeroMemory(&msg, sizeof(msg));
	while(msg.message != WM_QUIT){
		if(PeekMessage( &msg,
						NULL,
						0U,
						0U,
						PM_REMOVE)){
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		} else {
			Render();
		}
	}
	/*	メッセージループから抜けたらオブジェクトをすべて開放する。	*/
	FreeDx();

	/*	アプリケーションを終了する	*/
	return(INT)msg.wParam;
}

/*	ウィンドウプロシージャ	*/
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
	switch(iMsg){
		case WM_DESTROY :
			PostQuitMessage(0);
		break;
		case WM_KEYDOWN :
			switch((CHAR)wParam){
				case VK_ESCAPE:
				PostQuitMessage(0);
				break;
				case VK_LEFT:
					CamX += 0.5;
				break;
				case VK_RIGHT:
					CamX -= 0.5;
				break;
				case VK_UP:
					CamY += 0.5;
				break;
				case VK_DOWN:
					CamY -= 0.5;
				break;
				case VK_NUMPAD8:
					CamZ += 0.5;
				break;
				case VK_NUMPAD2:
					CamZ -= 0.5;
				break;
			}
		break;
	}
	return DefWindowProc(hWnd, iMsg, wParam, lParam);
}

/*	Direct3Dの初期化	*/
HRESULT InitD3d(HWND hWnd)
{
	/*	Direct3Dオブジェクトの生成	*/
	if(NULL == (pD3d = Direct3DCreate9(D3D_SDK_VERSION))){
		MessageBox(0, L"Direct3Dの作成に失敗しました", L"", MB_OK);
		return E_FAIL;
	}
	/*	Direct3Dデバイスオブジェクトの生成	*/
	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory(&d3dpp, sizeof(d3dpp));
	d3dpp.BackBufferFormat	= D3DFMT_UNKNOWN;
	d3dpp.BackBufferCount	= 1;
	d3dpp.SwapEffect		= D3DSWAPEFFECT_DISCARD;
	d3dpp.Windowed			= TRUE;
	if(FAILED(pD3d->CreateDevice(	D3DADAPTER_DEFAULT,
									D3DDEVTYPE_HAL,
									hWnd,
									D3DCREATE_MIXED_VERTEXPROCESSING,
									&d3dpp,
									&pDevice))){
		MessageBox(0, L"HALモードでDirect3Dデバイスを作成できませんnREFモードで再試行します", NULL, MB_OK);
		if(FAILED(pD3d->CreateDevice(D3DADAPTER_DEFAULT,
									 D3DDEVTYPE_REF,
									 hWnd,
									 D3DCREATE_MIXED_VERTEXPROCESSING,
									 &d3dpp,
									 &pDevice))){
			 MessageBox(0, L"DIRECT3Dデバイスの作成に失敗しました", NULL, MB_OK);
			 return E_FAIL;
		}
	}
	/*	頂点データの初期化	*/
	CUSTOMVERTEX vertices[]	=
	{
		{	-1.0f, -1.0f, 0.0f, 0xffff0000,	},
		{	 1.0f, -1.0f, 0.0f, 0xff00ff00,	},
		{	 0.0f,  1.0f, 0.0f, 0xff0000ff,	},
	};
	/*	頂点バッファを作成	*/
	if(pDevice->CreateVertexBuffer(sizeof(vertices),			/*	バッファのサイズ	*/
								   0,							/*	当面は0でいいらしい	*/
								   D3DFVF_XYZ | D3DFVF_DIFFUSE,	/*	FVF定数(頂点の属性)を指定	*/
								   D3DPOOL_DEFAULT,				/*	当面はdefaultでいいらしい	*/
								   &pVB,						/*	頂点バッファへのポインタ	*/
								   NULL))						/*	常にNULL	*/
	{
		MessageBox(0, L"頂点バッファの作成に失敗しました", L"", MB_OK);
		return E_FAIL;
	}
	/*	この時点でまだpVBは空。以下でvertices構造体をpVBに格納する	*/
	VOID* pVertices;	/*	vertices構造体を格納する変数宣言		*/
	/*	頂点バッファをロック(アクセス権獲得)	*/
	if(FAILED(pVB->Lock(0,
						sizeof(vertices),		/*	全体サイズ	*/
						(void**)&pVertices,		/*	vertices構造体を格納する変数	*/
						0)))					/*	defaultの0を指定	*/
	{
		MessageBox(0, L"頂点バッファのロックに失敗しました", L"", MB_OK);
		return E_FAIL;
	}
	/*	pVerticesにvertices構造体を格納する変数宣言	*/
	memcpy(pVertices, vertices, sizeof(vertices));
	/*	頂点バッファをアンロック	*/
	pVB->Unlock();
	
	/*	レンダリングステートの設定	*/
	/*	カリングしない(=ポリゴンの背面はレンダリングする)	*/
	pDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE);
	/*	ライトをオフにする(=現時点ではオンだと真っ黒にレンダリングされる)	*/
	pDevice->SetRenderState( D3DRS_LIGHTING, FALSE);
	return S_OK;
}
/*	頂点バッファの頂点をレンダリング	*/
VOID Render()
{
	/*	ポリゴン(やメッシュ)のレンダリングに必要な3つのトランスフォームを実施	*/
	/*	1:ワールドトランスフォーム(絶対座標変換)			*/
	/*		ローカル座標をワールド座標絶対座標に変換する	*/
	D3DXMATRIXA16 matWorld;
	D3DXMatrixIdentity(&matWorld);
	/*	matWorld行列を係数としてワールドトランスフォーム実施	*/
	pDevice->SetTransform(D3DTS_WORLD, &matWorld);
	
	/*	2:ビュートランスフォーム(視点座標変換)	*/
	/*		カメラの位置を指定する。				*/
	D3DXVECTOR3 vecEyePt( CamX, CamY, CamZ);	/*	カメラ(視点)位置	*/
	D3DXVECTOR3 vecLookatPt( 0.0f, 0.0f, 0.0f);/*	注視位置	*/
	D3DXVECTOR3 vecUpVec( 0.0f, 1.0f, 0.0f);	/*	上方位置	*/
	//D3DMATRIXA16 matView;
	D3DMATRIX matView;
	D3DXMatrixLookAtLH((D3DXMATRIX *)&matView,
					   &vecEyePt,
					   &vecLookatPt,
					   &vecUpVec);
	pDevice->SetTransform( D3DTS_VIEW, &matView);

	/*	3:プロジェクショントランスフォーム(射影変換)	*/
	/*		カメラレンズのズーム等の指定				*/
	D3DXMATRIXA16 matProj;
	D3DXMatrixPerspectiveFovLH( &matProj, 
								D3DX_PI/4,
								1.0f,
								1.0f,
								100.f);
	pDevice->SetTransform( D3DTS_PROJECTION, &matProj);

	/*	レンダリング	*/
	pDevice->Clear(	0,
					NULL,
					D3DCLEAR_TARGET,
					D3DCOLOR_XRGB(100,100,100),
					1.0f,
					0);
	if(SUCCEEDED(pDevice->BeginScene())){
		/*	頂点バッファとサイズをDirect3Dに通知	*/
		pDevice->SetStreamSource(0,
								 pVB,	
								 0,
								 sizeof(CUSTOMVERTEX));
		/*	頂点の性質をDirect3Dに通知	*/
		pDevice->SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE);
		/*	レンダリング(D3DPT_TRIANGLELISTは連続した三角形の描画)	*/
		pDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);
		pDevice->EndScene();
	}
	pDevice->Present(NULL,
					 NULL,
					 NULL,
					 NULL); 
					
}
/*	DirectXオブジェクトの解放	*/
VOID FreeDx()
{
	SAFE_RELEASE(pVB);
	SAFE_RELEASE(pDevice);
	SAFE_RELEASE(pD3d);
}

今回は三角形一つのレンダリングだったので、まだ3Dっぽさが実感できないですね。。
次回はXファイルからメッシュを読み込んでレンダリングしてみたいと思います。


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です