屬性

屬性是全域性狀態(*)。如果它們是用 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。所以,你必須問它你用於 positiontexcoordnormal 的屬性是什麼?。你可以通過撥打 gl.getAttribLocation 來做到這一點

var positionLoc = gl.getAttribLocation(program, "position");
var texcoordLoc = gl.getAttribLocation(program, "texcoord");
var normalLoc = gl.getAttribLocation(program, "normal");

讓我們說 positionLoc = 5。這意味著當頂點著色器執行時(當你呼叫 gl.drawArraysgl.drawElements 時),頂點著色器希望你具有正確的 typesizeoffsetstridebuffer 等的 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 功能設定該值。當 enablefalse 時使用 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)。