属性
属性是全局状态(*)。如果它们是用 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)。