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