頂点バッファ


柔軟な頂点処理

これまで、ポリゴンの頂点データは、私たちのプログラムが管理してきました
つまり、アプリケーションが管理するシステムメモリ領域に配置していました

しかし、DirectX の思想から考えれば、それ以上に速い領域が存在するならば
すなわち、ローカルビデオメモリや AGP メモリなどに保存可能であれば、これを使うべきです
当然、アプリケーションはこのような領域にアクセスする権限はありません
そこで、頂点バッファと呼ばれる方法によって、これを実現できます

頂点バッファは、頂点データの保存方法として最も最適なものとして推奨されます
頂点バッファとは IDirect3DVertexBuffer8 インターフェイスで表され
頂点データを含んだメモリデータを管理する、一つのリソースと考えることができます

頂点バッファを用いれば、単純にアプリケーションが制御する時に比べ
より柔軟で高速な、最適化された頂点の処理が期待できます

頂点バッファを使うには、まずその実体を作成しなければなりません
これには IDirect3DDevice8::CreateVertexBuffer() メソッドを使います
HRESULT CreateVertexBuffer(
	UINT Length , DWORD Usage ,
	DWORD FVF , D3DPOOL Pool ,
	IDirect3DVertexBuffer8** ppVertexBuffer
);
Length には、頂点バッファのサイズをバイト単位で指定します
ここには、必ず1つ以上の頂点データを格納できるだけのサイズを指定します
Usage は、必要の応じてリソースの制御方法を表すフラグを指定します
この引数に指定するフラグについては、下記のリファレンスを参照してください

FVF には、用いられる頂点フォーマットのフラグの組み合わせを指定します
Pool には、D3DPOOL 列挙型のメンバで、どのメモリを使用するかを指定できます
ppVertexBuffer は作成した頂点バッファを受けるポインタのポインタを指定します

メソッドが成功すれば D3D_OK が、失敗すればエラー値が返ります
このメソッドが返すエラー値には、以下のようなものがあります

定数解説
D3DERR_INVALIDCALL メソッドの呼び出しが無効である
D3DERR_OUTOFVIDEOMEMORY 十分なディスプレイメモリが存在しない
E_OUTOFMEMORY 十分なメモリを割り当てることができない

こうして作成した頂点バッファには、頂点データを格納することができ
そのデータは、デバイスと関連付けることで一括して描画することができるようになります
D3DPOOL 列挙型は次のように定義されています
typedef enum _D3DPOOL {
    D3DPOOL_DEFAULT                 = 0,
    D3DPOOL_MANAGED                 = 1,
    D3DPOOL_SYSTEMMEM               = 2,

    D3DPOOL_FORCE_DWORD             = 0x7fffffff
} D3DPOOL;
この列挙型のメンバは、次のような意味が定義されています
D3DPOOL_MANAGED フラグを用いる方法が、最も安全だと思われます

定数解説
D3DPOOL_DEFAULT リソースは、リソースに対して要求された使用方法に
最も適したメモリ プールに置かれる
通常、これは、ローカル ビデオ メモリと
AGP メモリの両方を含む、ビデオ メモリである
D3DPOOL_DEFAULT プールは、D3DPOOL_MANAGED
および D3DPOOL_SYTEMMEM から独立しており
リソースがデバイス アクセス用の優先メモリに配置されることを指定する
D3DPOOL_DEFAULT は、このリソースのメモリ プール タイプとして
D3DPOOL_MANAGED または D3DPOOL_SYSTEMMEM の
いずれかを選択しなければならないことを示しているのではないことに注意すること
D3DPOOL_DEFAULT プールに配置されたテクスチャは
ロックできないので、直接アクセスできない
代わりに、IDirect3DDevice8::CopyRects や
IDirect3DDevice8::UpdateTexture などの関数を使用しなければならない
D3DPOOL_MANAGED は、ほとんどのアプリケーションで
D3DPOOL_DEFAULT よりも適している場合が多い
Direct3D ランタイムでは未知の、ドライバ独自のピクセル フォーマットで作成された
テクスチャの一部はロックできることに注意すること
また、テクスチャとは異なり、スワップチェーン バックバッファ、レンダリング ターゲット
頂点バッファ、およびインデックス バッファがロックできることにも注意する

デバイスが消失した場合は、D3DPOOL_DEFAULT を使って作成したリソースは
IDirect3DDevice8::Reset を呼び出す前に解放しなければならない
詳細については、「消失したデバイス」を参照すること
D3DPOOL_MANAGED リソースは、必要に応じて、デバイスからアクセスできるメモリに自動的にコピーされる
管理リソースは、システム メモリにバックアップされるので
デバイスが消失したときに作成し直す必要がない
詳細については、「リソースの管理」を参照すること
管理リソースはロックできる
システム メモリのコピーだけが直接変更される
Direct3D は、必要に応じて、ドライバがアクセス可能なメモリに変更内容をコピーする
D3DPOOL_SYSTEMMEM 通常、3D デバイスによってアクセスできないメモリ
システム RAM を使用するが、ページング可能な RAM が減ることはない
これらのリソースは、デバイスが消失しても作成し直す必要がない
このプールのリソースはロックすることができ、D3DPOOL_DEFAULT を使って
作成されたメモリ リソースに対する IDirect3DDevice8::CopyRects
または IDirect3DDevice8::UpdateTexture 処理の転送元として使用できる
D3DPOOL_FORCE_DWORD この列挙型を強制的に 32 ビット サイズにコンパイルする
この値は使用されていない

頂点バッファのデータをやり取りするには、バッファへのポインタを取得します
これは移動可能メモリの可能性があり、最初に取得範囲をロックしてポインタを得ます
範囲のロックには IDirect3DVertexBuffer8::Lock() を用います
HRESULT Lock(
	UINT OffsetToLock , UINT SizeToLock ,
	BYTE** ppbData , DWORD Flags
);
OffsetToLock には、ロックする頂点データのオフセットを
SizeToLock はロックするオフセットからのサイズを、それぞれバイト単位で指定します

ppbData には、取得する頂点データへのポインタのポインタを指定します
このポインタから、アプリケーションは頂点データを操作することができます
Flags には、ロックの方法を表すフラグを指定します
この引数に指定できるフラグについては、下記のリファレンスを参照してください

このメソッドは、成功した場合は D3D_OK を
そうでなければ D3DERR_INVALIDCALL を返します

適切なフラグを指定取得したメモリポインタならば
後はこれに、必要なデータを代入したり、あるいは取得するなどして
頂点バッファに格納している頂点データのやり取りを行うことができます
このポインタが不用になれば IDirect3DVertexBuffer8::Unlock() を使います

HRESULT Unlock();

成功した場合は D3D_OK を、そうでなければ D3DERR_INVALIDCALL を返します
これを呼び出した後は、取得したメモリポインタを使うことはできません

因みに、Lock() の呼び出しは同じに複数呼び出すことが可能です
メモリがロックされると、DirectX は専用のカウンタでその回数をカウントし
適切な順序で割り当てたメモリをアンロックします
Unlock() を呼び出す回数は Lock() を呼び出した回数と同じでなければなりません

頂点バッファを描画するには、まずこれをデバイスに設定しなければなりません
設定するには IDirect3DDevice8::SetStreamSource() を使います
HRESULT SetStreamSource(
	UINT StreamNumber ,
	IDirect3DVertexBuffer8* pStreamData , UINT Stride
);
StreamNumber には、頂点バッファを設定するストリーム番号を指定します
この値は、必ず 「0 〜 最大ストリーム数 - 1」となります
頂点バッファは、頂点シェーダというものを使うことによって多重に設定できます
ただし、頂点シェーダを扱わないデフォルトの場合では 0 を指定します

pStreamData には頂点バッファへのポインタを
Stride には、頂点データの一つ分のデータサイズをバイト単位で指定します
成功した場合は D3D_OK を、そうでなければ D3DERR_INVALIDCALL を返します

こうして、正しくデバイスに頂点バッファが設定されれば
後は IDirect3DDevice8::DrawPrimitive() メソッドを使って描画します
HRESULT DrawPrimitive(
	D3DPRIMITIVETYPE PrimitiveType ,
	UINT StartVertex , UINT PrimitiveCount
);
PrimitiveType には、D3DPRIMITIVETYPE 列挙型のメンバでプリミティブを指定します
StartVertex は、描画を開始する頂点を、
PrimitiveCount には使用するプリミティブの数を指定します
成功した場合は D3D_OK を、そうでなければ D3DERR_INVALIDCALL を返します
#include <windows.h>
#include <d3d8.h>
#include <d3dx8.h>

#define TITLE TEXT("Kitty on your lap")
#define D3DFVF D3DFVF_XYZRHW | D3DFVF_DIFFUSE

IDirect3D8 * pDirect3D;
IDirect3DDevice8 * pD3Device;
D3DPRESENT_PARAMETERS d3dpp;

typedef struct {
	float x , y , z , w;
	DWORD color;
} D3DVERTEX;

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	static D3DVERTEX pt[3] = {
		{200 , 10 , 0 , 1 , 0xFFFF0000} ,
		{400 , 200 , 0 , 1 , 0xFF00FF00} ,
		{10 , 200 , 0 , 1 , 0xFF0000FF}
	};
	D3DXMATRIX d3dm;
	D3DVERTEX *d3dv;
	static IDirect3DVertexBuffer8 *d3dvb;

	switch(msg){
		case WM_DESTROY:
			PostQuitMessage(0);
			break;
		case WM_CREATE:
			pDirect3D->CreateDevice(
				D3DADAPTER_DEFAULT , D3DDEVTYPE_HAL , hWnd ,
				D3DCREATE_SOFTWARE_VERTEXPROCESSING , &d3dpp , &pD3Device
			);

			pD3Device->CreateVertexBuffer(
				sizeof (D3DVERTEX) * 3 , 0 , D3DFVF , D3DPOOL_MANAGED , &d3dvb);
			d3dvb->Lock(0 , 0 , (BYTE**)&d3dv , 0);
			memcpy(d3dv , pt , sizeof (D3DVERTEX) * 3);
			d3dvb->Unlock();
			return 0;
		case WM_PAINT:
			pD3Device->Clear(0 , NULL , D3DCLEAR_TARGET ,
				D3DCOLOR_XRGB(0xFF , 0xFF , 0xFF) , 1.0 , 0);
			pD3Device->BeginScene();
			pD3Device->SetVertexShader(D3DFVF);
			pD3Device->SetStreamSource(0 , d3dvb , sizeof (D3DVERTEX));
			pD3Device->DrawPrimitive(D3DPT_TRIANGLESTRIP , 0 , 1);
			pD3Device->EndScene();

			pD3Device->Present(NULL,NULL,NULL,NULL);
			ValidateRect(hWnd,NULL);
			break;
		case WM_SIZE:
			if(pD3Device && (wp == SIZE_RESTORED || wp == SIZE_MAXIMIZED)) {
				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 ,
				   LPSTR lpCmdLine,int nCmdShow) {
	MSG msg;
	HWND hWnd;
	WNDCLASS winc;
	D3DDISPLAYMODE dmode;

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

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

	winc.style			= CS_CLASSDC;
	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 ,
					(HWND)NULL,(HMENU)NULL , hInstance , (LPSTR)NULL);

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

	pDirect3D->Release();
	pD3Device->Release();
	return msg.wParam;
}
このプログラムの実行結果は、見た目はこれまでと同じ三角形です
しかし、三角形の頂点を扱っているのはアプリケーションではなく頂点バッファです
そのため、これまでのものよりも幾分の高速化、最適化が期待できます
基本的には、頂点データは頂点バッファに保存するべきでしょう


IDirect3DDevice8::CreateVertexBuffer()

HRESULT CreateVertexBuffer(
	UINT Length , DWORD Usage ,
	DWORD FVF , D3DPOOL Pool ,
	IDirect3DVertexBuffer8** ppVertexBuffer
);
頂点バッファを作成します

Length - 頂点バッファのサイズをバイト単位で指定します
Usage - 必要の応じてリソースの制御方法を表すフラグを指定します
FVF - 用いられる頂点フォーマットのフラグの組み合わせを指定します
Pool - D3DPOOL 列挙型のメンバで、どのメモリを使用するかを指定できます
ppVertexBuffer - 作成した頂点バッファを受けるポインタのポインタを指定します

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

Usage には以下のフラグの組み合わせを指定することができます

定数解説
D3DUSAGE_DONOTCLIP 頂点バッファの内容が決してクリッピングを要求しないことを示す
このフラグが設定されたバッファを使ってレンダリングする場合は
D3DRS_CLIPPING レンダリング ステートが FALSE に設定されていなければならない
D3DUSAGE_DYNAMIC 頂点またはインデックス バッファが動的なメモリの使用を必要とすることを示す
これを使用すると、ドライバが配置場所を決定できるので、ドライバにとって有効である
一般的に、静的な頂点バッファはビデオ メモリに配置され
動的な頂点バッファは AGP メモリに配置される
静的な使用方法だけを分離できないことに注意すること
D3DUSAGE_DYNAMIC を指定しないと、頂点バッファは静的に作成される
D3DUSAGE_DYNAMIC は、D3DLOCK_DISCARD
および D3DLOCK_NOOVERWRITE ロッキング フラグによって厳密に強制される
この結果、D3DLOCK_DISCARD および D3DLOCK_NOOVERWRITE は
D3DUSAGE_DYNAMIC を指定して作成された頂点
およびインデックス バッファでのみ有効である
これらは、静的な頂点バッファでは有効なフラグではない

D3DUSAGE_DYNAMIC は、管理下の頂点
およびインデックス バッファでは指定できないことに注意すること
D3DUSAGE_RTPATCHES 高次プリミティブの描画に頂点バッファを使用するとき設定する
D3DUSAGE_NPATCHES N パッチの描画に頂点バッファを使用するとき設定する
D3DUSAGE_POINTS ポイント スプライトまたはインデックス付きポイント リストの描画に
頂点バッファを使用するとき設定する
D3DUSAGE_SOFTWAREPROCESSING ソフトウェア頂点処理で使用する頂点バッファであることを示す
D3DUSAGE_WRITEONLY 頂点バッファへの書き込み操作しか実行しないことをシステムに知らせる
このフラグを使用することにより、ドライバは最適なメモリ領域を選択して
効率的に書き込み操作やレンダリングを実行する
この能力を使用して作成された頂点バッファからの読み出しは失敗する

メソッドが失敗すると、以下のエラー値が返る可能性があります

定数解説
D3DERR_INVALIDCALL メソッドの呼び出しが無効である
D3DERR_OUTOFVIDEOMEMORY 十分なディスプレイメモリが存在しない
E_OUTOFMEMORY 十分なメモリを割り当てることができない

IDirect3DVertexBuffer8::Lock()

HRESULT Lock(
	UINT OffsetToLock , UINT SizeToLock ,
	BYTE** ppbData , DWORD Flags
);
頂点データの指定範囲をロックし、バッファへのポインタを取得します

OffsetToLock - ロックする頂点データのオフセットをバイト単位で指定します
SizeToLock - ロックする範囲をバイト単位で指定します
ppbData - 取得する頂点データへのポインタのポインタを指定します
Flags - ロックの方法を表すフラグを指定します

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

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

定数解説
D3DLOCK_DISCARD アプリケーションは、書き込み専用処理でインデックス バッファ全体を上書きする
これにより、Direct3D は新しいメモリ領域へのポインタを返すことができるので
ダイナミック メモリ アクセス (DMA) と
古い領域からのレンダリングが機能停止することはない
D3DLOCK_NOOVERWRITE フレームの開始以降、またはこのフラグが指定されていない最後のロック以降に
描画処理の呼び出しの中で参照された頂点は、ロック中に変更されないことを示す
頂点バッファにのみデータを追加する場合は、このフラグにより処理を最適化できる
D3DLOCK_NOSYSLOCK ビデオ メモリ ロックのデフォルトの動作は
システムのクリティカル セクションを確保することで
ロック中にディスプレイ モードの変更が行われないことを保証する
このフラグは、システムワイドなクリティカル セクションがロックの間保持されないようにする
ロック処理は若干負荷が高くなるが、マウス カーソルの移動など
システムでほかの処理を実行することが可能になる
このフラグは、ソフトウェア レンダリングのバックバッファのロックのように
ロックが長時間に及び、システムの応答性に
悪影響を与えてしまうようなロックに対して有効である
D3DLOCK_READONLY アプリケーションはバッファに書き込みを行わない
これにより、いくらかの最適化が実現する
D3DLOCK_READONLY は D3DLOCK_DISCARD と共に指定することはできない
また、D3DUSAGE_WRITEONLY で作成された頂点バッファに指定することもできない

IDirect3DVertexBuffer8::Unlock()

HRESULT Unlock();

ロックした頂点データ範囲をアンロックします

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

IDirect3DDevice8::SetStreamSource()

HRESULT SetStreamSource(
	UINT StreamNumber ,
	IDirect3DVertexBuffer8* pStreamData , UINT Stride
);
頂点バッファをデバイスに設定します

StreamNumber - 頂点バッファを設定するストリーム番号を指定します
pStreamData - 頂点バッファへのポインタを指定します
Stride - 頂点データの一つ分のデータサイズをバイト単位で指定します

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

IDirect3DDevice8::DrawPrimitive()

HRESULT DrawPrimitive(
	D3DPRIMITIVETYPE PrimitiveType ,
	UINT StartVertex , UINT PrimitiveCount
);
現在のデータ入力ストリーム セットから、指定されたタイプの
非インデックス付きジオメトリ プリミティブのシーケンスをレンダリングします

PrimitiveType - プリミティブを指定します
StartVertex - 描画を開始する頂点を指定します
PrimitiveCount - 使用するプリミティブの数を指定します

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



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