最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - When should I enabledisable vertex position attributes in WebGLOpenGL? - Stack Overflow

programmeradmin7浏览0评论

I'm working on some WebGL code that has multiple shader programs that run sequentially.

Previously, I was using gl.enableVertexAttribArray(...) as required during initialization for my gl context and shaders. I assumed, perhaps incorrectly, that calling this function was setting state specific to the program selected by gl.useProgram(...)

Now, my first shader program has two enabled attribute arrays, and my second one has one enabled. When the second program runs, I get an error:

Error: WebGL: drawArrays: no VBO bound to enabled vertex attrib index 1!

So that leads me to think that maybe I need to be disabling vertex attribute 1 after using it in the first program, but I wanted to verify that this is how I'm supposed to be doing it, and hopefully get an explanation of why this is or isn't correct.

Is the best practice to enableVertexAttribArray(...) and disableVertexAttribArray for every array location before and after each use?

I'm working on some WebGL code that has multiple shader programs that run sequentially.

Previously, I was using gl.enableVertexAttribArray(...) as required during initialization for my gl context and shaders. I assumed, perhaps incorrectly, that calling this function was setting state specific to the program selected by gl.useProgram(...)

Now, my first shader program has two enabled attribute arrays, and my second one has one enabled. When the second program runs, I get an error:

Error: WebGL: drawArrays: no VBO bound to enabled vertex attrib index 1!

So that leads me to think that maybe I need to be disabling vertex attribute 1 after using it in the first program, but I wanted to verify that this is how I'm supposed to be doing it, and hopefully get an explanation of why this is or isn't correct.

Is the best practice to enableVertexAttribArray(...) and disableVertexAttribArray for every array location before and after each use?

Share Improve this question edited Mar 29, 2016 at 15:33 Nicol Bolas 474k64 gold badges832 silver badges1k bronze badges asked Mar 29, 2016 at 15:17 Joshua BreedenJoshua Breeden 1,9941 gold badge19 silver badges30 bronze badges 2
  • sounds like it expects it after you set the vertexAttribPointer – ratchet freak Commented Mar 29, 2016 at 15:24
  • Whoops... I described it incorrectly before. Edited now, but it's my first program with two attributes, and my second with one. And it doesn't like going to one after starting with two. – Joshua Breeden Commented Mar 29, 2016 at 15:26
Add a comment  | 

3 Answers 3

Reset to default 9

I've never called disableVertexAttribArray in my life and I've written 100s of WebGL programs. There may or may not be any perf benefits to calling it but there's no compatibility issues to not calling it.

The spec says you'll only get an error if the attribute is consumed by the current program and access would be out of range or if there's no buffer bound to an enabled attribute.

We can test that and see that it works just fine.

var vsThatUses2Attributes = `
  attribute vec4 position;
  attribute vec2 texcoord;
  
  varying vec2 v_texcoord;
  
  void main() {
    v_texcoord = texcoord;
    gl_Position = position;
  }
`;

var vsThatUses1Attribute = `
  attribute vec4 position;
  
  varying vec2 v_texcoord;
  
  void main() {
    v_texcoord = position.xy * 0.5 + 0.5;
    gl_Position = position + vec4(1, 0, 0, 0);
  }
`;

var fs = `
  precision mediump float;
  varying vec2 v_texcoord;
  
  void main () {
    gl_FragColor = vec4(v_texcoord, v_texcoord.x * v_texcoord.y, 1);
  }
`;

var gl = document.querySelector("canvas").getContext("webgl");
document.body.appendChild(gl.canvas);
var programThatUses2Attributes = twgl.createProgramFromSources(gl, [vsThatUses2Attributes, fs]);
var programThatUses1Attribute = twgl.createProgramFromSources(gl, [vsThatUses1Attribute, fs]);

var positionLocation2AttribProg = gl.getAttribLocation(programThatUses2Attributes, "position");
var texcoordLocation2AttribProg = gl.getAttribLocation(programThatUses2Attributes, "texcoord");

var positionLocation1AttribProg = gl.getAttribLocation(programThatUses1Attribute, "position");

var positionBufferFor2AttribPrg = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBufferFor2AttribPrg);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
  -1, -1,
 -.5,  1,
   0, -1,
]), gl.STATIC_DRAW);

var texcoordBufferFor2AttribPrg = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBufferFor2AttribPrg);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
  0, 0,
0.5, 1,
  1, 0,
]), gl.STATIC_DRAW);


var positionBufferFor1AttribPrg = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBufferFor1AttribPrg);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
  -1, -1,
   0, -1,
  -1,  1,
  -1,  1,
   0, -1,
   0,  1,
]), gl.STATIC_DRAW);


// turn on 2 attributes
gl.enableVertexAttribArray(positionLocation2AttribProg);
gl.enableVertexAttribArray(texcoordLocation2AttribProg);

gl.bindBuffer(gl.ARRAY_BUFFER, positionBufferFor2AttribPrg);
gl.vertexAttribPointer(positionLocation2AttribProg, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBufferFor2AttribPrg);
gl.vertexAttribPointer(texcoordLocation2AttribProg, 2, gl.FLOAT, false, 0, 0);

// draw with 2 attributes enabled
gl.useProgram(programThatUses2Attributes);
gl.drawArrays(gl.TRIANGLES, 0, 3);

// setup for second program that uses only 1 attribute
gl.bindBuffer(gl.ARRAY_BUFFER, positionBufferFor1AttribPrg);
gl.vertexAttribPointer(positionLocation1AttribProg, 2, gl.FLOAT, false, 0, 0);

// NOTICE WE HAVE !NOT turned off other attribute
gl.useProgram(programThatUses1Attribute);
gl.drawArrays(gl.TRIANGLES, 0, 6);

log("glError:", gl.getError());

function log() {
  var pre = document.createElement("pre");
  pre.appendChild(document.createTextNode(Array.prototype.join.call(arguments, " ")));
  document.body.appendChild(pre);
}
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/twgl.min.js"></script>
<pre>
This example draws a triangle with 3 vertices using 2 attributes.

It then draws a quad using 6 vertices and 1 attribute 
<b>WITHOUT TURNING OFF THE NOW 2nd UNUSED ATTRIBUTE</b>.

That means not only is that attribute left on but it only has 3 vertices even 
though the draw will use 6 vertices. Because that attribute is not 'comsumed' by 
the current program it's ok according to the spec.
</pre>
<canvas width="150" height="30"></canvas>

So, your error is likely something else.

Note that deleting a buffer will unbind it from the attribute, at which point it will be an enabled attribute with no buffer and cause an error unless you disable it.

Attribute state is separate from program state which you found out.

Your error means exactly what it says. You tried to draw, that program required data on attribute #1. You enabled it at some point with gl.enableVertexAttribArray but you didn't give it any data with gl.vertexAttribPointer. So you got an error.

Note that gl.vertexAttribPointer binds the buffer currently bound to gl.ARRAY_BUFFER to the specified attribute.

You might find this answer useful

https://stackoverflow.com/a/27164577/128511

This Q/A helped me answer my question:

Conflict when using two or more shaders with different number of attributes

In order to make it work correctly, I needed to disable unused attributes before drawing with a shader program that doesn't use them. I can't explain why some people say this shouldn't be necessary, but doing so solves my problem.

You'll never needs to call disableVertexAttribArray() in WebGL. I'll note why this method actually exists at first.

As you know, WebGL was just a ported library from OpenGLES2, and OpenGLES2 is a subset of OpenGL.
In the list of the API of OpenGL, there is some of the other way to pass vertex buffers to GPU without specifying vertex buffer as pointer. For instance, in OpenGL environment you can send vertex buffer data by calling gl.begin(), gl.Vertex(), gl.end() and so on.
In this way, you don't need to call gl.enableVertexAttribArray().

But there is no way to specify buffers without calling enableVertexAttribArray() in WebGL. Therefore, you never need to call the method, it is just a historical reason.

And you don't need to call enableVertexAttribArray() in each frame. It is very heavy operation since you should not call that each frame. You just needs to call enableVertexAttribArray() just after initializing buffer.

发布评论

评论列表(0)

  1. 暂无评论