網格的原理:
我們在前面已經知道,一個物體可以用三角形單元來逼近表示。而網格是由一個或者多個子集構成,這些子集是網格中一組可以用相同屬性進行繪制的三角形單元(屬性相同是指:材質、紋理和繪制狀態)
或者你可以不太明白,那么看下圖:
要想知道每個三角形是屬于哪個子集的,就必須給每個三角形一個ID,這個ID就是指的這個三角形所屬子集。例如:從上圖中可以看出,如果一個三角形的ID為0,那么就是屬于子集0(即:地板),如果ID為1,那么該三角形就是屬于子集1(即:墻)。
說的很清楚,那么三角形單元的ID如何在計算機中存儲呢?對于這些數據,計算機將其存儲在屬性緩存中的。屬性緩存的實質就是一個DWORD數組,由于每個三角形單元的ID都存儲在屬性緩存中,那么屬性緩存中的項一一對應每個三角形單元,也就是說三角形單元的個數等于屬性緩存中的數據個數。而且,屬性緩存中的項與索引緩存中定義的三角形單元是一一對應的。因為索引緩存中每連續三個數據構成一個三角形單元,也就是說一個屬性緩存的數據對應三個索引緩存數據:即屬性緩存的第i項對應于索引緩存中的第i個三角形:
A = i * 3;
B = i * 3 + 1;
C = i * 3 + 2;
下圖解釋了這種關系:
那么這種技術該怎么使用?或者說有什么好處?我們為什么要這樣用?
我的粗淺理解就是:我們這樣做的目的就是為了方便繪制。ID3DXMesh接口提供了繪制一個子集的所有三角形單元的方法:如:繪制子集0:
Mesh->DrawSubset(0);如果要繪制一個網格,就必須繪制所有的子集。那么前面所說,一個子集的材質、紋理、繪制狀態是一樣的,也就是說:一個子集和一組屬性是一一對應的。這樣,我們將各個子集的屬性ID依次設置為0,1,2......n-1。n為子集總數。每個子集對應一組紋理、材質、繪制狀態,我們用索引i
就可以找到與子集i所對應的紋理和材質,這種方式使得我們使用一個簡單的循環就完成了整個網格的繪制。
for (int i = 0; i < Numsubsets; i++){Device->SetTexture(0, Textures[i]); mesh->DrawSubset(i);}
網格優化:
上面說的那么多枯燥的話,現在來說的有意思的東西。
為了更高效的繪制一個網格(高效的東西總是令人追求和喜愛),我們可以對網格內的頂點和索引進行重組,這個重組的過程就是網格優化。
優化的方法有很多:從網格內移除那些無用的頂點和索引數據、提高頂點高速緩存的命中率、對索引緩存進行優化忽略頂點等等。這些我在此都不講,我們只講:
屬性表優化方式優化機理:屬性表優化方式是指:我們依據屬性對各三角形單元進行排序,并且生成了一個屬性表。這樣可以使得DrawSubset獲得更高的繪制效率。
我們讓構成該網格的三角形依據其屬性進行排序,那么,相同屬性的三角形就位于了一塊連續的存儲空間內,也就是說:術語特定子集的三角形單元就會被保存在頂點緩存或者索引緩存中的一個連續的存儲空間內。
看下圖理解:
構成網格的三角形和屬性緩存中的內容都依據了屬性進行排序,這樣屬于特定子集的三角形就會位于一塊連續的存儲空間內。使得我們可以很容易的標出一個子集的三角形單元在存儲空間中的起始位置和結束位置。如上圖:子集0包含了三個三角形:0,1,2。子集1包含了三角形:3,4,5,6,7。(上圖中的Tri并不是指的點,而是三角形單元)
這樣做的意義何在呢?
我們先說如果繪制的時候按照原來的方式:
我們知道,剛開始所有的三角形單元存儲是雜亂無章的存儲在一塊存儲空間內。那么,如果我們想在繪制子集0的三角形單元,首先我們需要遍歷一遍該存儲空間,找出ID為0的所有三角形單元進行繪制。這種方法的效率無疑是低下的。因為其中做了很多不需要的判斷和查找,而且需要遍歷所有數據。
那么,如果我們按照屬性表優化方式:
所有相同屬性的三角形單元都存儲在連續的空間內,這樣我們如果繪制子集0,就不需要遍歷所有的數據進行查找,因為它們位于連續的空間內,省去了很多的查找。這樣做,雖然看起來沒多大卵用。但是如果數據大了呢?這種方式無疑會很大程度的提高繪制效率。而且敲代碼的原則就是:盡量優化。
代碼實現:
ok,原理都講完了,現在來說實現:
由于代碼太長就不粘貼了。而且覺得一些函數、接口的參數實在無意義,我把接口或者所用到的函數給出來,大家自行查看API。我會在文章末尾提供完整下載鏈接,完整代碼附有要點注釋。
總得來說步驟大致如下:
先給出全局變量:
IDirect3DDevice9 * Device = 0; //主設備ID3DXMesh* mesh = 0; //網格const DWORD Numsubsets = 3; //網格子集個數IDirect3DTexture9* Textures[3] = { 0, 0, 0 }; //紋理存儲
1.創建空的網格:
HRESULT hr = 0;hr = D3DXCreateMeshFVF( //創建網格12,24,D3DXMESH_MANAGED,Vertex::FVF,Device,&mesh);
2.將數據寫入:(1)頂點緩存:
Vertex* v = 0;mesh->LockVertexBuffer(0, (void**)&v);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);..............................................................mesh->UnlockVertexBuffer();
(2)索引緩存:
WORD* i = 0;mesh->LockIndexBuffer(0, (void**)&i);i[0] = 0; i[1] = 1; i[2] = 2;i[3] = 0; i[4] = 2; i[5] = 3;i[6] = 4; i[7] = 5; i[8] = 6;i[9] = 4; i[10] = 6; i[11] = 7;...................................mesh->UnlockIndexBuffer();
3.指定網格中每個片面所屬的子集:
本例中,我們制定索引緩存中的前四個三角形單元屬于子集0,中間4個屬于子集1,最后的屬于子集2.:
//屬性緩存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();
4.生成該網格的鄰接信息:
對于網格的一些運算,必須要用到鄰接信息,包括網格優化。需要知道對于給定的三角形,哪些三角形與其相鄰。這些信息被存儲在鄰接數組中。鄰接數組的類型為DWORD,其中每一項包括了一個標識網格中某個三角形的索引。例如:第i項是由下列頂點構成的三角形單元:
A = i * 3;
B = i * 3 + 1;
C = i * 3 + 2;
至于這個鄰接信息到底如何用的,博主現在還不太了解。等知道了,立馬更新。
//計算該網格的鄰接信息:std::vectoradjacencyBuffer(mesh->GetNumFaces() * 3);mesh->GenerateAdjacency(0.0f, &adjacencyBuffer[0]);
5.對網格進行優化:
hr = mesh->OptimizeInplace(D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_COMPACT | D3DXMESHOPT_VERTEXCACHE, &adjacencyBuffer[0], 0, 0, 0);
6.繪制網格:
for (int i = 0; i < Numsubsets; i++){Device->SetTexture(0, Textures[i]);mesh->DrawSubset(i);}
總的來說:
1.創建空的網格對象。
2.將數據寫入網格緩存中
3.指定網格內每個三角形單元所屬的子集
4.生成該網格的鄰接信息
5.優化網格。
6.繪制網格。
代碼運行效果:
就愛閱讀www.92to.com網友整理上傳,為您提供最全的知識大全,期待您的分享,轉載請注明出處。
歡迎轉載:http://www.kanwencang.com/bangong/20161116/54003.html
文章列表