您的位置:首页 > 其它

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

2013-12-30 11:23 253 查看

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

网格模型:

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

网格接口:

网格接口有ID3DXMesh和ID3DPMesh(渐进网格),这两个接口都继承自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_XYZ和D3DFVF_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;
}

程序运行结果:





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

百度网盘
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: