D3D网格(一)

1.D3D网格基础(顶点缓存,索引缓存,属性缓存,获取邻接信息,网格优化,优化后产生属性表缓存)

D3D网格就是指三角索引网格,包括了三角形列表信息和顶点信息,也就是包含了顶点缓存(顶点位置、uv、点法向量、材质),索引缓存,纹理信息,材质信息用于光照,网格的操作,点焊接简单焊接优化三角形,面拆分着色;边坍塌消减三角形,点拆分反演边坍塌生成LOD网格。
    1.1属性缓存的下标是对应三角形面片的编号,属性缓存上的ID值是对应的纹理数组和材质数组的下标,也就是多个三角形面片或者全部对应一个属性缓存ID。
     除此之外,还包括了属性缓存, 属性缓存在创建D3D网格的时候就存在了属性缓存是和三角形对应的(指索引缓存中指明的三角形)也就是属性缓存的大小等于三角形的个数(面片数)也就是索引缓存中一个三角形对应一个属性缓存ID,该属性ID指明三角形所属的纹理和材质组,属性ID存储在属性缓存中。 在进行网格优化时候,如果指明了D3DMESHOPT_ATTRSORT那么会对网格按照属性ID进行排序,且会创建属性表,属性表个数就是属性类型个数每个属性表指明了该属性的顶点开始位置和顶点个数,索引开始位置和索引个数,这样通过属性绘制网格时候就可以直接取出三角网格中的顶点缓存和索引缓存信息,指明对应的纹理信息和材质信息,就可以直接快速的绘制网格了。巨大的纹理和材质信息只需要提交一次就可以了,可以有效的提高D3D渲染网格的效率。
通过属性ID来绘制网格时候,遍历属性ID个数调用DrawSubset( attrID )就可以把网格绘制出来了。
  1.2 DrawSubset绘制函数,也是用索引缓存的,所以一切顶点快速绘制都需要顶点缓存和索引缓存(没索引缓存直接绘制点也可以)
属性子集和索引缓存的关系是,既然每个面片编号id都知道了,面片关联的材质网格属性id也知道了,通过面片编号faceid*3,faceid*3 + 1,faceid*3+2就可以确定索引缓存下标,索引缓存下标的值就是顶点缓存的下标,可以通过顶点缓存获取顶点数据,材质纹理属性id可以通过材质和纹理数组获取,然后进行渲染。优化得到属性表后,可以获得更快的渲染效率。

The subset that is specified by AttribId will be rendered by the IDirect3DDevice9::DrawIndexedPrimitive method, using the D3DPT_TRIANGLELIST primitive type, so anindex buffer must be properly initialized.纹理材质也应该是要指定好的,没有对网格优化时候也是可以用属性ID绘制网格的,但是优化了网格产生了属性表,绘制速度会更快。

An attribute table is used to identify areas of the mesh that need to be drawn with different textures, render states, materials, and so on. In addition, the application can use the attribute table to hide portions of a mesh by not drawing a given attribute identifier (AttribId) when drawing the frame.



    2.邻接信息是邻接数组和索引缓存一样,下标和三角形编号相对应(查询编号为i的三角形可以通过访问邻接数组或索引数组下标为(3*i, 3*i + 1, 3 * i + 2)上的值得到三角形i相关的邻接信息或者索引信息),邻接信息的值是边(边是索引缓存中当前点和下一个点组成的边)相邻的三角形的编号(没有则是-1或者SHORT_MAX值)。索引缓存的下标也是和邻接信息一样和三角形编号相对应,值则是顶点缓存上的索引。
    邻接信息缓存,当 创建D3D网格时候,可以获取邻接信息,不存在邻接信息缓存,因为是从网格中获取出来的,邻接信息是指明了三角形边相邻的三角形的信息。邻接信息和索引缓存对应,也就是 索引缓存中三个索引指明的三角形就是对应邻接信息中指明的三角形,邻接信息大小为三角形个数的三倍(因为边),索引缓存的顶点v1v2v3对应邻接信息的e1e2e3,其中e1对应v1v2指明的边,e1的值指明v1v2边相邻的三角形是第几个三角形(三角形标识从0开始),没有是-1, 其它邻接信息缓存上的值同理写入。
        邻接信息真正的有用关系,邻接信息和索引缓存大小一样,且和索引缓存对应,邻接信息缓存key是和索引缓存一样(概念为边),value没有DX中是USHRT_MAX,有是和当前三角形相邻的三角形编号,该编号代表索引下标3*value, 3*value + 1, 3*value + 2代表的三角形,而索引上的value代表的是顶点缓存的key,这样可以通过邻接信息是可以获取到相邻的三角形的顶点信息的。例如:

for(int i = 0; i < mesh->GetNumFaces(); i++)
{
     // 根据当前的面片信息,获取当前的缓存信息,因为索引缓存和三角形的关系是,索引下标(3*i, 3*i + 1, 3*i + 2)确定当前编号为i的三角形
    WORD index0 = indices[i * 3];
    WORD index1 = indices[i * 3 + 1];
    WORD index2 = indices[i* 3 + 2];

    // Now extract the triangles vertices positions
    // 索引缓存上的值是顶点缓存的下标,通过顶点缓存得到顶点信息
    D3DXVECTOR3 v0 = vertices[index0].position;
    D3DXVECTOR3 v1 = vertices[index1].position;
    D3DXVECTOR3 v2 = vertices[index2].position;

    // 根据当前的面片信息,得到当前的邻接信息,因为邻接信息和索引缓存对应的,邻接信息下标(3*i, 3*i + 1, 3*i + 2)确定当前编号为i的三角形
    // 邻接信息上的值是代表当前三角形边,相邻的三角形的编号。
    WORD faceIndex0 = adj[i * 3];
    WORD faceIndex1 = adj[i* 3 + 1];
    WORD faceIndex2 = adj[i * 3 + 2];
    // 得到了邻接三角形的编号,就可以得到邻接三角形的索引缓存值,从而得到顶点缓存值
   if( faceIndex0 != USHRT_MAX ) // is there an adjacent triangle?
    {
        WORD i0 = indices[faceIndex0 * 3];
        WORD i1 = indices[faceIndex0 * 3 + 1];
        WORD i2 = indices[faceIndex0 * 3 + 2];

        D3DXVECTOR3 v0 = vertices[i0].position;
        D3DXVECTOR3 v1 = vertices[i1].position;
        D3DXVECTOR3 v2 = vertices[i2].position;

        D3DXVECTOR3 edge0 = v1 - v0;
        D3DXVECTOR3 edge1 = v2 - v0;
        D3DXVec3Cross(&faceNormal0, &edge0, &edge1);
        D3DXVec3Normalize(&faceNormal0, &faceNormal0);
    }
}


     Mesh->OptimizeInplace优化网格原来的网格会改变,顶点缓存,索引缓存也会排序(也可以使用Optimize优化网格原来的网格不变,返回一个优化后的网格)  优化标识D3DXMESHOPT_VERTEXCACHE和D3DXMESHOPT_STRIPREORDER不能在一起组合,其它组合都可以 优化网格后, 网格里面的顶点缓存和索引缓存都会发生变化 ,但属性缓存为每个三角形指定的属性ID不会改变,邻接信息也会发生变化


2. D3DXCreateMeshFVF建立网格和填充顶点缓存,索引缓存,属性缓存,产生邻接信息和优化网格的代码

也可以用 D3DXCreateMesh创建网格,不过需要D3DXDeclaratorFromFVF来配合设置FVF信息。

bool Setup()
{
 HRESULT hr = 0;

 //
 // We are going to fill the empty mesh with the geometry of a box,
 // so we need 12 triangles and 24 vetices.
 //
 // 创建空的网格
 hr = D3DXCreateMeshFVF(
  12,
  24,
  D3DXMESH_MANAGED,
  Vertex::FVF,
  Device,
  &Mesh);

 if(FAILED(hr))
 {
  ::MessageBox(0, "D3DXCreateMeshFVF() - FAILED", 0, 0);
  return false;
 }

 //
 // Fill in vertices of a box
 //
 Vertex* v = 0;
 Mesh->LockVertexBuffer(0, (void**)&v);

 // fill in the front face vertex data
 v[0] = Vertex(-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f);
 v[1] = Vertex(-1.0f,  1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f);
 v[2] = Vertex( 1.0f,  1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f);
 v[3] = Vertex( 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f);

 // fill in the back face vertex data
 v[4] = Vertex(-1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
 v[5] = Vertex( 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f);
 v[6] = Vertex( 1.0f,  1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f);
 v[7] = Vertex(-1.0f,  1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f);

 // fill in the top face vertex data
 v[8]  = Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
 v[9]  = Vertex(-1.0f, 1.0f,  1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f);
 v[10] = Vertex( 1.0f, 1.0f,  1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f);
 v[11] = Vertex( 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);

 // fill in the bottom face vertex data
 v[12] = Vertex(-1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f);
 v[13] = Vertex( 1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f);
 v[14] = Vertex( 1.0f, -1.0f,  1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f);
 v[15] = Vertex(-1.0f, -1.0f,  1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f);

 // fill in the left face vertex data
 v[16] = Vertex(-1.0f, -1.0f,  1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f);
 v[17] = Vertex(-1.0f,  1.0f,  1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
 v[18] = Vertex(-1.0f,  1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f);
 v[19] = Vertex(-1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f);

 // fill in the right face vertex data
 v[20] = Vertex( 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f);
 v[21] = Vertex( 1.0f,  1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
 v[22] = Vertex( 1.0f,  1.0f,  1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f);
 v[23] = Vertex( 1.0f, -1.0f,  1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f);

 Mesh->UnlockVertexBuffer();

 //
 // Define the triangles of the box
 //
 WORD* i = 0;
 Mesh->LockIndexBuffer(0, (void**)&i);

 // fill in the front face index data
 i[0] = 0; i[1] = 1; i[2] = 2;
 i[3] = 0; i[4] = 2; i[5] = 3;

 // fill in the back face index data
 i[6] = 4; i[7]  = 5; i[8]  = 6;
 i[9] = 4; i[10] = 6; i[11] = 7;

 // fill in the top face index data
 i[12] = 8; i[13] =  9; i[14] = 10;
 i[15] = 8; i[16] = 10; i[17] = 11;

 // fill in the bottom face index data
 i[18] = 12; i[19] = 13; i[20] = 14;
 i[21] = 12; i[22] = 14; i[23] = 15;

 // fill in the left face index data
 i[24] = 16; i[25] = 17; i[26] = 18;
 i[27] = 16; i[28] = 18; i[29] = 19;

 // fill in the right face index data
 i[30] = 20; i[31] = 21; i[32] = 22;
 i[33] = 20; i[34] = 22; i[35] = 23;

 Mesh->UnlockIndexBuffer();

 //
 // Specify the subset each triangle belongs to, in this example
 // we will use three subsets, the first two faces of the cube specified
 // will be in subset 0, the next two faces will be in subset 1 and
 // the the last two faces will be in subset 2.
 //
 // 指定每个三角形的属性缓存ID,优化后也不会改变
 DWORD* attributeBuffer = 0;
 Mesh->LockAttributeBuffer(0, &attributeBuffer);

 for(int a = 0; a < 4; a++)
  attributeBuffer[a] = 0;

 for(int b = 4; b < 8; b++)
  attributeBuffer[b] = 1;

 for(int c = 8; c < 12; c++)
  attributeBuffer[c] = 2;

 Mesh->UnlockAttributeBuffer();

 //
 // Optimize the mesh to generate an attribute table.
 //
 // 声明邻接缓存,并从网格中生成
 std::vector<DWORD> adjacencyBuffer(Mesh->GetNumFaces() * 3);
 Mesh->GenerateAdjacency(0.0f, &adjacencyBuffer[0]);

 OutFile.open("Mesh Dump.txt");
 dumpAdjacencyBuffer(OutFile, Mesh);
 /*HRESULT OptimizeInplace(
  [in]        DWORD        Flags,
  [in]  const DWORD        *pAdjacencyIn,
  [out]       DWORD        *pAdjacencyOut,
  [out]       DWORD        *pFaceRemap,
  [out]       LPD3DXBUFFER *ppVertexRemap
  );
  Parameters

  Flags [in]

  Type: DWORD

  Combination of one or more D3DXMESHOPT flags, specifying the type of optimization to perform.
  pAdjacencyIn [in]

  Type: const DWORD*

  Pointer to an array of three DWORDs per face that specifies the three neighbors for each face in the source mesh. If the edge has no adjacent faces, the value is 0xffffffff.
  pAdjacencyOut [out]

  Type: DWORD*

  Pointer to an array of three DWORDs per face that specifies the three neighbors for each face in the optimized mesh. If the edge has no adjacent faces, the value is 0xffffffff. If the value supplied for this argument is NULL, adjacency data is not returned.
  pFaceRemap [out]

  Type: DWORD*

  An array of DWORDs, one per face, that identifies the original mesh face that corresponds to each face in the optimized mesh. If the value supplied for this argument is NULL, face remap data is not returned.
  ppVertexRemap [out]

  Type: LPD3DXBUFFER*

  Address of a pointer to an ID3DXBuffer interface, which contains a DWORD for each vertex that specifies how the new vertices map to the old vertices. This remap is useful if you need to alter external data based on the new vertex mapping. If the value supplied for this argument is NULL, vertex remap data is not returned.
  */

 // 优化网格, 优化标识D3DXMESHOPT_VERTEXCACHE和D3DXMESHOPT_STRIPREORDER不能在一起组合,其它组合都可以
 // 优化网格后,网格里面的顶点缓存和索引缓存都会发生变化,但属性缓存为每个三角形指定的属性ID不会改变,邻接信息
 // 也会发生变化。
 std::vector<DWORD> adjacencyBufferAfter(Mesh->GetNumFaces() * 3);
 hr = Mesh->OptimizeInplace(
  D3DXMESHOPT_ATTRSORT |
  D3DXMESHOPT_COMPACT  |
  D3DXMESHOPT_VERTEXCACHE,
  &adjacencyBuffer[0],
  &adjacencyBufferAfter[0],
  0, // pFaceRemap不需要就填空
  0); // ppVertexRemap不需要则填空

// 禁用光照
Device->SetRenderState(D3DRS_LIGHTING, false);

 //
 // Dump the Mesh Data to file.
 //
}

3.根据网格属性集(优化后有属性表)绘制网格的代码

Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0);
  Device->BeginScene();

  // 优化后,也是直接根据网格属性集个数,直接绘制三角网格出来,设置一次大型纹理信息,有效的提高了性能
  for(int i = 0; i < NumSubsets; i++)
  {
   Device->SetTexture( 0, Textures[i] );
   Mesh->DrawSubset( i );
  }

  Device->EndScene();
  Device->Present(0, 0, 0, 0);


你可能感兴趣的:(D3D网格(一))