在DirectX程序中我们可以自定义多个不同数据格式的顶点缓冲区,本例我们定义了三个顶点缓冲区(在DX9中应该最大可以支持16个)存放顶点位置,颜色和纹理的坐标。通过鼠标拖曳旋转物体,按键1和2控制纹理的采样方式最近点采样(最近点采样和线性纹理过滤)

我们先看以下代码:

//********************************************************************
//      Filename: Main.cpp
//        Author: Chinafish
//      Modifier: Chinafish
//       Created: 2011-05-11 16:52
//       Updated: 2011-05-11 16:52
//            QQ: 149200849
//           MSN: china_fish@msn.com
//       Purpose: Multiple Vertex buffer.
//====================================================================
//  Copyright(C) 2004-2011 by Chinafish. All Rights Reserved.
//********************************************************************

#define STRICT
#define WIN32_LEAN_AND_MEAN
#include "windows.h"

// Link D3D 
#include "d3d9.h"
#include "d3dx9.h"
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
 
#define Release_Safe(p){if(p!=NULL) p->Release();}

#define WINDOW_TITLE	"www.csinx.org - Multiple Vertex Buffer (DX9+VC9)"
#define WINDOW_WIDTH	800
#define WINDOW_HEIGHT	600

//-----------------------------------------------------------------------------
// GLOBALS 
//-----------------------------------------------------------------------------
HWND							g_hWnd			= NULL;
LPDIRECT3D9						g_pD3D			= NULL;
LPDIRECT3DDEVICE9				g_pd3dDevice	= NULL;
LPDIRECT3DTEXTURE9				g_pTexture		= NULL;
D3DPRESENT_PARAMETERS			g_d3dpp;
LPDIRECT3DVERTEXBUFFER9			g_pVertexBuffer		= NULL;
LPDIRECT3DVERTEXBUFFER9			g_pColorBuffer		= NULL;
LPDIRECT3DVERTEXBUFFER9			g_pTexCoordBuffer	= NULL;
LPDIRECT3DVERTEXDECLARATION9	g_pVertexDeclaration;

float g_fSpinX		= 0.0f;
float g_fSpinY		= 0.0f;
int g_nNumVertex	= 0;

// 图片数据
BYTE g_TexData[] = {
	0x42,	0x4D,	0x46,	0x00,	0x00,	0x00,	0x00,
	0x00,	0x00,	0x00,	0x36,	0x00,	0x00,	0x00,
	0x28,	0x00,	0x00,	0x00,	0x02,	0x00,	0x00,
	0x00,	0x02,	0x00,	0x00,	0x00,	0x01,	0x00,
	0x18,	0x00,	0x00,	0x00,	0x00,	0x00,	0x10,
	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
	0x00,	0xFF,	0xFF,	0xFF,	0x00,	0x00,	0xFF,
	0xFF,	0xFF,	0x00,	0x00,	0x00,	0x00,	0x0
};

struct Vertex
{
	float x, y, z;
};

// 顶点位置
Vertex g_VertexData[] =
{
	{-1.0f, 1.0f,-1.0f },
	{ 1.0f, 1.0f,-1.0f },
	{-1.0f,-1.0f,-1.0f },
	{ 1.0f,-1.0f,-1.0f },

	{-1.0f, 1.0f, 1.0f },
	{-1.0f,-1.0f, 1.0f },
	{ 1.0f, 1.0f, 1.0f },
	{ 1.0f,-1.0f, 1.0f },

	{-1.0f, 1.0f, 1.0f },
	{ 1.0f, 1.0f, 1.0f },
	{-1.0f, 1.0f,-1.0f },
	{ 1.0f, 1.0f,-1.0f },

	{-1.0f,-1.0f, 1.0f },
	{-1.0f,-1.0f,-1.0f },
	{ 1.0f,-1.0f, 1.0f },
	{ 1.0f,-1.0f,-1.0f },

	{ 1.0f, 1.0f,-1.0f },
	{ 1.0f, 1.0f, 1.0f },
	{ 1.0f,-1.0f,-1.0f },
	{ 1.0f,-1.0f, 1.0f },

	{-1.0f, 1.0f,-1.0f },
	{-1.0f,-1.0f,-1.0f },
	{-1.0f, 1.0f, 1.0f },
	{-1.0f,-1.0f, 1.0f }
};

struct Color
{
	DWORD color;
};

// 顶点颜色
Color g_VertexColor[] =
{
	{ D3DCOLOR_COLORVALUE( 1.0, 0.0, 0.0, 1.0 ) },
	{ D3DCOLOR_COLORVALUE( 1.0, 0.0, 0.0, 1.0 ) },
	{ D3DCOLOR_COLORVALUE( 1.0, 0.0, 0.0, 1.0 ) },
	{ D3DCOLOR_COLORVALUE( 1.0, 0.0, 0.0, 1.0 ) },

	{ D3DCOLOR_COLORVALUE( 0.0, 1.0, 0.0, 1.0 ) },
	{ D3DCOLOR_COLORVALUE( 0.0, 1.0, 0.0, 1.0 ) },
	{ D3DCOLOR_COLORVALUE( 0.0, 1.0, 0.0, 1.0 ) },
	{ D3DCOLOR_COLORVALUE( 0.0, 1.0, 0.0, 1.0 ) },

	{ D3DCOLOR_COLORVALUE( 0.0, 0.0, 1.0, 1.0 ) },
	{ D3DCOLOR_COLORVALUE( 0.0, 0.0, 1.0, 1.0 ) },
	{ D3DCOLOR_COLORVALUE( 0.0, 0.0, 1.0, 1.0 ) },
	{ D3DCOLOR_COLORVALUE( 0.0, 0.0, 1.0, 1.0 ) },

	{ D3DCOLOR_COLORVALUE( 1.0, 1.0, 0.0, 1.0 ) },
	{ D3DCOLOR_COLORVALUE( 1.0, 1.0, 0.0, 1.0 ) },
	{ D3DCOLOR_COLORVALUE( 1.0, 1.0, 0.0, 1.0 ) },
	{ D3DCOLOR_COLORVALUE( 1.0, 1.0, 0.0, 1.0 ) },

	{ D3DCOLOR_COLORVALUE( 1.0, 0.0, 1.0, 1.0 ) },
	{ D3DCOLOR_COLORVALUE( 1.0, 0.0, 1.0, 1.0 ) },
	{ D3DCOLOR_COLORVALUE( 1.0, 0.0, 1.0, 1.0 ) },
	{ D3DCOLOR_COLORVALUE( 1.0, 0.0, 1.0, 1.0 ) },

	{ D3DCOLOR_COLORVALUE( 0.0, 1.0, 1.0, 1.0 ) },
	{ D3DCOLOR_COLORVALUE( 0.0, 1.0, 1.0, 1.0 ) },
	{ D3DCOLOR_COLORVALUE( 0.0, 1.0, 1.0, 1.0 ) },
	{ D3DCOLOR_COLORVALUE( 0.0, 1.0, 1.0, 1.0 ) }
};

struct TexCoord
{
	float tu, tv;
};

// 纹理坐标
TexCoord g_TexCoord[] =
{
	{ 0.0f, 0.0f },
	{ 1.0f, 0.0f },
	{ 0.0f, 1.0f },
	{ 1.0f, 1.0f },

	{ 1.0f, 0.0f },
	{ 1.0f, 1.0f },
	{ 0.0f, 0.0f },
	{ 0.0f, 1.0f },

	{ 0.0f, 0.0f },
	{ 1.0f, 0.0f },
	{ 0.0f, 1.0f },
	{ 1.0f, 1.0f },

	{ 0.0f, 0.0f },
	{ 1.0f, 0.0f },
	{ 0.0f, 1.0f },
	{ 1.0f, 1.0f },

	{ 0.0f, 0.0f },
	{ 1.0f, 0.0f },
	{ 0.0f, 1.0f },
	{ 1.0f, 1.0f },

	{ 1.0f, 0.0f },
	{ 1.0f, 1.0f },
	{ 0.0f, 0.0f },
	{ 0.0f, 1.0f }
};

//-----------------------------------------------------------------------------
// PROTOTYPES 
//-----------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void InitD3D();
void ShutDown(void);
void Render(void);

//-----------------------------------------------------------------------------
// Name: WinMain()
// Desc: The application's entry point
//-----------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{ 
	WNDCLASSEX winClass;
	MSG        uMsg;
	memset(&uMsg,0,sizeof(uMsg));

	winClass.lpszClassName = "CLASS_MULTIVERTEXBUFFER";
	winClass.cbSize        = sizeof(WNDCLASSEX);
	winClass.style         = CS_HREDRAW | CS_VREDRAW;
	winClass.lpfnWndProc   = WindowProc;
	winClass.hInstance     = hInstance;
	winClass.hIcon         = LoadIcon(hInstance, (LPCTSTR)NULL);
	winClass.hIconSm       = LoadIcon(hInstance, (LPCTSTR)NULL);
	winClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
	winClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	winClass.lpszMenuName  = NULL;
	winClass.cbClsExtra    = 0;
	winClass.cbWndExtra    = 0;

	if(!RegisterClassEx(&winClass))
		return E_FAIL; 

	g_hWnd = CreateWindowEx(NULL, "CLASS_MULTIVERTEXBUFFER",
		WINDOW_TITLE,
		WS_EX_TOPMOST, 0, 0,
		WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL,
		hInstance, NULL);
	
	if(g_hWnd == NULL)
		return E_FAIL;
	
	// Adjust window 
	RECT rect = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT };
	AdjustWindowRect(&rect, GetWindowLong(g_hWnd, GWL_STYLE), FALSE);
	SetWindowPos(g_hWnd, 0, 0, 0, rect.right - rect.left, rect.bottom - rect.top,
		SWP_NOZORDER | SWP_NOMOVE);

	ShowWindow(g_hWnd, nCmdShow);
	UpdateWindow(g_hWnd);
	
	// Init or reset D3D
	InitD3D();

	while(uMsg.message != WM_QUIT)
	{
		if(PeekMessage(&uMsg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&uMsg);
			DispatchMessage(&uMsg);
		}
		else
		{
			// Render a frame
			Render();
		}
	}
	
	ShutDown();
	
	UnregisterClass("CLASS_MULTIVERTEXBUFFER", winClass.hInstance);
	
	return uMsg.wParam;
}

//-----------------------------------------------------------------------------
// Name: WindowProc()
// Desc: The window's message handler
//-----------------------------------------------------------------------------
LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	static POINT ptLastMousePos;
	static POINT ptCurrentMousePos;
	static bool bDraging;

	switch(msg)
	{
	case WM_KEYDOWN:
		{
			switch(wParam)
			{
			case VK_ESCAPE:
				PostQuitMessage(0);
				break;
			case 0x31: // 1
				{
					// 最近点采样
					g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_POINT );
					g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT );
				}
				break;
			case 0x32: // 2
				{
					// 线性纹理过滤
					g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
					g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
				}
				break;
			}
		}
		break;
	case WM_LBUTTONDOWN:
		{
			ptLastMousePos.x = ptCurrentMousePos.x = LOWORD (lParam);
			ptLastMousePos.y = ptCurrentMousePos.y = HIWORD (lParam);
			bDraging = true;
		}
		break;
	case WM_LBUTTONUP:
		{
			bDraging = false;
		}
		break;
	case WM_MOUSEMOVE:
		{
			ptCurrentMousePos.x = LOWORD (lParam);
			ptCurrentMousePos.y = HIWORD (lParam);

			if( bDraging )
			{
				g_fSpinX -= (ptCurrentMousePos.x - ptLastMousePos.x);
				g_fSpinY -= (ptCurrentMousePos.y - ptLastMousePos.y);
			}

			ptLastMousePos.x = ptCurrentMousePos.x;
			ptLastMousePos.y = ptCurrentMousePos.y;
		}
		break;
	case WM_CLOSE:
		{
			PostQuitMessage(0);
		}
	case WM_DESTROY:
		{
			PostQuitMessage(0);
		}
		break;
	default:
		{
			return DefWindowProc(hWnd, msg, wParam, lParam);
		}
		break;
	}
	
	return 0;
}

//-----------------------------------------------------------------------------
// Name: InitD3D()
// Desc: This function will only be called once during the application's
//       initialization phase. Therefore, it can't contain any resources that
//       need to be restored every time the Direct3D device is lost or the
//       window is resized.
//-----------------------------------------------------------------------------
void InitD3D()
{
	// Create D3D
	g_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
	if(!g_pD3D)
	{
		MessageBox(NULL, "Direct3DCreate9() - Failed!", "Error", NULL);
		return;
	}
	
	D3DDISPLAYMODE d3ddm;
	g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);
	
	ZeroMemory(&g_d3dpp, sizeof(g_d3dpp));
	g_d3dpp.Windowed				= TRUE;
	g_d3dpp.SwapEffect				= D3DSWAPEFFECT_DISCARD;
	g_d3dpp.BackBufferFormat		= d3ddm.Format;
	g_d3dpp.EnableAutoDepthStencil	= TRUE;
	g_d3dpp.AutoDepthStencilFormat	= D3DFMT_D16;
	g_d3dpp.PresentationInterval	= D3DPRESENT_INTERVAL_IMMEDIATE;
	
	g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
		g_hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING,
		&g_d3dpp, &g_pd3dDevice);
	
	if(!g_pd3dDevice)
	{
		return;
	}

	g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
	g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE );

	D3DXMATRIX matProj;
	D3DXMatrixPerspectiveFovLH( &matProj, D3DXToRadian( 45.0f ), 
		640.0f / 480.0f, 0.1f, 100.0f );

	g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );

	// Create Texture
	D3DXCreateTextureFromFileInMemory( g_pd3dDevice, &g_TexData, sizeof(g_TexData)/sizeof(BYTE), &g_pTexture);
	if(!g_pTexture)
	{
		MessageBox(NULL, "D3DXCreateTextureFromFileInMemory() - Failed!", "Error", NULL);
		return;
	}

	g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
	g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );

	// Create the vertex buffer. Here we are allocating enough memory
	// (from the default pool) to hold all our 3 custom vertices. We also
	// specify the FVF, so the vertex buffer knows what data it contains.
	g_nNumVertex = sizeof(g_VertexData)/sizeof(Vertex);
	g_pd3dDevice->CreateVertexBuffer( g_nNumVertex*sizeof(Vertex), 0, 0,
		D3DPOOL_DEFAULT, &g_pVertexBuffer,
		NULL );

	// Now we fill the vertex buffer. To do this, we need to Lock() the VB to
	// gain access to the vertices. This mechanism is required becuase vertex
	// buffers may be in device memory.
	Vertex *pVertices = NULL;
	g_pVertexBuffer->Lock( 0, sizeof(g_VertexData), (void**)&pVertices, 0 );
	memcpy( pVertices, g_VertexData, sizeof(g_VertexData) );
	g_pVertexBuffer->Unlock();

	// Create a vertex buffer that contains only the color data
	g_nNumVertex = sizeof(g_VertexColor)/sizeof(Color);
	g_pd3dDevice->CreateVertexBuffer( g_nNumVertex*sizeof(Color), 0, 0,
		D3DPOOL_DEFAULT, &g_pColorBuffer,
		NULL );

	void *pColors = NULL;
	g_pColorBuffer->Lock( 0, sizeof(g_VertexColor), (void**)&pColors, 0 );
	memcpy( pColors, g_VertexColor, sizeof(g_VertexColor) );
	g_pColorBuffer->Unlock();

	// Create a vertex buffer that contains only the texture coordinate data
	g_nNumVertex = sizeof(g_TexCoord)/sizeof(TexCoord);
	g_pd3dDevice->CreateVertexBuffer( g_nNumVertex*sizeof(TexCoord), 0, 0,
		D3DPOOL_DEFAULT, &g_pTexCoordBuffer,
		NULL );

	void *pTexCoords = NULL;
	g_pTexCoordBuffer->Lock( 0, sizeof(g_TexCoord), (void**)&pTexCoords, 0 );
	memcpy( pTexCoords, g_TexCoord, sizeof(g_TexCoord) );
	g_pTexCoordBuffer->Unlock();

	// Create a vertex declaration so we can describe to Direct3D how we'll be passing our data to it.
	D3DVERTEXELEMENT9 dwDecl[] = 
	{
		//  Stream  Offset         Type                   Method                 Usage          UsageIndex
		{     0,      0,    D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION,     0      },
		{     1,      0,    D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR,        0      },
		{     2,      0,    D3DDECLTYPE_FLOAT2,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD,     0      },
		D3DDECL_END()
	};

	// 创建顶点着色声明
	g_pd3dDevice->CreateVertexDeclaration( dwDecl, &g_pVertexDeclaration );
}

//-----------------------------------------------------------------------------
// Name: ShutDown()
// Desc: Release all
//-----------------------------------------------------------------------------
void ShutDown(void)
{
	Release_Safe(g_pVertexBuffer);
	Release_Safe(g_pColorBuffer);
	Release_Safe(g_pTexCoordBuffer);
	Release_Safe(g_pTexture);
	Release_Safe(g_pd3dDevice);
	Release_Safe(g_pD3D);
}

//-----------------------------------------------------------------------------
// Name: Render()
// Desc: Render something you want
//-----------------------------------------------------------------------------
void Render(void)
{
	// Clear screen with D3DCOLOR_XRGB(80, 0, 80)
	g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(80, 0, 80), 1.0f, 0);
	
	D3DXMATRIX matTrans;
	D3DXMATRIX matRot;
	D3DXMATRIX matWorld;

	D3DXMatrixTranslation( &matTrans, 0.0f, 0.0f, 5.0f );

	D3DXMatrixRotationYawPitchRoll( &matRot,
		D3DXToRadian(g_fSpinX),
		D3DXToRadian(g_fSpinY),
		0.0f );

	matWorld = matRot * matTrans;
	g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );

	g_pd3dDevice->BeginScene();
	
	// Render geometry here...
	g_pd3dDevice->SetTexture( 0, g_pTexture );
	g_pd3dDevice->SetVertexDeclaration( g_pVertexDeclaration );
	g_pd3dDevice->SetStreamSource( 0, g_pVertexBuffer,   0, sizeof(Vertex) );
	g_pd3dDevice->SetStreamSource( 1, g_pColorBuffer,    0, sizeof(Color) );
	g_pd3dDevice->SetStreamSource( 2, g_pTexCoordBuffer, 0, sizeof(TexCoord) );

	g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP,  0, 2 );
	g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP,  4, 2 );
	g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP,  8, 2 );
	g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 12, 2 );
	g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 16, 2 );
	g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 20, 2 );

	g_pd3dDevice->EndScene();
	g_pd3dDevice->Present(NULL, NULL, NULL, NULL);
}
				

这里需要说明的是首先定义了图片数据以及顶点的位置数据,颜色数据和纹理坐标数据。 再按以前的步骤创建DX应用程序,在创建设备后通过调用 D3DXCreateTextureFromFileInMemory 方法从内存图片数据创建纹理,通过调用 IDirect3DDevice9::CreateVertexBuffer 方法创建多个我们需要格式的顶点缓冲区,通过调用 IDirect3DDevice9::CreateVertexDeclaration 方法创建顶点着色声明。在渲染时调用 IDirect3DDevice9::SetTexture 设定纹理状态,调用 IDirect3DDevice9::SetVertexDeclaration 设定顶点数据流声明,再通过多次调用 IDirect3DDevice9::SetStreamSource 方法将不同的顶点缓冲区绑定到多个设备数据流上,最后通过调用 IDirect3DDevice9::DrawPrimitive 方法按照当前指定的顶点格式渲染无索引的顶点序列,最后显示出来。

下图为本例代码执行效果:

[返回目录][我有话说]