WebGL提供了一種很方便的機制,即緩沖區(qū)對象(buffer object),它可以一次性地向著色器傳入多個頂點的數(shù)據(jù)鸭廷。
緩沖區(qū)對象是WebGL系統(tǒng)中的一塊內(nèi)存區(qū)域,我們可以一次性地向緩沖區(qū)對象中填充大量的頂點數(shù)據(jù)熔吗,然后將這些數(shù)據(jù)保存在其中辆床,供頂點著色器使用。
使用緩沖區(qū)對象向頂點著色器傳入多個頂點的數(shù)據(jù)桅狠,需要遵循以下5個步驟:
- 創(chuàng)建緩沖區(qū)對象
gl.createBuffer()
- 綁定緩沖區(qū)對象
gl.bindBuffer()
- 將數(shù)據(jù)寫入緩沖區(qū)對象
gl.bufferData()
- 將緩沖區(qū)對象分配給一個
attribute
變量gl.vertexAttribPointer()
- 開始
attribute
變量gl.enableVertexAttribArray()
- 繪制三角形的3個頂點
// 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ū)對象
var vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
console.log('Failed to create the buffer object');
return -1;
}
// 將緩沖區(qū)對象綁定到目標
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 向緩沖區(qū)對象中寫入數(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ū)對象分配給a_Position變量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
// 連接a_Position變量與分配給它的緩沖區(qū)對象
gl.enableVertexAttribArray(a_Position);
return n;
}
- 創(chuàng)建緩沖區(qū)對象
gl.createBuffer()
執(zhí)行該方法的結(jié)果就是讼载,WebGL系統(tǒng)中多了一個新創(chuàng)建出來的緩沖區(qū)對象轿秧。返回值為null表示創(chuàng)建失敗。
相應地维雇,
gl.deleteBuffer(buffer)
函數(shù)可以用來刪除被gl.createBuffer()
創(chuàng)建出來的緩沖區(qū)對象
- 綁定緩沖區(qū)
gl.bindBuffer(target,buffer)
創(chuàng)建緩沖區(qū)的第二步就是將緩沖區(qū)對象綁定到WebGL系統(tǒng)中已經(jīng)存在的“目標”上塔逃。這個“目標”表示緩沖區(qū)對象的用途详拙。允許使用buffer表示的緩沖區(qū)對象綁定到target表示的目標上。
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
其中,
gl.ARRAY_BUFFER
表示緩沖區(qū)對象中包含了頂點的數(shù)據(jù)
- 向緩沖區(qū)對象中寫入數(shù)據(jù)
gl.bufferData(target,data,usage)
第三步变擒,開辟空間并向緩沖區(qū)中寫入數(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個參數(shù)vertices中的數(shù)據(jù)寫入綁定到第1個參數(shù)
gl.ARRAY_BUFFER
上的緩沖區(qū)對象锌唾。我們不能直接向緩沖區(qū)寫入數(shù)據(jù),而只能向"目標"寫入數(shù)據(jù)触徐,所以要向緩沖區(qū)寫數(shù)據(jù)咪鲜,必須先綁定。
其中撞鹉,參數(shù)usage表示程序?qū)⑷绾问褂么鎯υ诰彌_區(qū)對象中的數(shù)據(jù)疟丙。該參數(shù)將幫助WebGL優(yōu)化操作,但即便傳入了錯誤的值鸟雏,也不會終止程序(僅僅是降低程序的效率)
上面享郊,我們使用了Float32Array對象,而不是JS更常見的Array對象孝鹊。這是因為炊琉,JS中的數(shù)組Array是一種通用的類型,既可以存儲數(shù)字也可以存儲字符串又活,而并沒有對“大量元素都是同一種類型”優(yōu)化苔咪。為了解決這個問題,WebGL引入了類型化的數(shù)組柳骄,F(xiàn)loat32Array就是其中之一团赏。
- WebGL使用的各種類型化數(shù)組
數(shù)組類型 | 每個元素所點字節(jié)數(shù) | 描述(C語言中的數(shù)據(jù)類型) |
---|---|---|
Int8Array | 1 | 8位整型(singed char) |
UInt8Array | 1 | 8位無符號整型(unsinged char) |
Int16Array | 2 | 16位整型(singed short) |
UInt16Array | 2 | 16位無符號整型(unsinged short) |
Int32Array | 4 | 32位整型(singed int) |
UInt32Array | 4 | 32位無符號整型(unsinged int) |
Float32Array | 4 | 單精度32位浮點數(shù)(float) |
Float64Array | 8 | 雙精度64位浮點數(shù)(double) |
注意: 與普通的Array數(shù)組不同,類型化數(shù)組不支持push()和pop()方法;創(chuàng)建類型化數(shù)組的唯一方法就是使用new運算符夹界,不能使用[]運算符馆里。
- 類型化數(shù)組的方法、屬性和常量
方法可柿、屬性和常量 | 描述 |
---|---|
get(index) | 獲取第index個元素值 |
set(index,value) | 設置第index個元素的值為value |
set(array,offset) | 從第offset個元素開始將數(shù)組array中的值填充進去 |
length | 數(shù)組的長度 |
BYTES_PER_ELEMENT | 數(shù)組中每個元素所占的字節(jié)數(shù) |
- 將緩沖區(qū)對象分配給attribute變量
gl.vertexAttribPointer(location,size,type,normalized,stride,offset)
將綁定到
gl.ARRAY_BUFFER
的緩沖區(qū)對象分配給由location指定的attribute變量鸠踪。
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
參數(shù)location指定待分配attribute變量的存儲位置;
size指定緩沖區(qū)中每個頂點的分量個數(shù)(1到4)复斥,若size比attribute變量需要的分量數(shù)小营密,缺失分量將按照與
gl.vertexAttrib[1234]f()
相同的規(guī)則補全。(比如:size為1目锭,那么第2评汰、3分量自動設為0纷捞,第4分量為1)type為數(shù)據(jù)類型
normalize傳入true或false,表明是否將非浮點型的數(shù)據(jù)歸一化到[0,1]或[1,1]區(qū)間
stride指定相鄰兩個頂點間的字節(jié)數(shù)被去,默認為0
offset指定緩沖區(qū)對象中的偏移量
- 開啟attribute變量
gl.enableVertexAttribArray()
gl.enableVertexAttribArray(a_Position)
注意:雖然函數(shù)的名稱似乎表示該函數(shù)是用來處理“頂點數(shù)組”的主儡,但實際上它處理的對象是緩沖區(qū)。這是由于歷史原因(從OpenGL中繼承)造成的惨缆。
開啟attribute變量后糜值,就不能再用gl.vertexAttrib[1234]f()向它傳數(shù)據(jù)了,除非你顯示地關閉該attribute變量坯墨。實際上寂汇,你無法(也不應該)同時使用這兩個函數(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指定從哪個頂點開始繪制(整數(shù))
count 指定繪制需要用到多少個頂點(整數(shù))
gl.drawArrays(gl.POINTS,0,n) //n為3
實際上臼膏,頂點著色器執(zhí)行了n(3)次,我們通過存儲在緩沖區(qū)中的頂點坐標數(shù)據(jù)被依次傳給attribute變量示损。
- 連接三個頂點,填充三角形
基于上面頂點程序的改動有兩處:
1.在頂點著色器中嚷硫,去掉指定點的尺寸
gl_PointSize = 10.0;
,該語句只有在繪制單個點的時候才起作用检访。2.
gl.drawArrays()
方法的第1個參數(shù)從gl.POINTS
被改為了gl.TRINGLES
,就相當于告訴WebGL仔掸,從緩沖區(qū)中的第1個頂點開始脆贵,使頂點著色器執(zhí)行3次(n為3),用這3個點繪制出一個三角形起暮。
- WebGL可以繪制的基本圖形
- 對應效果
WebGL有點上頭卖氨,暫時告一段落……