屬性
屬性是全域性狀態(*)。如果它們是用 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
)。