WebGL提供了一種很方便的機(jī)制,即緩沖區(qū)對(duì)象(buffer object),它可以一次性地向著色器傳入多個(gè)頂點(diǎn)的數(shù)據(jù)。
緩沖區(qū)對(duì)象是WebGL系統(tǒng)中的一塊內(nèi)存區(qū)域,我們可以一次性地向緩沖區(qū)對(duì)象中填充大量的頂點(diǎn)數(shù)據(jù),然后將這些數(shù)據(jù)保存在其中,供頂點(diǎn)著色器使用。
使用緩沖區(qū)對(duì)象向頂點(diǎn)著色器傳入多個(gè)頂點(diǎn)的數(shù)據(jù),需要遵循以下5個(gè)步驟:
- 創(chuàng)建緩沖區(qū)對(duì)象
gl.createBuffer()- 綁定緩沖區(qū)對(duì)象
gl.bindBuffer()- 將數(shù)據(jù)寫(xiě)入緩沖區(qū)對(duì)象
gl.bufferData()- 將緩沖區(qū)對(duì)象分配給一個(gè)
attribute變量gl.vertexAttribPointer()- 開(kāi)始
attribute變量gl.enableVertexAttribArray()
- 繪制三角形的3個(gè)頂點(diǎn)
// Vertex shader program
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'void main() {\n' +
' gl_Position = a_Position;\n' +
' gl_PointSize = 10.0;\n' +
'}\n';
// Fragment shader program
var FSHADER_SOURCE =
'void main() {\n' +
' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' +
'}\n';
function main() {
// Retrieve <canvas> element
var canvas = document.getElementById('webgl');
// Get the rendering context for WebGL
var gl = getWebGLContext(canvas);
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
// Initialize shaders
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to intialize shaders.');
return;
}
// Write the positions of vertices to a vertex shader
var n = initVertexBuffers(gl);
if (n < 0) {
console.log('Failed to set the positions of the vertices');
return;
}
// Specify the color for clearing <canvas>
gl.clearColor(0, 0, 0, 1);
// Clear <canvas>
gl.clear(gl.COLOR_BUFFER_BIT);
// Draw three points
gl.drawArrays(gl.POINTS, 0, n);
}
function initVertexBuffers(gl) {
var vertices = new Float32Array([
0.0, 0.5, -0.5, -0.5, 0.5, -0.5
]);
var n = 3; // The number of vertices
// 創(chuàng)建緩沖區(qū)對(duì)象
var vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
console.log('Failed to create the buffer object');
return -1;
}
// 將緩沖區(qū)對(duì)象綁定到目標(biāo)
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 向緩沖區(qū)對(duì)象中寫(xiě)入數(shù)據(jù)
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return -1;
}
// 將緩沖區(qū)對(duì)象分配給a_Position變量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
// 連接a_Position變量與分配給它的緩沖區(qū)對(duì)象
gl.enableVertexAttribArray(a_Position);
return n;
}
- 創(chuàng)建緩沖區(qū)對(duì)象
gl.createBuffer()
執(zhí)行該方法的結(jié)果就是,WebGL系統(tǒng)中多了一個(gè)新創(chuàng)建出來(lái)的緩沖區(qū)對(duì)象。返回值為null表示創(chuàng)建失敗。
相應(yīng)地,
gl.deleteBuffer(buffer)函數(shù)可以用來(lái)刪除被gl.createBuffer()創(chuàng)建出來(lái)的緩沖區(qū)對(duì)象
- 綁定緩沖區(qū)
gl.bindBuffer(target,buffer)
創(chuàng)建緩沖區(qū)的第二步就是將緩沖區(qū)對(duì)象綁定到WebGL系統(tǒng)中已經(jīng)存在的“目標(biāo)”上。這個(gè)“目標(biāo)”表示緩沖區(qū)對(duì)象的用途。允許使用buffer表示的緩沖區(qū)對(duì)象綁定到target表示的目標(biāo)上。
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
其中,
gl.ARRAY_BUFFER表示緩沖區(qū)對(duì)象中包含了頂點(diǎn)的數(shù)據(jù)
- 向緩沖區(qū)對(duì)象中寫(xiě)入數(shù)據(jù)
gl.bufferData(target,data,usage)
第三步,開(kāi)辟空間并向緩沖區(qū)中寫(xiě)入數(shù)據(jù)。
var vertices = new Float32Array([
0.0,0.5,-0.5,-0.5,0.5,0.5
])
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)
該方法的效果是,將第2個(gè)參數(shù)vertices中的數(shù)據(jù)寫(xiě)入綁定到第1個(gè)參數(shù)
gl.ARRAY_BUFFER上的緩沖區(qū)對(duì)象。我們不能直接向緩沖區(qū)寫(xiě)入數(shù)據(jù),而只能向"目標(biāo)"寫(xiě)入數(shù)據(jù),所以要向緩沖區(qū)寫(xiě)數(shù)據(jù),必須先綁定。
其中,參數(shù)usage表示程序?qū)⑷绾问褂么鎯?chǔ)在緩沖區(qū)對(duì)象中的數(shù)據(jù)。該參數(shù)將幫助WebGL優(yōu)化操作,但即便傳入了錯(cuò)誤的值,也不會(huì)終止程序(僅僅是降低程序的效率)
上面,我們使用了Float32Array對(duì)象,而不是JS更常見(jiàn)的Array對(duì)象。這是因?yàn)?,JS中的數(shù)組Array是一種通用的類(lèi)型,既可以存儲(chǔ)數(shù)字也可以存儲(chǔ)字符串,而并沒(méi)有對(duì)“大量元素都是同一種類(lèi)型”優(yōu)化。為了解決這個(gè)問(wèn)題,WebGL引入了類(lèi)型化的數(shù)組,F(xiàn)loat32Array就是其中之一。
- WebGL使用的各種類(lèi)型化數(shù)組
| 數(shù)組類(lèi)型 | 每個(gè)元素所點(diǎn)字節(jié)數(shù) | 描述(C語(yǔ)言中的數(shù)據(jù)類(lèi)型) |
|---|---|---|
| Int8Array | 1 | 8位整型(singed char) |
| UInt8Array | 1 | 8位無(wú)符號(hào)整型(unsinged char) |
| Int16Array | 2 | 16位整型(singed short) |
| UInt16Array | 2 | 16位無(wú)符號(hào)整型(unsinged short) |
| Int32Array | 4 | 32位整型(singed int) |
| UInt32Array | 4 | 32位無(wú)符號(hào)整型(unsinged int) |
| Float32Array | 4 | 單精度32位浮點(diǎn)數(shù)(float) |
| Float64Array | 8 | 雙精度64位浮點(diǎn)數(shù)(double) |
注意: 與普通的Array數(shù)組不同,類(lèi)型化數(shù)組不支持push()和pop()方法;創(chuàng)建類(lèi)型化數(shù)組的唯一方法就是使用new運(yùn)算符,不能使用[]運(yùn)算符。
- 類(lèi)型化數(shù)組的方法、屬性和常量
| 方法、屬性和常量 | 描述 |
|---|---|
| get(index) | 獲取第index個(gè)元素值 |
| set(index,value) | 設(shè)置第index個(gè)元素的值為value |
| set(array,offset) | 從第offset個(gè)元素開(kāi)始將數(shù)組array中的值填充進(jìn)去 |
| length | 數(shù)組的長(zhǎng)度 |
| BYTES_PER_ELEMENT | 數(shù)組中每個(gè)元素所占的字節(jié)數(shù) |
- 將緩沖區(qū)對(duì)象分配給attribute變量
gl.vertexAttribPointer(location,size,type,normalized,stride,offset)
將綁定到
gl.ARRAY_BUFFER的緩沖區(qū)對(duì)象分配給由location指定的attribute變量。
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
參數(shù)location指定待分配attribute變量的存儲(chǔ)位置;
size指定緩沖區(qū)中每個(gè)頂點(diǎn)的分量個(gè)數(shù)(1到4),若size比attribute變量需要的分量數(shù)小,缺失分量將按照與
gl.vertexAttrib[1234]f()相同的規(guī)則補(bǔ)全。(比如:size為1,那么第2、3分量自動(dòng)設(shè)為0,第4分量為1)type為數(shù)據(jù)類(lèi)型
normalize傳入true或false,表明是否將非浮點(diǎn)型的數(shù)據(jù)歸一化到[0,1]或[1,1]區(qū)間
stride指定相鄰兩個(gè)頂點(diǎn)間的字節(jié)數(shù),默認(rèn)為0
offset指定緩沖區(qū)對(duì)象中的偏移量
- 開(kāi)啟attribute變量
gl.enableVertexAttribArray()
gl.enableVertexAttribArray(a_Position)
注意:雖然函數(shù)的名稱(chēng)似乎表示該函數(shù)是用來(lái)處理“頂點(diǎn)數(shù)組”的,但實(shí)際上它處理的對(duì)象是緩沖區(qū)。這是由于歷史原因(從OpenGL中繼承)造成的。
開(kāi)啟attribute變量后,就不能再用gl.vertexAttrib[1234]f()向它傳數(shù)據(jù)了,除非你顯示地關(guān)閉該attribute變量。實(shí)際上,你無(wú)法(也不應(yīng)該)同時(shí)使用這兩個(gè)函數(shù)。
gl.drawArrays(mode,first,count)
mode指定繪制的方式,可接收:gl.POINTS、gl.LINES、gl.LINES_STRIP、gl.LINE_LOOP、gl.TRIANGLES、gl.TRIANGLE_STRIP、gl.TRINGLE_FAN
first指定從哪個(gè)頂點(diǎn)開(kāi)始繪制(整數(shù))
count 指定繪制需要用到多少個(gè)頂點(diǎn)(整數(shù))
gl.drawArrays(gl.POINTS,0,n) //n為3
實(shí)際上,頂點(diǎn)著色器執(zhí)行了n(3)次,我們通過(guò)存儲(chǔ)在緩沖區(qū)中的頂點(diǎn)坐標(biāo)數(shù)據(jù)被依次傳給attribute變量。
- 連接三個(gè)頂點(diǎn),填充三角形
基于上面頂點(diǎn)程序的改動(dòng)有兩處:
1.在頂點(diǎn)著色器中,去掉指定點(diǎn)的尺寸
gl_PointSize = 10.0;,該語(yǔ)句只有在繪制單個(gè)點(diǎn)的時(shí)候才起作用。2.
gl.drawArrays()方法的第1個(gè)參數(shù)從gl.POINTS被改為了gl.TRINGLES,就相當(dāng)于告訴WebGL,從緩沖區(qū)中的第1個(gè)頂點(diǎn)開(kāi)始,使頂點(diǎn)著色器執(zhí)行3次(n為3),用這3個(gè)點(diǎn)繪制出一個(gè)三角形。
- WebGL可以繪制的基本圖形

- 對(duì)應(yīng)效果

WebGL有點(diǎn)上頭,暫時(shí)告一段落……