webgl繪製一個三角形

轉載來自:http://www.jiazhengblog.com/blog/2016/02/19/2910/

很多WebGL教程都會從繪製一個三角形開始,如同其它語言教程中的Hello World。我也不能免俗,來一起看一下如何在WebGL中繪製三角形吧。本文將向你介紹如下內容:

  • 如何定義三角形頂點
  • 如何繪製三角形
  • 如何繪製多個三角形

繪製一個三角形

完整的代碼可以看這裏

如果你對比之前繪製點的代碼,發現繪製三角形僅在兩個地方進行了修改:

  • 頂點定義部分
  • 繪製部分

一個三角形包含三個頂點,因此vertex變量中數據內容增多了:

1
2
3
4
5
var vertex = [
    -.5, -.2,  0,
    .5,  -.2,  0,
    0,   .6,   0
];

數組中包含了三個點的座標信息,每個點包含x、y、z三個維度,因此數組的長度爲9。爲了可讀性,頂點數據一般每行寫三個元素,這樣每一行就代表一個點,也比較容易看出點的數量。

三角形頂點的順序可以任意來寫,但是我的習慣是從左下角的頂點開始寫起,並按照逆時針的順序。無論採用什麼方案,只要在一個項目中保持一致即可。從下圖我們可以看到頂點的對應關係

 

 

下面一個發生變化的地方是繪製代碼:

1
gl.drawArrays(gl.TRIANGLES, 0, 3);

這裏繪製模式變爲gl.TRIANGLES,表示繪製爲三角形,即要繪製一個面,只有面纔會有填充顏色。第三個參數由1變爲3,表示要繪製三個頂點。

我們會在瀏覽器裏看到下面的效果:

 

 

繪製多個三角形

在WebGL中不論多麼複雜的幾何體最終都是由三角形構成,因此在繪製之前都需要將物體進行三角形化(Triangulation),也就是把複雜的多邊形分解爲一個個三角形,再交由WebGL繪製。在下面的例子中,我們來繪製一個矩形,其中包含兩個三角形,如下圖所示:

 

 

首先還是準備頂點數組,現在是兩個三角形,於是數組的長度變爲18(2個三角形 x 3個頂點 x 3個座標):

1
2
3
4
5
6
7
8
var vertex = [
    -.5, -.3,  0,
    .5,  -.3,  0,
    .5,   .3,  0,
    -.5, -.3,  0,
    .5,   .3,  0,
    -.5,   .3,  0
];

數組中座標對應關係如下圖所示:

 

 

最後我們修改drawArray代碼:

1
gl.drawArray(gl.TRIANGLES, 0, 6);

可以看到一個矩形繪製出來了。

 

 

如果你是個細節控、或者愛問問題,那麼你可能會發現定義座標的時候存在數據冗餘,因爲兩個三角形中有兩個頂點是共用的。有沒有辦法來避免有冗餘數據呢?

索引緩衝區對象 Index Buffer Object(IBO)

除了VBO,WebGL還有另外一類緩衝區對象,稱作索引緩衝區對象(Index Buffer Object,IBO,後文均採用IBO一詞)。通過索引的方式可以達到去除冗餘頂點數據的目的。我們來看看它是怎麼做到的。

首先修改vertex數組,裏面只包含必須的4個頂點:

1
2
3
4
5
6
var vertex = [
    -.5, -.3,  0,
    .5,  -.3,  0,
    .5,   .3,  0,
    -.5,   .3,  0
];

在給VBO填充數據之後我們增加創建IBO的代碼:

1
2
3
4
5
6
7
var index = [
    0, 1, 2,
    0, 2, 3
];
var indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(index), gl.STATIC_DRAW);

index中的數據指明每個三角形的頂點在VBO中的位置,第一行 0, 1, 2 表示第一個三角形由VBO中第0個、第1個和第2個頂點(注意是頂點,每個頂點包含三個座標,所以你要三個元素三個元素的看)構成,第二行的 0, 2, 3 表示第二個三角形由VBO中的第0個、第2個和第3個頂點構成。

 

 

接着創建緩衝區對象,這和之前創建VBO代碼一樣。在綁定緩衝區時,第一個參數爲gl.ELEMENT_ARRAY_BUFFER,此時WebGL才知道這是一個索引緩衝區,後面的bufferData的首個參數也是這個值,傳輸的數據同樣需要typed array,這裏我們使用Uint16Array,也就是每個索引值採用無符號16位整型數值,當然對於我們這個簡單的例子,用Uint8Array也是可以的。

最後我們需要使用WebGL另外一個繪製函數來繪製:

1
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);

如果使用了IBO,就需要用drawElements方法來替代原來的drawArrays,drawElements原型如下:

1
void drawElements(GLenum mode, GLsizei count, GLenum type, GLintptr offset);

這裏多了一個type,表示索引的數據類型。這裏我用使用unsigned short類型,它和之前Uint16Array是一致的。如果前面用了Uint8Array,那麼這裏需要改成gl.UNSIGNED_BYTE。

在採用了索引方式後,vertex內容確實減少了,在複雜的模型裏這會節省出不少的內存空間。如果你是一個追求極致的人,你會發現索引裏仍然存在冗餘,索引0和2出現了兩次。有沒有辦法把索引中的冗餘也去掉呢?請你往下看。

不同的繪製模式

不論是drawArrays還是drawElements,第一個參數都是指定繪製模式(mode),目前我們繪製三角形使用的模式都是gl.TRIANGLES,此外還有兩種與繪製三角形相關的模式:gl.TRIANGLE_STRIP和gl.TRIANGLE_FAN。

關於這三種模式的區別,我們用一張圖來說明:

 

 

  • gl.TRIANGLES:用來繪製相互獨立的三角形。從緩衝區中每次獲取3個頂點的數據作爲一個三角形。
  • gl.TRIANGLE_STRIP:用來繪製有共享邊的三角形。從第二個三角形開始,每次讀取一個頂點,並利用前面的末尾兩個頂點構成一個三角形,以此類推。
  • gl.TRIANGLES_FAN:同樣用來繪製有共享邊的三角形。從第二個三角形開始,每次讀取一個頂點,並利用首個頂點和之前最後一個頂點來構成一個三角形,以此類推。

在繪製矩形的例子中,我們分別看如何使用strip和fan這兩種模式。

strip模式下,我們修改index爲:

1
2
3
var index = [
    1, 2, 0, 3
];

fan模式下,修改index爲:

1
2
3
var index = [
    0, 1, 2, 3
];
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章