D3D网格(二)

从网格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对象进行计算即可,不必将包围盒和包围球的网格创建出来。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值