Is it important to call glDisableVertexAttribArray()?
I'm not entirely clear on the scope of enabling vertex attrib arrays. I've got several different shader programs with differing numbers of vertex attributes. Are glEnableVertexAttribArray
calls local to a shader program, or global?
Right now I'm enabling vertex attrib arrays when I create the shader program, and never disabling them, and all seems to work, but it seems like I'm possibly supposed to enable/disable them right before/after draw calls. Is there an impact to this?
(I'm in WebGL, as it happens, so we're really talking about gl.enableVertexAttribArray
and gl.disableVertexAttribArray
. I'll note also that the orange book, OpenGL Shading Language, is quite uninformative about these calls.)
Solution 1:
The state of which Vertex Attribute Arrays are enabled can be either bound to a Vertex Array Object (VAO), or be global.
If you're using VAOs, then you should not disable attribute arrays, as they are encapsulated in the VAO.
However for global vertex attribute array enabled state you should disable them, because if they're left enabled OpenGL will try to read from arrays, which may be bound to a invalid pointer, which may either crash your program if the pointer is to client address space, or raise a OpenGL error if it points out of the limits of a bound Vertex Buffer Object.
Solution 2:
WebGL is not the same as OpenGL.
In WebGL leaving arrays enabled is explicitly allowed as long as there is a buffer attached to the attribute and (a) if it's used it's large enough to satisfy the draw call or (b) it's not used.
Unlike OpenGL ES 2.0, WebGL doesn't allow client side arrays.
Proof:
const gl = document.querySelector("canvas").getContext("webgl");
const vsUses2Attributes = `
attribute vec4 position;
attribute vec4 color;
varying vec4 v_color;
void main() {
gl_Position = position;
gl_PointSize = 20.0;
v_color = color;
}
`;
const vsUses1Attribute = `
attribute vec4 position;
varying vec4 v_color;
void main() {
gl_Position = position;
gl_PointSize = 20.0;
v_color = vec4(0,1,1,1);
}
`
const fs = `
precision mediump float;
varying vec4 v_color;
void main() {
gl_FragColor = v_color;
}
`;
const program2Attribs = twgl.createProgram(gl, [vsUses2Attributes, fs]);
const program1Attrib = twgl.createProgram(gl, [vsUses1Attribute, fs]);
function createBuffer(data) {
const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);
return buf;
}
const buffer3Points = createBuffer([
-0.7, 0.5,
0.0, 0.5,
0.7, 0.5,
]);
const buffer3Colors = createBuffer([
1, 0, 0, 1,
0, 1, 0, 1,
0, 0, 1, 1,
]);
const buffer9Points = createBuffer([
-0.8, -0.5,
-0.6, -0.5,
-0.4, -0.5,
-0.2, -0.5,
0.0, -0.5,
0.2, -0.5,
0.4, -0.5,
0.6, -0.5,
0.8, -0.5,
]);
// set up 2 attributes
{
const posLoc = gl.getAttribLocation(program2Attribs, 'position');
gl.enableVertexAttribArray(posLoc);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer3Points);
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
const colorLoc = gl.getAttribLocation(program2Attribs, 'color');
gl.enableVertexAttribArray(colorLoc);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer3Colors);
gl.vertexAttribPointer(colorLoc, 4, gl.FLOAT, false, 0, 0);
}
// draw
gl.useProgram(program2Attribs);
gl.drawArrays(gl.POINTS, 0, 3);
// setup 1 attribute (don't disable the second attribute
{
const posLoc = gl.getAttribLocation(program1Attrib, 'position');
gl.enableVertexAttribArray(posLoc);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer9Points);
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
}
// draw
gl.useProgram(program1Attrib);
gl.drawArrays(gl.POINTS, 0, 9);
const err = gl.getError();
console.log(err ? `ERROR: ${twgl.glEnumToString(gl, err)}` : 'no WebGL errors');
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<p>
1st it draws 3 points (3 vertices, 2 attributes)<br/>
2nd it draws 9 points (9 vertices, 1 attribute)<br/>
It does NOT call 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 9 vertices will be drawn. There are no errors.
</p>
<canvas></canvas>
Another example, just enable all the attributes, then draw with a shader that uses no attributes (no error) and also draw with a shader that uses 1 attribute (again no error), no need to call gl.disbleVertexAttribArray
const gl = document.querySelector("canvas").getContext("webgl");
const vsUses1Attributes = `
attribute vec4 position;
void main() {
gl_Position = position;
gl_PointSize = 20.0;
}
`;
const vsUses0Attributes = `
void main() {
gl_Position = vec4(0, 0, 0, 1);
gl_PointSize = 20.0;
}
`
const fs = `
precision mediump float;
void main() {
gl_FragColor = vec4(1, 0, 0, 1);
}
`;
const program0Attribs = twgl.createProgram(gl, [vsUses0Attributes, fs]);
const program1Attrib = twgl.createProgram(gl, [vsUses1Attributes, fs]);
function createBuffer(data) {
const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);
return buf;
}
const buffer3Points = createBuffer([
-0.7, 0.5,
0.0, 0.5,
0.7, 0.5,
]);
const buffer0Points = createBuffer([]);
// enable all the attributes and bind a buffer to them
const maxAttrib = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
for (let i = 0; i < maxAttrib; ++i) {
gl.enableVertexAttribArray(i);
gl.vertexAttribPointer(i, 2, gl.FLOAT, false, 0, 0);
}
gl.useProgram(program0Attribs);
gl.drawArrays(gl.POINTS, 0, 1);
gl.useProgram(program1Attrib);
const posLoc = gl.getAttribLocation(program1Attrib, 'position');
gl.bindBuffer(gl.ARRAY_BUFFER, buffer3Points);
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.POINTS, 0, 3);
const err = gl.getError();
console.log(err ? `ERROR: ${twgl.glEnumToString(gl, err)}` : 'no WebGL errors');
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<p>
1st it enables all attributes<br/>
2nd it draws 1 point that needs no attributes (no error)<br/>
3rd it draws 3 points that need 1 attribute (no error)<br/>
It does NOT call gl.disableVertexAttribArray on any of the attributes so they are all still enabled. There are no errors.
</p>
<canvas></canvas>