一方面:
使用Attribute Table 的首要原因是為了豐富的渲染物體,比如,把物體的面分為不同的組,每個組,對應一個Atrribute ID,即組號。這樣,可以為每個組制定不同的材質,要知道,D3D裡面,同時只能使用一種材質。
另一方面:
為每個面指定不同的組好後,通過,Opitimize()方法,可以將相同組號的面放在一起,這樣提高渲染效率。
這裡有一篇國外的講Attribute Table的很詳細的文章,呈現如下:
http://www.mvps.org/directx/articles/mesh_subset.htm
The ID3DXMesh class provided in DX 8 allows you to divide a mesh into several parts, or subsets, that can be rendered independently of each other using the ID3DXMesh::DrawSubset() method. This allows you to apply different materials, textures, or renderstates to different portions of a mesh, or to selectively render only certain portions of a mesh.
To do this, you must set the attribute ID for each face, to define which subset the face belongs to. First, call ID3DXMesh::LockAttributeBuffer() to get a pointer to the attribute buffer, which is an array of DWORDs that defines the subset number for each face. The subset numbers can be any 32 bit value you choose, though it is typical to use values 0 to n-1 to define n subsets.
Once you have locked the attribute buffers, you can set the desired attribute values using the buffer pointer, then unlock the attribute buffer using ID3DXMesh::UnlockAttributeBuffer() when the changes are complete.
At this point, you can use ID3DXMesh::DrawSubset() to render a subset, but you will first want to perform one more step for the sake of performance. If you were to call ID3DXMesh::DrawSubset() at this point, it would have to scan through the attribute buffer to determine which faces belong to the requested subset. Instead, if you call ID3DXMesh::OptimizeInPlace() or ID3DXMesh::Optimize() with the D3DXMESHOPT_ATTRSORT flag (see note below), the index and vertex buffers will be sorted such that each subset represents a contiguous sequence of faces. This call will also generate an "attribute table", which is an array of one D3DXATTRIBUTERANGE structure per subset defining the starting and ending face numbers for each range, and the range of vertices used. Once this is performed, ID3DXMesh::DrawSubset() merely has to look up the range of faces and vertices to draw from the attribute table, providing far greater performance.
Note: While D3DXMESHOPT_ATTRSORT is sufficient to set up the attribute tables, it does not provide further optimization to improve vertex cache performance. It is highly recommended that you instead use the D3DXMESHOPT_VERTEXCACHE flag, which will create the attribute table and also reorder the vertices to make optimum use of the vertex cache.
Here is an example of a function that divides a mesh into a series of irregularly sized subsets, allowing subsets to consist of non-contiguous series of faces:
// define subset info we will assignDWORD faceCount[] = { 50, 100, 50, 100, 200, 0};DWORD subsetNum[] = { 0, 1, 0, 2, 3 };// First 50 faces (0-49) belong to subset 0// Next 100 faces (50-149) belong to subset 1// Next 50 faces (150-199) also belong to subset 0// Next 100 faces (200-299) belong to subset 2// Next 200 faces (300-499) belong to subset 3// 0 faces in final faceCount[] index denotes end of list
if (FAILED(SetSubsets(pMesh,faceCount,subsetNum)) {
// handle error
...}
DWORD SetSubsets(ID3DXMesh *pMesh,DWORD *pFaceCount,DWORD *pSubsetNum) {
// Get face count, used to ensure we don't overrun attribute buffer DWORD numFaces=pMesh->GetFaceCount(); // lock the attribute buffer DWORD *attribBuf; HRESULT hr; if (SUCCEEDED(hr=pMesh->LockAttributeBuffer(D3DLOCK_DISCARD,&attribBuf))) {
// initialize face counter
DWORD faceNum=0;
// loop through the subsets
for (int i=0;pFaceCount[i];i++) {
// make sure there are enough faces for this subset
if (faceNum+pFaceCount[i]>=numFaces) {
// not enough faces, unlock the attribute buffer and return error code
pMesh->UnlockAttributeBuffer(); return E_INVALIDARG;
} // loop through the faces for this range
for (int j=0;j<pFaceCount[i];i++) {
// set the subset number of each face in this range
attribBuf[faceNum]=pSubsetNum[i];
// increment face counter
faceNum++;
}
}
// unlock the attribute buffer
pMesh->UnlockAttributeBuffer();
// allocate storage and generate adjacency data
DWORD *pAdj=new DWORD[numFaces*3];
if (!pAdj)
return E_OUTOFMEMORY;
if (FAILED(hr=pMesh->GenerateAdjacency(0.0f,pAdj) {
delete pAdj;
return hr;
}
// optimize the mesh with attribute sorting
// D3DXMESHOPT_ATTRSORT
if (FAILED(hr=pMesh->OptimizeInPlace(D3DXMESHOPT_VERTEXCACHE,pAdj,NULL,NULL,NULL)) {
delete pAdj; return hr;
}
// de-allocate adjacency data storage
delete pAdj;
} else
// return error code on failure to lock attribute buffer
return hr;
// return success
return S_OK;}
Note that if you already have adjacency data for the mesh, you won't have to re-compute it in the function as I have here - I included that for simplicity. If you already have adjacency data you can pass it to the optimization function, and may also need to provide a pointer to an array to contain the new adjacency information, as a parameter to the optimization function, if you have further use for this data.
For more information on using the D3DX Mesh interfaces, see Using the ID3DXMesh Class.