调用glDisableVertexAttribArray()很重要吗?

时间:2012-09-14 15:47:12

标签: opengl-es webgl vertex-shader

我对启用顶点属性数组的范围并不完全清楚。我有几个不同的着色器程序,具有不同数量的顶点属性。 glEnableVertexAttribArray是否为着色器程序本地调用,还是全局调用?

现在我在创建着色器程序时启用顶点属性数组,并且从不禁用它们,并且一切似乎都有效,但似乎我可能应该在绘制调用之前/之后启用/禁用它们。对此有影响吗?

(我正在使用WebGL,所以我们真的在谈论gl.enableVertexAttribArraygl.disableVertexAttribArray。我还会注意到橙皮书, OpenGL着色语言< / em>,对于这些调用没有任何信息。)

4 个答案:

答案 0 :(得分:40)

启用顶点属性数组的状态可以绑定到顶点数组对象(VAO),也可以是全局的。

如果你正在使用VAO,那么你应该禁用属性数组,因为它们被封装在VAO中。

然而,对于全局顶点属性数组启用状态,您应该禁用它们,因为如果它们保持启用状态,OpenGL将尝试从数组中读取,这些数组可能绑定到无效指针,如果指针是指针,则可能会使程序崩溃到客户端地址空间,或者如果它指出了绑定的顶点缓冲区对象的限制,则引发OpenGL错误。

答案 1 :(得分:13)

WebGL与OpenGL不同。

在WebGL中,只要存在附加到属性的缓冲区,并且(a)如果使用它足够大以满足绘制调用或(b)它未被使用,则显式允许启用数组。

与OpenGL ES 2.0不同,WebGL不允许客户端阵列。

证明:

var gl = document.querySelector("canvas").getContext("webgl");
var m4 = twgl.m4;
var programInfo2Attribs = twgl.createProgramInfo(gl, ["vs-uses-2-attributes", "fs"]);
var programInfo1Attrib  = twgl.createProgramInfo(gl, ["vs-uses-1-attribute", "fs"]);

var arrays2Attribs = {
  position: [
    -1, -1, 0, 
    1, -1, 0, 
    -1, 1, 0, 
  ],
  color: [
    1,0,0,1,
    1,1,0,1,
    0,1,0,1,
  ],
};

var arrays1Attrib = {
  position: [
    -1, -1, 0, 
    1, -1, 0, 
    -1, 1, 0, 
    -1, 1, 0, 
    1, -1, 0, 
    1, 1, 0,
  ],
};


var bufferInfo2Attribs = twgl.createBufferInfoFromArrays(gl, arrays2Attribs);
var bufferInfo1Attrib  = twgl.createBufferInfoFromArrays(gl, arrays1Attrib);

var uniforms = {
  u_matrix: m4.scale(m4.translation([-0.5, 0, 0]), [0.25, 0.5, 0.5]),
};

gl.useProgram(programInfo2Attribs.program);
twgl.setBuffersAndAttributes(gl, programInfo2Attribs, bufferInfo2Attribs);
twgl.setUniforms(programInfo2Attribs, uniforms);
twgl.drawBufferInfo(gl, gl.TRIANGLES, bufferInfo2Attribs);

uniforms.u_matrix = m4.scale(m4.translation([0.5, 0, 0]), [0.25, 0.5, 0.5]);

gl.useProgram(programInfo1Attrib.program);
twgl.setBuffersAndAttributes(gl, programInfo1Attrib, bufferInfo1Attrib);
twgl.setUniforms(programInfo1Attrib, uniforms);
twgl.drawBufferInfo(gl, gl.TRIANGLES, bufferInfo1Attrib);
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/twgl-full.min.js"></script>
<script id="vs-uses-2-attributes" type="not-js">
attribute vec4 position;
attribute vec4 color;
  
varying vec4 v_color;

uniform mat4 u_matrix;
  
void main() {
  gl_Position = u_matrix * position;
  v_color = color;
}
</script>
<script id="vs-uses-1-attribute" type="not-js">
attribute vec4 position;
  
varying vec4 v_color;
  
uniform mat4 u_matrix;
  
void main() {
  gl_Position = u_matrix * position;
  v_color = vec4(0,1,0,1);
}
</script>
<script id="fs" type="not-js">
precision mediump float;
varying vec4 v_color;

void main() {
  gl_FragColor = v_color;
}
</script>
<p>
1st it draws a triangle (3 vertices, 2 attributes)<br/>
2nd it draws a quad (6 vertices, 1 attribute)<br/>
It does NOT called gl.disableVertexAttrib so on the second draw call one of the attributes is still enabled. It is pointing to a buffer with only 3 vertices in it even though 6 vertices will be drawn. There are no errors.
</p>
<canvas></canvas>

答案 2 :(得分:4)

对于webGL,我将选择,调用gl.disableVertexAttribArray非常重要。

Chrome正在给我这个警告:

WebGL: INVALID_OPERATION: drawElements: attribs not setup correctly

当程序使用少于最大属性数量更改为1时,会发生这种情况。显然,解决方案是在绘制之前禁用未使用的属性。

如果所有程序都使用相同数量的属性,那么在初始化时可能会调用gl.enableVertexAttribArray一次。否则,您需要在更改程序时管理它们。

答案 3 :(得分:0)

将其视为属性是VAO而不是着色器程序的本地属性。 VBO位于GPU内存中。

现在考虑一下,在WebGL中,WebGL默认使用默认的VAO。 (它也可以是程序员创建的VAO,适用相同的概念)。此VAO包含一个名为ARRAY_BUFFER的目标,GPU内存中的任何VBO都可以绑定到该目标。此VAO还包含具有固定数量的属性插槽的属性数组(数量取决于实现和平台,在此可以说8,这是Webgl规范要求的最小值)。另外,该VAO具有ELEMENT_ARRAY_BUFFER目标,可以将任何索引数据缓冲区绑定到该目标。

现在,当您创建着色器程序时,它将具有您指定的属性。当链接着色器程序时,Webgl会将可能的属性插槽“数字”之一分配给程序中指定的所有属性。现在,属性将使用VAO中的相应属性插槽来访问绑定到VAO中的ARRAY_BUFFER或ELEMENT_ARRAY_BUFFER目标的数据。现在,当您使用函数gl.enableVertexAttribArray(location)和gl.vertexAttribPointer(location,....)时,您不会更改着色器程序中属性的任何特征(它们只是具有一个引用其中一个属性的属性编号)它们将用于访问数据的VAO中的属性槽)。您实际上正在做的是使用其位置号修改VAO中属性插槽的状态。因此,为了使程序中的属性能够访问数据,必须启用VAO中其相应的属性插槽(gl.enableVertexAttribArray())。并且我们必须配置属性插槽,以便它可以正确地从绑定到ARRAY_BUFFER的缓冲区中读取数据(gl.vertexAttribPointer())。一个VBO设置为不会更改的插槽,即使我们将其与目标解除绑定,只要插槽在GPU内存中,属性插槽csn仍会从VBO中变为红色。另外,必须有一些绑定到VAO目标的缓冲区(gl.bindBuffer())。因此gl.enableVertexAttribArray(location)将启用当前VAO中“ location”指定的属性插槽。 gl.disableVertexAttribArray(location)将禁用它。它与着色器程序无关。即使您使用其他着色器程序,这些属性插槽的状态也不会受到影响。

因此,如果两个不同的着色器程序使用相同的属性插槽,则不会有任何错误,因为VAO中的相应属性插槽已处于活动状态。但是如果需要属性,则在两个着色器程序中以不同的方式解释数据时,可能无法正确读取来自目标的数据。现在考虑,如果两个着色器程序使用不同的属性插槽,则可以启用第二个着色器程序的必需属性插槽,并认为您的程序应该可以工作。但是已经启用的属性插槽(由先前的着色器程序启用)仍将启用,但不会使用。这会导致错误。

因此,在更改着色器程序时,必须确保必须禁用该着色器程序不会使用的VAO中已启用的属性插槽。尽管我们现在可以明确指定任何VAO,但Webgl的默认工作方式是这样。

一种方法是在javascript端维护已启用属性的列表,并在仍使用相同VAO的情况下切换程序时禁用所有已启用属性的插槽。解决此问题的另一种方法是创建仅由一个着色器程序访问的自定义VAO。但效率较低。还有一种方法是在使用gl.bindAttribLocation()链接着色器程序之前,将属性位置绑定到固定插槽。