屬性
屬性是全域性狀態(*)。如果它們是用 JavaScript 實現的,那麼它們看起來就像這樣
// pseudo code
gl = {
ARRAY_BUFFER: null,
vertexArray: {
attributes: [
{ enable: ?, type: ?, size: ?, normalize: ?, stride: ?, offset: ?, buffer: ? },
{ enable: ?, type: ?, size: ?, normalize: ?, stride: ?, offset: ?, buffer: ? },
{ enable: ?, type: ?, size: ?, normalize: ?, stride: ?, offset: ?, buffer: ? },
{ enable: ?, type: ?, size: ?, normalize: ?, stride: ?, offset: ?, buffer: ? },
{ enable: ?, type: ?, size: ?, normalize: ?, stride: ?, offset: ?, buffer: ? },
{ enable: ?, type: ?, size: ?, normalize: ?, stride: ?, offset: ?, buffer: ? },
{ enable: ?, type: ?, size: ?, normalize: ?, stride: ?, offset: ?, buffer: ? },
{ enable: ?, type: ?, size: ?, normalize: ?, stride: ?, offset: ?, buffer: ? },
],
ELEMENT_ARRAY_BUFFER: null,
},
}
如上所示,有 8 屬性,它們是全域性狀態。
當你呼叫 gl.enableVertexAttribArray(location) 或 gl.disableVertexAttribArray 時,你可以這樣想
// pseudo code
gl.enableVertexAttribArray = function(location) {
gl.vertexArray.attributes[location].enable = true;
};
gl.disableVertexAttribArray = function(location) {
gl.vertexArray.attributes[location].enable = false;
};
換句話說,location 直接引用屬性的索引。
同樣地,gl.vertexAttribPointer 將實現類似的東西
// pseudo code
gl.vertexAttribPointer = function(location, size, type, normalize, stride, offset) {
var attrib = gl.vertexArray.attributes[location];
attrib.size = size;
attrib.type = type;
attrib.normalize = normalize;
attrib.stride = stride ? stride : sizeof(type) * size;
attrib.offset = offset;
attrib.buffer = gl.ARRAY_BUFFER; // !!!! <-----
};
請注意,attrib.buffer 設定為當前 gl.ARRAY_BUFFER 設定的任何值。gl.ARRAY_BUFFER 是通過呼叫 gl.bindBuffer(gl.ARRAY_BUFFER, someBuffer) 設定的。
所以,接下來我們有頂點著色器。在頂點著色器中,你宣告屬性。例
attribute vec4 position;
attribute vec2 texcoord;
attribute vec3 normal;
...
void main() {
...
}
通過呼叫 gl.linkProgram(someProgram) 連結頂點著色器和片段著色器 WebGL(驅動程式/ GPU /瀏覽器)決定自己使用哪個索引/位置用於每個屬性。你不知道他們會選擇哪一個。這是瀏覽器/驅動程式/ GPU。所以,你必須問它你用於 position,texcoord 和 normal 的屬性是什麼?。你可以通過撥打 gl.getAttribLocation 來做到這一點
var positionLoc = gl.getAttribLocation(program, "position");
var texcoordLoc = gl.getAttribLocation(program, "texcoord");
var normalLoc = gl.getAttribLocation(program, "normal");
讓我們說 positionLoc = 5。這意味著當頂點著色器執行時(當你呼叫 gl.drawArrays 或 gl.drawElements 時),頂點著色器希望你具有正確的 type,size,offset,stride,buffer 等的 setup 屬性 5。
請注意,在連結程式之前,你可以通過呼叫 gl.bindAttribLoction(program, location, nameOfAttribute) 來選擇位置。例:
// Tell `gl.linkProgram` to assign `position` to use attribute #7
gl.bindAttribLocation(program, 7, "position");
完整的屬性狀態
缺少上述描述是每個屬性也有一個預設值。它被遺漏在上面,因為使用它並不常見。
attributes: [
{ enable: ?, type: ?, size: ?, normalize: ?, stride: ?, offset: ?, buffer: ?
value: [0, 0, 0, 1], },
{ enable: ?, type: ?, size: ?, normalize: ?, stride: ?, offset: ?, buffer: ?
value: [0, 0, 0, 1], },
..
你可以使用各種 gl.vertexAttribXXX 功能設定該值。當 enable 是 false 時使用 value。當 enable 為 true 時,屬性的資料將從指定的 buffer 中提取。
頂點陣列物件
WebGL 有一個副檔名 OES_vertex_array_object
在上圖中,OES_vertex_array_object 允許你建立和替換 vertexArray。換一種說法
var vao = ext.createVertexArrayOES();
在上面的虛擬碼中建立你看到附加到 gl.vertexArray 的物件。呼叫 ext.bindVertexArrayOES(vao) 將建立的頂點陣列物件指定為當前頂點陣列。
// pseudo code
ext.bindVertexArrayOES = function(vao) {
gl.vertexArray = vao;
}
這樣你就可以在當前的 VAO 中設定所有屬性和 ELEMENT_ARRAY_BUFFER,這樣當你想要繪製一個叫 ext.bindVertexArrayOES 的地方時,如果沒有副檔名,那麼每個屬性最多可以呼叫一次 gl.bindBuffer gl.vertexAttribPointer(可能還有 gl.enableVertexAttribArray)。