圖元
WebGL可以繪制非常復雜的3D模型,這些模型都是由下面3種基本幾何圖元構成的,下面我們來詳細的看看。
三角形
WebGL中任何復雜的模型,都是由三角形組合而成的,可以說三角形是任意形狀的最小構成單位。
WebGL可以繪制下面幾種三角形:
獨立的三角形(gl.TRIANGLES)
指定3*n個任意點,可以任意繪制的三角形,優點是沒有限制,缺點是數據量大;
三角形帶(gl.TRIANGLE_STRIP)
經過指定的規則,繪制連在一起的三角形,比如4個點就可以繪制2個三角形了,優點是數據量小,缺點是頂點順序要按照指定的順序來;
三角扇(gl.TRIANGLE_FAN)
類型三角形帶,是另外一套繪制規則;
直線
雖然,3D中的大部分圖形都是由三角形組成的,但是當我們需要繪制如線框之類的純線段時,就需要使用到直線這種圖元了。
同樣,直線的繪制也有幾種方式:
獨立線(gl.LINES)
指定2*n個任意點,可以任意的繪制線段;
線帶(gl.LINE_STRIP)
傳入的點,是連續的,比如傳入3個點就會繪制2條線段;
線環(gl.LINE_LOOP)
類似線帶,最后一個點和第一個點會繪制直線;
點(gl.POINTS)
需要提前設定點的大小,在WebGL中,點經常用來實現粒子效果,比如模擬火焰煙霧等。
三角形頂點順序和背面剔除
我們需要確定一個三角形三個點的順序,順序有兩種順時針(gl.CW)和逆時針(gl.CCW);
當我們確定這個順序之后,就可以知道一個三角形面,是正對着我們還是背對着我們了,比如當我們確定順時針的頂點的三角形是正面時,當有三角形在繪制時其頂點時逆時針的時候,這些三角形就是背向我們的了,背向我們的三角形,一般來說都是看不見的,為了提高運行效率可以不進行繪制,這就是背面剔除技術。
1 // 設定逆時針頂點順序為正面 2 gl.frontFace(gl.CCW); 3 // 開啟背面剔除 4 gl.enable(gl.CULL_FACE); 5 // 剔除背面 6 gl.cullFace(gl.BACK);
繪圖方法
gl.clear
該方法會清除整個顏色緩沖,並填充顏色為 gl.clearColor 設定的顏色;
gl.drawArrays
通過前面設定好的頂點來繪制圖像(可以指定要繪制那種圖元);
gl.drawElements
和 drawArrays 基本一至,不同的地方是,這個方法支持傳入索引緩沖,通過索引緩沖,可以用更少的頂點來繪制圖像;
類型化數組
由於js中並沒有指定位數的數字,所有的數字使用的類型都是64位(整形和浮點型都包括),而在WebGL中,需要操作二進制數據,js天生是不支持二進制數據的操作的(以前有人通過字符串的操作來實現,但是效率極低),所以特別引入了類型化數組來操作二進制數據,解決這個問題:
- ArrayBuffer:作為內存區域,可以存放多種類型的數據。不同數據有不同的存儲方式,這就叫做“視圖”。
目前,JavaScript提供以下類型的視圖:
- Int8Array:8位有符號整數,長度1個字節。
- Uint8Array:8位無符號整數,長度1個字節。
- Uint8ClampedArray:8位無符號整數,長度1個字節。(和Uint8Array的區別在於處理不在[0-255]范圍內的數字不一樣,詳情查看:https://blog.csdn.net/cuixiping/article/details/42270561)
- Int16Array:16位有符號整數,長度2個字節。
- Uint16Array:16位無符號整數,長度2個字節。
- Int32Array:32位有符號整數,長度4個字節。
- Uint32Array:32位無符號整數,長度4個字節。
- Float32Array:32位浮點數,長度4個字節。
- Float64Array:64位浮點數,長度8個字節。
可以將ArrayBuffer看做二進制數據源,他本身是不能直接操作二進制數據的,而多個類型的Array則是可以操作該二進制數據源的視圖,具體使用方法如下:
1 // 創建 4 個字節大小的數組緩沖 2 var arrayBuffer = new ArrayBuffer(4); 3 // 創建 Int32 位的操作視圖 4 var int32View = new Int32Array(arrayBuffer); 5 // 給第一個數組元素賦值, 可以理解為在 0 號字節位置寫入一個 int32 類型的數字 6 int32View[0] = 10000000; 7 // 創建 Uint8 位的操作視圖 8 var unit8View = new Uint8Array(arrayBuffer); 9 // 打印后即可看到 Int32 位的 10000000 在 Uint8 的字節中看起來的樣子了 10 console.log(unit8View); // Uint8Array(4) [128, 150, 152, 0]
退化三角形
如果一個三角形的面積是0的話,這種三角形就被稱為退化三角形,WebGL遇到退化三角形時,會進行跳過不繪制。
通過這個特性,我們在繪制三角帶(正常情況繪制出來是必須連着的)時,可以在一個三角帶中插入一個或多個退化三角形來繪制多個相互之間不相連的圖像。
頂點變換緩存
為了減少傳遞到顯卡的頂點數量,我們會采取使用共享頂點的方式,比如一個矩形由兩個三角形組成,我們會傳遞4個頂點而不是6個,那么我們知道,我們的頂點會經過頂點着色器進行變換,這就意味着在渲染管線中,共享的頂點可能會經過多次的轉換,而除了第一次的轉換,后面的轉換都是多余的,平均緩存缺失率表示一個頂點被轉換的次數,下面是來自網絡的記錄:
ACMR(平均緩存缺失率):每個三角形所處理的平均頂點數量,該數據用來測量渲染性能。在典型的封閉網絡中,三角形數量約等於兩倍的頂點數量,理想的ACMR值為0.5,理想的取值范圍為0.5~1,最壞的情況是3;
歐拉多面體公式:V-E+F=2 V表示頂點,e表示邊,f表示面,優化ACMR的方法是對三角形中的頂點進行排序;
交叉存放頂點數據
頂點緩沖,除了存放頂點的坐標數據外,還可以同時存放顏色、法線、紋理坐標等數據;
使用常量頂點數組
我們之前提交的頂點緩沖,在頂點着色器中,會按照我們指定的順序處理每一個頂點,代碼如下:
1 // 將提交的頂點數據綁定到着色器的 aVertexPosition 屬性, 並設置每份數據的尺寸和數據類型 2 gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, vertexBuffer.itemSize, gl.FLOAT, false, 0, 0); 3 // 開啟屬性 aVertexPosition 作為數組的使用, 開啟之后,頂點着色器會按照每個頂點的尺寸來遍歷處理這些數據,即頂點着色器每次處理該屬性的值都會改變 4 gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
那么如果我希望是傳遞一個數據,該數據對於每一個頂點來說,都是不變的時候,可以用下面的方法來實現:
1 // 將一個 vec4 的數據直接綁定到 vertexColorAttribute 屬性 2 gl.vertexAttrib4f(shaderProgram.vertexColorAttribute, 0, 0, 0, 1); 3 // 關閉該屬性作為數組的使用,關閉之后,頂點着色器每次處理 vertexColorAttribute 屬性的值都不會改變 4 gl.disableVertexAttribArray(shaderProgram.vertexColorAttribute);
通過調用enableVertexAttribArray和disableVertexAttribArray就可以確定指定的屬性是否是常量數據;
示例
點擊查看示例1:交叉存放頂點數據;
https://hammerc.github.io/dou3d-ts/learning/learningNotes/lesson_2/index_1.html
點擊查看示例2:多種類型圖元繪制;
https://hammerc.github.io/dou3d-ts/learning/learningNotes/lesson_2/index_2.html