您的位置:首页 > 其它

D3D网格(二)

2015-07-05 10:56 295 查看
从网格XFile文件中导入网格数据,并渲染出来;实际中一般自定义自己的二进制格式文件,实现导出模块,和加载模板,包括了模型和动画。ID3DXBuffer是Void类型指针,声明时传入,使用时候需要强转为指定类型,在网格邻接信息(网格优化/拷贝/LOD网格等)和网格材质信息(读取材质信息)中使用最多。计算网格中的顶点法向量。ID3DXPMesh渐进式网格的创建和使用。用网格顶点创建网格包围体,AABB或球体;实际中很多是在3ds max中创建好包围盒导出,并跟随物体网格一起移动。网格对象作用:// .X模型文件,不支持顶点法向量,通过指定新的顶点格式:D3DFVF_XYZ | D3DFVF_UV | D3DFVF_NORMAL // 可以重新计算顶点缓存,生成新的网格对象,网格对象封装了顶点缓存,索引缓存,属性缓存;创建时候可以获取邻接信息, // 材质信息,网格优化时候,可以优化D3DXMESHOPT_ATTRSORT产生属性表提高渲染效率。网格对象可以优化和直接绘制, // 可以获取邻接信息关系,可以拷贝生成新的网格/可以生成渐进网格PMesh,可以计算顶点法向量,可以生成球形和BOX包围盒等。

1.从XFile文件中导入ID3DXMesh网格

函数为D3DXLoadMeshFromX(),导入后网格数据存放到网格对象中。材质和邻接信息存放在ID3DXBuffer缓存中,ID3DXBuffer是一个泛型的缓存,也就是只是一个字节数组,输出后需要强转需要的类型。这里的材质是指:typedef struct _D3DXMATERIAL{ D3DMATERIAL9 MatD3D; LPSTR pTextureFilename; // 纹理文件名} D3DXMATERIAL;// 真正光照中需要的材质typedef struct _D3DMATERIAL9 { D3DCOLORVALUE Diffuse; /* Diffuse color RGBA */ D3DCOLORVALUE Ambient; /* Ambient color RGB */ D3DCOLORVALUE Specular; /* Specular 'shininess' */ D3DCOLORVALUE Emissive; /* Emissive color RGB */ float Power; /* Sharpness if specular highlight */} D3DMATERIAL9;所以网格中的材质,或者单纯说3ds max中的材质,是指D3DXMATERIAL;材质包含了光照材质和纹理文件名,图形渲染模块中的材质一般是只取光照材质D3DMATERIAL9。而网格中的纹理一般是根据材质模块解析出的贴图文件名来创建纹理。D3DXLoadMeshFromX获得材质缓存后需要过滤下,得到图形渲染中需要的材质,和渲染中需要的纹理,然后将ID3DXBuffer缓存释放掉。实现代码:ID3DXMesh* Mesh = 0;std::vector<D3DMATERIAL9> Mtrls(0);std::vector<IDirect3DTexture9*> Textures(0);//// Framework functions//bool Setup(){ HRESULT hr = 0; // // Load the XFile data. // ID3DXBuffer* adjBuffer = 0; ID3DXBuffer* mtrlBuffer = 0; DWORD numMtrls = 0; hr = D3DXLoadMeshFromX( "bigship1.x", D3DXMESH_MANAGED, Device, &adjBuffer, &mtrlBuffer, 0, &numMtrls, &Mesh); if(FAILED(hr)) { ::MessageBox(0, "D3DXLoadMeshFromX() - FAILED", 0, 0); return false; } // // Extract the materials, and load textures. // if( mtrlBuffer != 0 && numMtrls != 0 ) { D3DXMATERIAL* mtrls = (D3DXMATERIAL*)mtrlBuffer->GetBufferPointer(); for(int i = 0; i < numMtrls; i++) { // the MatD3D property doesn't have an ambient value set // when its loaded, so set it now: mtrls[i].MatD3D.Ambient = mtrls[i].MatD3D.Diffuse; // save the ith material Mtrls.push_back( mtrls[i].MatD3D ); // check if the ith material has an associative texture if( mtrls[i].pTextureFilename != 0 ) { // yes, load the texture for the ith subset IDirect3DTexture9* tex = 0; D3DXCreateTextureFromFile( Device, mtrls[i].pTextureFilename, &tex); // save the loaded texture Textures.push_back( tex ); } else { // no texture for the ith subset Textures.push_back( 0 ); } } } d3d::Release<ID3DXBuffer*>(mtrlBuffer); // done w/ buffer // // Optimize the mesh. // hr = Mesh->OptimizeInplace( D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_COMPACT | // D3DXMESHOPT_COMPACT Reorders faces to remove unused vertices and faces. D3DXMESHOPT_VERTEXCACHE, (DWORD*)adjBuffer->GetBufferPointer(), // adjBuffer是ID3DXBuffer需要强转为DWORD*类型指针 0, 0, 0);// 输出的都不需要 d3d::Release<ID3DXBuffer*>(adjBuffer); // done w/ buffer if(FAILED(hr)) { ::MessageBox(0, "OptimizeInplace() - FAILED", 0, 0); return false; } // // Set texture filters. // Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); Device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT); // // Set Lights. // D3DXVECTOR3 dir(1.0f, -1.0f, 1.0f); D3DXCOLOR col(1.0f, 1.0f, 1.0f, 1.0f); D3DLIGHT9 light = d3d::InitDirectionalLight(&dir, &col); Device->SetLight(0, &light); Device->LightEnable(0, true); // 默认是开启光照的,这里就不需要再开启了 //Device->SetRenderState(D3DRS_LIGHTING, TRUE); Device->SetRenderState(D3DRS_NORMALIZENORMALS, true); Device->SetRenderState(D3DRS_SPECULARENABLE, true);}//渲染出来Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0); Device->BeginScene(); for(int i = 0; i < Mtrls.size(); i++) { Device->SetMaterial( &Mtrls[i] ); Device->SetTexture(0, Textures[i]); Mesh->DrawSubset(i); } Device->EndScene(); Device->Present(0, 0, 0, 0);

2.计算网格中的顶点法向量

ID3DXBMesh中的D3DXComputeNormals来生成顶点法向量,因为从XFile中创建的网格模型一般不会有D3DFVF_NORMAL,要先CloneMeshFVF指定D3DFVF_NORMAL,然后释放老的网格,将新的网格赋值给老的网格,再进行计算顶点法向量。
D3DXComputeNormals函数:
HRESULT D3DXComputeNormals(
  _Inout_       LPD3DXBASEMESH pMesh,
  _In_    const DWORD          *pAdjacency
);
Parameters
pMesh [in, out]
Type: LPD3DXBASEMESH
Pointer to an ID3DXBaseMesh interface, representing the normalized mesh object.
pAdjacency [in]
Type: const DWORD*
Pointer to an array of three DWORDs per face that specify the three neighbors for each face in the created progressive mesh. This parameter is optional and should be set to NULL if it is unused.
如果指定了邻接信息,因为邻接信息中有相同点的精度设置,所以会认为是点焊接了来计算顶点法向量的。
没有设置邻接信息则会根据每个顶点所有相连的三角形法向量平均来计算顶点法向量。
因为没有进行面拆分,所以不知道直接用D3DXComputeNormals计算出来的顶点法向量,来进行光照着色时候是否会有公告板问题。

3.LOD网格ID3DXPMesh

ID3DXPMesh同样继承自ID3DXBMesh,因此ID3DXMesh有的函数都有。
渐进式网格实现了三角网格的边坍塌减少面片和点拆分反演边坍塌过程增加面片。如果渐进式纹理,远处纹理小分辨率低,近处的纹理大且清晰。三角网格也是同样的道理,需要远小时候面片减少分辨率降低,近大时候需要面片增加分辨率提高。
ID3DXPMesh中的函数。
创建ID3DXPMesh网格:
hr = D3DXGeneratePMesh(
SourceMesh,
(DWORD*)adjBuffer->GetBufferPointer(), // adjacency
0,                  // default vertex attribute weights 顶点属性权值默认就可以了
0,                  // default vertex weights 顶点权值也一般默认
1,                  // simplify as low as possible 最小的面片或者顶点数,看后参数是D3DXMESHSIMP_FACE还是D3DXMESHSIMP_VERTEX
D3DXMESHSIMP_FACE,  // simplify by face count
&PMesh);
// 用SetNumFaces改名渐进网格面片数,也可以用TrimByFaces来改变最大和最小面片数,用TrimByVertices改变最大和最小顶点数,他们都需要在原来的最小和最大区域内,否则会被调整到原来的最小和最大中
// set to original detail
DWORD maxFaces = PMesh->GetMaxFaces();
PMesh->SetNumFaces(maxFaces);
// Add a face, note the SetNumFaces() will  automatically
		// clamp the specified value if it goes out of bounds.
		if( ::GetAsyncKeyState('A') & 0x8000f )
		{
			// Sometimes we must add more than one face to invert
			// an edge collapse transformation
			PMesh->SetNumFaces( numFaces + 1 );
			if( PMesh->GetNumFaces() == numFaces ) // 有时候设置增加一个面片会不成功需要增加2个
				PMesh->SetNumFaces( numFaces + 2 );
}
		// Remove a face, note the SetNumFaces() will  automatically
		// clamp the specified value if it goes out of bounds.
		if( ::GetAsyncKeyState('S') & 0x8000f )
			PMesh->SetNumFaces( numFaces - 1 );
// 设置了渐进网格片面后直接绘制就可以了
for(int i = 0; i < Mtrls.size(); i++)
		{
			// draw pmesh
			Device->SetMaterial( &Mtrls[i] );
			Device->SetTexture(0, Textures[i]);
			PMesh->DrawSubset(i);
			// draw wireframe outline
			/*Device->SetMaterial(&d3d::YELLOW_MTRL);
			Device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
			PMesh->DrawSubset(i);
			Device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);*/
}

4.从D3D网格计算包围盒

计算包围盒的目的,主要用于中级VSD可见性检测时候进行裁剪,和碰撞检测时候进行检测,如果是需要更加高精度的检测,那么先用包围盒进行初步的排除,然后再进行高精度的检测。一般要求不高的游戏中,直接从3dx max中导出包围盒和物体一起运动,只进行包围盒可见性或者碰撞检测即可。
计算AABB包围盒函数:D3DXComputeBoundingBox
计算包围球函数:D3DXComputeBoundingSphere
代码:
// 先自动定义包围球和包围盒
mesh->UnlockVertexBuffer();
	struct BoundingBox
	{
		BoundingBox();
		bool isPointInside(D3DXVECTOR3& p);
		D3DXVECTOR3 _min;
		D3DXVECTOR3 _max;
};
	struct BoundingSphere
	{
		BoundingSphere();
		D3DXVECTOR3 _center;
		float       _radius;
};
// 根据三角网格计算包围球和包围盒
bool ComputeBoundingSphere(ID3DXMesh* mesh, d3d::BoundingSphere* sphere)
{
	HRESULT hr = 0;
	BYTE* v = 0;
	mesh->LockVertexBuffer(0, (void**)&v);
	hr = D3DXComputeBoundingSphere(
			(D3DXVECTOR3*)v,
			mesh->GetNumVertices(),
			D3DXGetFVFVertexSize(mesh->GetFVF()),
			&sphere->_center,
			&sphere->_radius);
mesh->UnlockVertexBuffer();
	if( FAILED(hr) )
		return false;
	return true;
}
bool ComputeBoundingBox(ID3DXMesh* mesh, d3d::BoundingBox* box)
{
	HRESULT hr = 0;
	BYTE* v = 0;
	mesh->LockVertexBuffer(0, (void**)&v);
	hr = D3DXComputeBoundingBox(
			(D3DXVECTOR3*)v,
			mesh->GetNumVertices(),
			D3DXGetFVFVertexSize(mesh->GetFVF()),
			&box->_min,
			&box->_max);
mesh->UnlockVertexBuffer();
	if( FAILED(hr) )
		return false;
	return true;
}
// 将包围球和包围盒网格创建出来
	ComputeBoundingSphere(Mesh, &boundingSphere);
	ComputeBoundingBox(Mesh, &boundingBox);
	D3DXCreateSphere(
		Device,
		boundingSphere._radius,
		20,
		20,
		&SphereMesh,
0);
	D3DXCreateBox(
		Device,
		boundingBox._max.x - boundingBox._min.x,
		boundingBox._max.y - boundingBox._min.y,
		boundingBox._max.z - boundingBox._min.z,
		&BoxMesh,
0);
// 将网格物体和包围盒物体绘制出来
		Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);
		Device->BeginScene();
		// draw the mesh
		for(int i = 0; i < Mtrls.size(); i++)
		{
			Device->SetMaterial( &Mtrls[i] );
			Device->SetTexture(0, Textures[i]);
			Mesh->DrawSubset(i);
}	
		//
		// Draw bounding volume in blue and at 10% opacity
		D3DMATERIAL9 blue = d3d::BLUE_MTRL;
		blue.Diffuse.a = 0.10f; // 10% opacity
		Device->SetMaterial(&blue);
		Device->SetTexture(0, 0); // disable texture
		Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
		Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
		Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
		if( RenderBoundingSphere )
			SphereMesh->DrawSubset(0);
		else
			BoxMesh->DrawSubset(0);
		Device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
		Device->EndScene();
Device->Present(0, 0, 0, 0);
如果是真正的渲染框架中还需要对包围盒进行可见性检测的函数,和碰撞检测的函数。
直接取得boundingSphere和boundingBox对象进行计算即可,不必将包围盒和包围球的网格创建出来。

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