Direct3D学习手记十:网格一【手动创建网格】

本文介绍网格技术并手动创建一个立方体网格

网格模型:

网格模型是一种将物体的顶点数据、材质信息、纹理信息存储在一个外部文件中的3D物体模型。

网格接口:

网格接口有ID3DXMeshID3DPMesh(渐进网格),这两个接口都继承自ID3DBaseMesh接口

创建空白网格:

HRESULT D3DXCreateMeshFVF(  DWORD NumFaces,  DWORD NumVertices,  DWORD Options,
  DWORD FVF,  LPDIRECT3DDEVICE9 pD3DDevice,  LPD3DXMESH * ppMesh);
NumFaces 为网格的三角形面片数,此次我们绘制一个立方体,就有12个三角形面片

NumVertices为网格的顶点数,此次为24个

Options为创建标志,包括D3DXMESH_32BIT表示使用32位的索引,还有D3DXMESH_MANAGED等等

FVF为顶点格式,此次我们只使用了D3DFVF_XYZD3DFVF_TEX1

pD3DDevice为设备指针

ppMesh用于存储创建的空白网格

例如:

	hr=D3DXCreateMeshFVF(12,//三角形图元个数
						24,//顶点个数,这里因为使用了纹理坐标,所以顶点个数不是8而是24
						D3DXMESH_MANAGED,//创建标记
						VERTEX::FVF,//灵活顶点格式
						g_pd3dDevice,//设备指针
						&g_pBoxMesh);//存储网格对象的指针

网格的几何信息:

网格的几何信息包括顶点数据和索引数据,网格创建完成后

就可以通过以下函数分别得到网格的顶点缓存和索引缓存:

HRESULT GetVertexBuffer(  LPDIRECT3DVERTEXBUFFER9 * ppVB);
HRESULT GetIndexBuffer(  LPDIRECT3DINDEXBUFFER9 * ppIB);

得到顶点缓存和索引缓存指针后,就可以直接读写其中的数据,可以使用如下函数对其加、解锁:

HRESULT LockVertexBuffer(  DWORD Flags,  LPVOID * ppData);
HRESULT LockIndexBuffer(  DWORD Flags,  LPVOID * ppData);
HRESULT UnlockVertexBuffer();
HRESULT UnlockIndexBuffer();
在加锁和解锁之间就可以直接向缓存中写入顶点数据和索引数据,例如:

写顶点数据:

//Step 2:填充顶点数据
	VERTEX * pVertexBuf=NULL;
	if(SUCCEEDED(g_pBoxMesh->LockVertexBuffer(0,(LPVOID *)&pVertexBuf)))
	{
		//上面
		pVertexBuf[0]=VERTEX(-1.0f,1.0f,1.0f,0.0f,0.0f);
		pVertexBuf[1]=VERTEX(1.0f,1.0f,1.0f,1.0f,0.0f);
		pVertexBuf[2]=VERTEX(1.0f,1.0f,-1.0f,1.0f,1.0f);
		pVertexBuf[3]=VERTEX(-1.0f,1.0f,-1.0f,0.0f,1.0f);

		//下面
		pVertexBuf[4]=VERTEX(-1.0f,-1.0f,-1.0f,0.0f,0.0f);
		pVertexBuf[5]=VERTEX(1.0f,-1.0f,-1.0f,1.0f,0.0f);
		pVertexBuf[6]=VERTEX(1.0f,-1.0f,1.0f,1.0f,1.0f);		
		pVertexBuf[7]=VERTEX(-1.0f,-1.0f,1.0f,0.0f,1.0f);

		//左面
		pVertexBuf[8]=VERTEX(-1.0f,1.0f,1.0f,0.0f,0.0f);
		pVertexBuf[9]=VERTEX(-1.0f,1.0f,-1.0f,1.0f,0.0f);
		pVertexBuf[10]=VERTEX(-1.0f,-1.0f,-1.0f,1.0f,1.0f);
		pVertexBuf[11]=VERTEX(-1.0f,-1.0f,1.0f,0.0f,1.0f);

		//右面
		pVertexBuf[12]=VERTEX(1.0f,1.0f,-1.0f,0.0f,0.0f);
		pVertexBuf[13]=VERTEX(1.0f,1.0f,1.0f,1.0f,0.0f);
		pVertexBuf[14]=VERTEX(1.0f,-1.0f,1.0f,1.0f,1.0f);
		pVertexBuf[15]=VERTEX(1.0f,-1.0f,-1.0f,0.0f,1.0f);

		//前面
		pVertexBuf[16]=VERTEX(-1.0f,1.0f,-1.0f,0.0f,0.0f);
		pVertexBuf[17]=VERTEX(1.0f,1.0f,-1.0f,1.0f,0.0f);
		pVertexBuf[18]=VERTEX(1.0f,-1.0f,-1.0f,1.0f,1.0f);
		pVertexBuf[19]=VERTEX(-1.0f,-1.0f,-1.0f,0.0f,1.0f);

		//后面
		pVertexBuf[20]=VERTEX(1.0f,1.0f,1.0f,0.0f,0.0f);
		pVertexBuf[21]=VERTEX(-1.0f,1.0f,1.0f,1.0f,0.0f);
		pVertexBuf[22]=VERTEX(-1.0f,-1.0f,1.0f,1.0f,1.0f);
		pVertexBuf[23]=VERTEX(1.0f,-1.0f,1.0f,0.0f,1.0f);

		g_pBoxMesh->UnlockVertexBuffer();
	}

写索引数据:

	//Step 3:设置顶点索引
	WORD * pIndexBuf=NULL;
	if(SUCCEEDED(g_pBoxMesh->LockIndexBuffer(0,(LPVOID *)&pIndexBuf)))
	{
		//上面
		pIndexBuf[0]=0,pIndexBuf[1]=1,pIndexBuf[2]=2;
		pIndexBuf[3]=0,pIndexBuf[4]=2,pIndexBuf[5]=3;

		//下面
		pIndexBuf[6]=4,pIndexBuf[7]=5,pIndexBuf[8]=6;
		pIndexBuf[9]=4,pIndexBuf[10]=6,pIndexBuf[11]=7;

		//左面
		pIndexBuf[12]=8,pIndexBuf[13]=9,pIndexBuf[14]=10;
		pIndexBuf[15]=8,pIndexBuf[16]=10,pIndexBuf[17]=11;

		//右面
		pIndexBuf[18]=12,pIndexBuf[19]=13,pIndexBuf[20]=14;
		pIndexBuf[21]=12,pIndexBuf[22]=14,pIndexBuf[23]=15;

		//前面
		pIndexBuf[24]=16,pIndexBuf[25]=17,pIndexBuf[26]=18;
		pIndexBuf[27]=16,pIndexBuf[28]=18,pIndexBuf[29]=19;

		//后面
		pIndexBuf[30]=20,pIndexBuf[31]=21,pIndexBuf[32]=22;
		pIndexBuf[33]=20,pIndexBuf[34]=22,pIndexBuf[35]=23;

		g_pBoxMesh->UnlockIndexBuffer();
	}

属性和子集:

网格有很多子集组成,一个子集是相同材质、纹理的众多三角形图元的集合。

为了区分不同的子集,就为其指定所属子集的ID,即属性ID(DWORD类型),

存储属性ID的是属性缓存,其中保存了每个三角形图元的属性ID


和顶点缓存及索引缓存一样,也可以直接读写,

这里我们使立方体的一个面的两个三角形图元属于同一个子集,即属性ID相同,

例如,立方体的上面的两个三角形图元0和图元1,设置其属性ID为0,其他的类似,

程序中我使用如下方式为每个三角形图元赋予一个属性ID

	//Step 4:设置每个图元的属性,即所属于的子集,每个子集使用不同的材质
	//使每个面的2个三角形图元属于同一个子集,即属性ID相同
	DWORD * pAttrBuf=NULL;
	if(SUCCEEDED(g_pBoxMesh->LockAttributeBuffer(0,&pAttrBuf)))
	{
		for(int i=0,j=0;i<TEXTURE_NUM;i++)
		{
			pAttrBuf[j++]=i;
			pAttrBuf[j++]=i;
		}
		g_pBoxMesh->UnlockAttributeBuffer();
	}

邻接信息:

在网格中每个三角形都会与其他三角形相邻,所以使用DWORD类型的数组保存网格三角形的邻接信息,

可以使用如下方式得到:

	std::vector<DWORD> vAdjBuf(g_pBoxMesh->GetNumFaces()*3);//存储邻接图元信息,每个图元最多有3个邻接图元
	g_pBoxMesh->GenerateAdjacency(0.0f,&vAdjBuf[0]);//得到邻接信息

网格优化:

网格优化在使用网格时可有可无,但使用它可以提高绘制效率:

	g_pBoxMesh->OptimizeInplace(D3DXMESHOPT_COMPACT//优化标志,移除无用的顶点和索引
								|D3DXMESHOPT_ATTRSORT//根据属性ID对三角形图元进行排序
								|D3DXMESHOPT_VERTEXCACHE,//提高顶点高速缓存的命中率
								&vAdjBuf[0],//指向尚未优化的网格的邻接数组的指针
								NULL,//存储优化后的网格的邻接信息
								NULL,
								NULL);

加载纹理:

在使用网格时,可以为每个子集的图元设置纹理和材质(可以相同或不同),此次我们只使用了纹理,

	//加载纹理贴图
	WCHAR file[MAX_PATH]={0};
	for(int i=0;i<TEXTURE_NUM;i++)
	{
		swprintf(file,L"Demo_10_Media\\texture%d.bmp",i+1);
		D3DXCreateTextureFromFileW(g_pd3dDevice,file,&g_pFaceTextures[i]);
	}

绘制网格:

网格创建完成并且数据填写完成就可以绘制网格了,循环使用DrawSubnet绘制每个子集,并设置子集的纹理:

		//绘制立方体网格
		for(int i=0;i<TEXTURE_NUM;i++)
		{
			//设置纹理
			g_pd3dDevice->SetTexture(0,g_pFaceTextures[i]);		
			//绘制子集i
			g_pBoxMesh->DrawSubset(i);
		}

Setup函数:

在函数Setup函数里完成了网格的创建与顶点数据的填写

/****************************************************************
*函数名	:	Setup
*功能		:	创建与初始化资源、缓存、变换等
*输入		:	hWnd:窗口句柄
*输出		:	无
*返回值	:	成功:TRUE		失败:FALSE
****************************************************************/
BOOL			Setup(HWND hWnd)
{
	if(NULL==hWnd)
		return FALSE;

	//创建字体
	//方式一
	D3DXCreateFont(g_pd3dDevice,20,14,600,D3DX_DEFAULT,FALSE,DEFAULT_CHARSET,0,0,0,TEXT("微软雅黑"),&g_pHelpFont);

	//方式二
	D3DXFONT_DESC fd;
	ZeroMemory(&fd,sizeof(D3DXFONT_DESC));
	fd.Height=20;
	fd.Width=14;
	fd.Weight=600;
	fd.MipLevels=D3DX_DEFAULT;
	fd.Italic=TRUE;
	fd.CharSet=DEFAULT_CHARSET;
	fd.OutputPrecision=0;
	fd.PitchAndFamily=0;
	fd.Quality=0;
	_tcscpy_s(fd.FaceName,TEXT("Times New Roman"));
	D3DXCreateFontIndirect(g_pd3dDevice,&fd,&g_pTipFont);
	
	//加载纹理贴图
	WCHAR file[MAX_PATH]={0};
	for(int i=0;i<TEXTURE_NUM;i++)
	{
		swprintf(file,L"Demo_10_Media\\texture%d.bmp",i+1);
		D3DXCreateTextureFromFileW(g_pd3dDevice,file,&g_pFaceTextures[i]);
	}

	//手动创建立方体网格
	HRESULT hr=E_FAIL;
	//Step 1:创建网格
	hr=D3DXCreateMeshFVF(12,//三角形图元个数
						24,//顶点个数,这里因为使用了纹理坐标,所以顶点个数不是8而是24
						D3DXMESH_MANAGED,//创建标记
						VERTEX::FVF,//灵活顶点格式
						g_pd3dDevice,//设备指针
						&g_pBoxMesh);//存储网格对象的指针
	if(FAILED(hr))
		return FALSE;
	//Step 2:填充顶点数据
	VERTEX * pVertexBuf=NULL;
	if(SUCCEEDED(g_pBoxMesh->LockVertexBuffer(0,(LPVOID *)&pVertexBuf)))
	{
		//上面
		pVertexBuf[0]=VERTEX(-1.0f,1.0f,1.0f,0.0f,0.0f);
		pVertexBuf[1]=VERTEX(1.0f,1.0f,1.0f,1.0f,0.0f);
		pVertexBuf[2]=VERTEX(1.0f,1.0f,-1.0f,1.0f,1.0f);
		pVertexBuf[3]=VERTEX(-1.0f,1.0f,-1.0f,0.0f,1.0f);

		//下面
		pVertexBuf[4]=VERTEX(-1.0f,-1.0f,-1.0f,0.0f,0.0f);
		pVertexBuf[5]=VERTEX(1.0f,-1.0f,-1.0f,1.0f,0.0f);
		pVertexBuf[6]=VERTEX(1.0f,-1.0f,1.0f,1.0f,1.0f);		
		pVertexBuf[7]=VERTEX(-1.0f,-1.0f,1.0f,0.0f,1.0f);

		//左面
		pVertexBuf[8]=VERTEX(-1.0f,1.0f,1.0f,0.0f,0.0f);
		pVertexBuf[9]=VERTEX(-1.0f,1.0f,-1.0f,1.0f,0.0f);
		pVertexBuf[10]=VERTEX(-1.0f,-1.0f,-1.0f,1.0f,1.0f);
		pVertexBuf[11]=VERTEX(-1.0f,-1.0f,1.0f,0.0f,1.0f);

		//右面
		pVertexBuf[12]=VERTEX(1.0f,1.0f,-1.0f,0.0f,0.0f);
		pVertexBuf[13]=VERTEX(1.0f,1.0f,1.0f,1.0f,0.0f);
		pVertexBuf[14]=VERTEX(1.0f,-1.0f,1.0f,1.0f,1.0f);
		pVertexBuf[15]=VERTEX(1.0f,-1.0f,-1.0f,0.0f,1.0f);

		//前面
		pVertexBuf[16]=VERTEX(-1.0f,1.0f,-1.0f,0.0f,0.0f);
		pVertexBuf[17]=VERTEX(1.0f,1.0f,-1.0f,1.0f,0.0f);
		pVertexBuf[18]=VERTEX(1.0f,-1.0f,-1.0f,1.0f,1.0f);
		pVertexBuf[19]=VERTEX(-1.0f,-1.0f,-1.0f,0.0f,1.0f);

		//后面
		pVertexBuf[20]=VERTEX(1.0f,1.0f,1.0f,0.0f,0.0f);
		pVertexBuf[21]=VERTEX(-1.0f,1.0f,1.0f,1.0f,0.0f);
		pVertexBuf[22]=VERTEX(-1.0f,-1.0f,1.0f,1.0f,1.0f);
		pVertexBuf[23]=VERTEX(1.0f,-1.0f,1.0f,0.0f,1.0f);

		g_pBoxMesh->UnlockVertexBuffer();
	}

	//Step 3:设置顶点索引
	WORD * pIndexBuf=NULL;
	if(SUCCEEDED(g_pBoxMesh->LockIndexBuffer(0,(LPVOID *)&pIndexBuf)))
	{
		//上面
		pIndexBuf[0]=0,pIndexBuf[1]=1,pIndexBuf[2]=2;
		pIndexBuf[3]=0,pIndexBuf[4]=2,pIndexBuf[5]=3;

		//下面
		pIndexBuf[6]=4,pIndexBuf[7]=5,pIndexBuf[8]=6;
		pIndexBuf[9]=4,pIndexBuf[10]=6,pIndexBuf[11]=7;

		//左面
		pIndexBuf[12]=8,pIndexBuf[13]=9,pIndexBuf[14]=10;
		pIndexBuf[15]=8,pIndexBuf[16]=10,pIndexBuf[17]=11;

		//右面
		pIndexBuf[18]=12,pIndexBuf[19]=13,pIndexBuf[20]=14;
		pIndexBuf[21]=12,pIndexBuf[22]=14,pIndexBuf[23]=15;

		//前面
		pIndexBuf[24]=16,pIndexBuf[25]=17,pIndexBuf[26]=18;
		pIndexBuf[27]=16,pIndexBuf[28]=18,pIndexBuf[29]=19;

		//后面
		pIndexBuf[30]=20,pIndexBuf[31]=21,pIndexBuf[32]=22;
		pIndexBuf[33]=20,pIndexBuf[34]=22,pIndexBuf[35]=23;

		g_pBoxMesh->UnlockIndexBuffer();
	}

	//Step 4:设置每个图元的属性,即所属于的子集,每个子集使用不同的材质
	//使每个面的2个三角形图元属于同一个子集,即属性ID相同
	DWORD * pAttrBuf=NULL;
	if(SUCCEEDED(g_pBoxMesh->LockAttributeBuffer(0,&pAttrBuf)))
	{
		for(int i=0,j=0;i<TEXTURE_NUM;i++)
		{
			pAttrBuf[j++]=i;
			pAttrBuf[j++]=i;
		}
		g_pBoxMesh->UnlockAttributeBuffer();
	}
	
	//Step 5:对网格进行优化,可有可无
	std::vector<DWORD> vAdjBuf(g_pBoxMesh->GetNumFaces()*3);//存储邻接图元信息,每个图元最多有3个邻接图元
	g_pBoxMesh->GenerateAdjacency(0.0f,&vAdjBuf[0]);//得到邻接信息

	g_pBoxMesh->OptimizeInplace(D3DXMESHOPT_COMPACT//优化标志,移除无用的顶点和索引
								|D3DXMESHOPT_ATTRSORT//根据属性ID对三角形图元进行排序
								|D3DXMESHOPT_VERTEXCACHE,//提高顶点高速缓存的命中率
								&vAdjBuf[0],//指向尚未优化的网格的邻接数组的指针
								NULL,//存储优化后的网格的邻接信息
								NULL,
								NULL);
	//设置取景变换矩阵
	D3DXMATRIX matView;
	D3DXVECTOR3 vEye(0.0f,0.0f,-5.0f);
	D3DXVECTOR3 vAt(0.0f,0.0f,0.0f);
	D3DXVECTOR3 vUp(0.0f,1.0f,0.0f);
	D3DXMatrixLookAtLH(&matView,&vEye,&vAt,&vUp);
	g_pd3dDevice->SetTransform(D3DTS_VIEW,&matView);

	//设置投影变换矩阵
	D3DXMATRIX matProjection;
	::D3DXMatrixPerspectiveFovLH(&matProjection,D3DX_PI/4.0F,(FLOAT)g_nWidth/(FLOAT)g_nHeight,1.0F,1000.0F);
	g_pd3dDevice->SetTransform(D3DTS_PROJECTION,&matProjection);

	//关闭光照
	g_pd3dDevice->SetRenderState(D3DRS_LIGHTING,false);

	//显示窗口
	ShowWindow(hWnd,SW_SHOW);
	UpdateWindow(hWnd);
	return TRUE;
}

程序运行结果:



源代码与工程文件下载地址:

百度网盘

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值