我应该什么时候在 WebGL/OpenGL 中启用/禁用顶点位置属性?

Jos*_*den 6 javascript webgl

我正在处理一些具有多个按顺序运行的着色器程序的 WebGL 代码。

以前,我gl.enableVertexAttribArray(...)在初始化期间根据需要使用我的 gl 上下文和着色器。我假设,也许是错误的,调用这个函数是设置特定于由gl.useProgram(...)

现在,我的第一个着色器程序有两个启用的属性数组,我的第二个启用了一个。当第二个程序运行时,出现错误:

Error: WebGL: drawArrays: no VBO bound to enabled vertex attrib index 1!
Run Code Online (Sandbox Code Playgroud)

所以这让我认为也许我需要在第一个程序中使用它后禁用顶点属性 1,但我想验证这就是我应该这样做的方式,并希望得到解释为什么会这样正确与否。

是最好的做法enableVertexAttribArray(...),并disableVertexAttribArray每一个在每次使用后阵列的位置?

gma*_*man 7

我从来没有打过电话disableVertexAttribArray,我已经编写了 100 多个 WebGL 程序。调用它可能有也可能没有任何性能优势,但不调用它没有兼容性问题。

规范说,只有当当前程序使用该属性并且访问超出范围或者没有缓冲区绑定到启用的属性时,您才会收到错误。

我们可以测试一下,看看它工作得很好。

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);
}
Run Code Online (Sandbox Code Playgroud)
canvas { border: 1px solid black; }
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

因此,您的错误可能是其他错误。

请注意,删除缓冲区会解除其与属性的绑定,此时它将成为没有缓冲区的已启用属性,并会导致错误,除非您禁用它。

属性状态与您发现的程序状态是分开的。

你的错误正是它所说的。您试图绘制,该程序需要属性 #1 上的数据。你在某个时候启用了它,gl.enableVertexAttribArray但你没有给它任何数据gl.vertexAttribPointer。所以你有一个错误。

请注意,gl.vertexAttribPointer将当前绑定到的缓冲区绑定到gl.ARRAY_BUFFER指定的属性。

你可能会发现这个答案很有用

/sf/answers/1901520421/


Jos*_*den 5

这个问答帮助我回答了我的问题:

使用两个或多个具有不同属性数量的着色器时发生冲突

为了使其正常工作,我需要在使用不使用未使用的属性的着色器程序进行绘制之前禁用它们。我无法解释为什么有些人说这没有必要,但这样做解决了我的问题。