一起學(xué) WebGL:繪制三角形
三角形可太重要了,再?gòu)?fù)雜的三維模型都是由一個(gè)個(gè)小三角形組合而成,越多越精細(xì)越真實(shí)。
繪制三角形
這次繪制三角形,要繪制的點(diǎn)就有三個(gè)了,不再是一個(gè)。為此我們需要用到緩存區(qū)對(duì)象(buffer object)。
通過(guò)緩存區(qū)對(duì)象,我們可以一次性向頂點(diǎn)著色器傳入多個(gè)頂點(diǎn)數(shù)據(jù)。
Float32Array
首先我們來(lái)用 Float32Array 數(shù)組保存需要用到的三個(gè)點(diǎn)的位置信息。
為了更簡(jiǎn)單一些,這里先不考慮 z 維度,每個(gè)頂點(diǎn)只用了 x 和 y 分量。到時(shí)候傳遞到頂點(diǎn)著色器的 gl_Position 時(shí),z 會(huì)自動(dòng)填充默認(rèn)的 0。
Float32Array 是一個(gè)比較特殊的 JavaScript 數(shù)組,最初也是為了和 WebGL 配合使用的,它可以創(chuàng)建一個(gè) 32 位浮點(diǎn)數(shù)的強(qiáng)類型數(shù)組。
普通的 JS 數(shù)組沒(méi)有類型的概念,數(shù)組元素可能是數(shù)字、字符串、對(duì)象的混合體,傳給 WebGL,要處理也麻煩,不太合適。
需要注意的是這種強(qiáng)類型數(shù)組的 API 和普通數(shù)組不一樣,比如不能用 push 方法。
緩沖區(qū)對(duì)象的創(chuàng)建和數(shù)據(jù)寫(xiě)入
首先是用 gl.createBuffer 方法創(chuàng)建一個(gè)緩沖區(qū)對(duì)象。
然后用 gl.bindBuffer(target, buffer) 將緩存區(qū)綁定到 gl 上下文指定目標(biāo)(gl.ARRAY_BUFFER)中。target 參數(shù)還有另一個(gè)值是 gl.ELEMENT_ARRAY_BUFFER,這個(gè)我們后面章節(jié)講如何繪制立方體的時(shí)候會(huì)用到哈。
buffer 參數(shù)就是剛剛創(chuàng)建的緩沖區(qū)對(duì)象。
最后往緩沖區(qū)對(duì)象寫(xiě)入我們剛剛的數(shù)組數(shù)據(jù)。最后一個(gè)參數(shù) gl.STATIC_DRAW,表示數(shù)據(jù)寫(xiě)入后不會(huì)再被修改,去繪制 靜態(tài) 場(chǎng)景。
綁定到頂點(diǎn)著色器上
接著就是讓緩沖區(qū)對(duì)象對(duì)接上頂點(diǎn)著色器的變量。
首先通過(guò) gl.getAttribLocation 拿到頂點(diǎn)著色器中聲明的 a_Position 變量的地址。
然后是比較復(fù)雜的 gl.vertexAttribPointer 方法。參數(shù)說(shuō)明:
- location,待分配的 attribute 變量地址;
- size,每個(gè)頂點(diǎn)分量分量個(gè)數(shù),就是一次從中取幾個(gè)作為一個(gè)頂點(diǎn)數(shù)據(jù),不夠的補(bǔ)缺省值。
- type,指定數(shù)據(jù)格式,gl.FLOAT 表示使用的是 Float32Array 的類型。
- normalize,是否將浮點(diǎn)數(shù)歸一化到 [0, 1] 或 [-1, 1] 。
- stride,相鄰兩個(gè)頂點(diǎn)之間的字節(jié)數(shù),以后用數(shù)組保存多種信息數(shù)據(jù)時(shí)會(huì)用到。
- offset,指定緩沖區(qū)對(duì)象中的偏移量。
目前我們只需要知道 location 和 size 就行了。最后兩個(gè)參數(shù)會(huì)在繪制顏色漸變的三角形用到,這里不細(xì)說(shuō)。
最后是用調(diào)用 gl.enableVertexAttribArray(a_Position),表示 a_Position 變量對(duì)應(yīng)的緩沖區(qū)被開(kāi)啟了。開(kāi)啟后,我們就不能再用原來(lái)的 gl.vertexAttrib3f 來(lái)傳遞數(shù)據(jù)了,WebGL 會(huì)從緩存區(qū)一個(gè)個(gè)拿。如果你想關(guān)閉分配,可以調(diào)用
繪制
最后是清空畫(huà)布,然后繪制三角形。
這里 gl.drawArrays 方法的第一個(gè) mode 參數(shù)換成了 gl.TRIANGLES(三角形圖元),不再是原來(lái)的 gl.POINTS。
繪制效果:
如果用原來(lái)的 gl.POINTS,并設(shè)置好 gl_PointSize 指定點(diǎn)大小,則會(huì)繪制出下面的效果:
代碼
下面是完整的代碼實(shí)現(xiàn)。
在線 demo:
https://codesandbox.io/s/gbh1xf?file=/index.js。