2012-03-17 76 views
1

我正在使用webgl開發一款小遊戲。在這個遊戲中,我有一些由許多(100+)樹木物體組成的森林。因爲我只有幾個不同的樹模型,所以我在顯示它們之前以不同的方式旋轉和縮放這些模型。如何使用webgl實現批處理?

目前,我遍歷所有的樹來顯示它們:

for (var tree in trees) { 
    tree.display(); 
} 

雖然樹的display()方法是這樣的:

display : function() { // tree 
    this.treeModel.setRotation(this.rotation); 
    this.treeModel.setScale(this.scale); 
    this.treeModel.setPosition(this.position); 
    this.treeModel.display(); 
} 

許多樹對象共享相同的treeModel對象,所以我我必須在每次顯示模型前設置模型的旋轉/縮放/位置。每棵樹的旋轉/比例/位置值都不相同。

treeModel的顯示方法做了所有的GL東西:

display : function() { // treeModel 
    // bind texture 
    // set uniforms for projection/modelview matrix based on rotation/scale/position 
    // bind buffers 
    // drawArrays 
} 

所有樹模型使用相同的着色器,但可以使用不同的紋理。

由於一棵樹模型只包含幾個三角形,所以我想將所有樹合併成一個VBO並用一個drawArrays()調用顯示整個森林。

一些假設,使談論比較容易的數字:

  • 有250棵顯示
  • 有5個不同的樹模型
  • 每個樹模型有50個三角形

問題我有:

  • 目前我有5個緩衝區,大小爲50 * 3 * 8 (position + normal + texCoord) * floatSize字節。當我想顯示所有樹與一個vbo我會有一個緩衝區與250 * 50 * 3 * 8 * floatSize字節大小。我認爲我不能使用索引緩衝區,因爲我對每棵樹都有不同的位置值(由樹模型的位置值和樹的位置/比例/旋轉計算得出)。這是正確的還是仍然有一種方法可以使用索引緩衝區來至少減少緩衝區大小?也許有其他方法來優化這個?

  • 如何處理樹模型的不同紋理?我可以將所有紋理綁定到不同的紋理單元,但是如何在着色器中決定哪些紋理應該用於當前顯示的片段?

  • 當我想在運行時向此緩衝區添加新樹(或任何其他類型的對象)時:是否必須創建新緩衝區並複製內容?我認爲無法使用glMapBuffer添加新值。它是否正確?

回答

4

索引元素緩衝區只能覆蓋長度等於或低於65535的屬性,因此您需要使用drawArrays代替。這通常不是一個大的損失。

您可以使用GL.bufferSubData將樹添加到緩衝區的末尾。

如果您的紋理尺寸合理(如128x128或256x256),您可以將它們合併爲一個大紋理,並使用UV-coords處理整個事物。如果沒有,你可以添加另一個屬性來說明頂點屬於哪個紋理,並且在頂點着色器中有一個條件,或者是一個sampler2Ds數組(不確定它是否工作,從未嘗試過)。請記住,着色器中的條件非常緩慢。

如果您決定堅持使用您當前的解決方案,請確保對樹進行排序,以便使用相同的紋理進行一次渲染 - 始終保持狀態切換至關重要。

+0

+1紋理圖集。此外,我不認爲WebGL(至少,Chrome在Mac OS上的實現,但可能的WebGL本身)支持陣列紋理。 – 2012-03-18 20:34:39

+0

將多個紋理合併爲一個是個好主意。我會研究這一點。我也不知道索引限於65k。好信息,謝謝。 – micha 2012-03-18 21:20:09

+0

由於用於索引元素的數據類型(Uint16Array),它被限制爲65k。 – MikaelEmtinger 2012-03-19 15:23:02

1

您目前的做法並不是那麼糟糕。我會說:堅持下去,直到你撞到牆上。

對於單個drawElements/drawArrays調用,50個三角形已經是合理的批量大小。這不是最佳的,但也不是那麼糟糕。因此,每一棵樹都可以通過制服來改變位置,紋理和形狀等參數。然後爲每棵樹進行一次繪製調用。總共250個drawElements調用也不算太糟糕。

所以我會使用一個包含所有使用的樹幾何變體的VBO。我實際上將樹分成了積木,以便我可以重新組合它們以增加多樣性。在調用drawArrays或drawElements之前,每棵樹都會在VBO中設置適當的偏移量。

另外不要忘記,你可以做一個非常便宜的視野剔除每棵樹。

2

的一點想法:

  1. 一旦你在你的世界種一棵樹,你有沒有改變呢?它會動畫嗎?或者它只是靜態幾何?如果它真的是靜態的,那麼你總是可以用每個樹的幾個副本構建一個單獨的緩衝區。在追加樹時,首先應用(使用Javascript)該實例的世界變換到頂點。如果使用三角形條,可以使用簡併多邊形將樹鏈接在一起。
  2. 你可以推出自己的僞實例化繪圖:
    1. 編碼在陣列緩存實例ID。只需將該屬性設置爲同一樹實例中所有頂點的相同值即可。我似乎記得在ES GLSL中不能有非floaty的頂點屬性(也許這是Chrome的限制),所以你需要把它作爲一個float來使用,但是把它作爲一個int來使用。既然它是以浮點形式出現的,你將不得不處理它在三角形內插的事實,所以這個值會有很小的波動 - 只是簡單地四捨五入到最接近的整數修正。
    2. 使用單獨的紋理(我將調用數據紋理)來編碼所有每個實例的信息。在頂點着色器中,查看當前頂點的實例ID並使用它來計算數據紋理中的紋理座標。取出你需要的任何變換當前頂點並應用它。我想,這就是所謂的「依賴紋理讀取」,這通常是令人難以接受的,因爲這可能會導致性能問題,但它可以幫助你批你的幾何形狀,這可以幫助解決性能問題。如果你有興趣,你必須嘗試一下,看看會發生什麼。
  3. 希望延期,支持真正的實例化圖。
+0

至#1:目前樹木完全靜止。不過,我想不得不選擇稍後爲其製作動畫,而無需更改所有內容。我認爲動畫將是我目前的方法的另一個問題。感謝「依賴紋理讀取」的想法。我肯定會研究這一點。 – micha 2012-03-18 21:34:32