代码之家  ›  专栏  ›  技术社区  ›  Grumdrig

调用glDisableVertexAttribArray()是否重要?

  •  62
  • Grumdrig  · 技术社区  · 14 年前

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

    现在,当我创建着色器程序时,我正在启用顶点属性数组,而从不禁用它们,所有这些似乎都能工作,但似乎我应该在绘制调用之前/之后立即启用/禁用它们。这有影响吗?

    (碰巧我在WebGL,所以我们真的在谈论 gl.enableVertexAttribArray gl.disableVertexAttribArray 。我还要注意的是,橙色的书, OpenGL着色语言 ,对这些电话一无所知。)

    4 回复  |  直到 5 年前
        1
  •  48
  •   datenwolf    14 年前

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

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

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

        2
  •  15
  •   gman    5 年前

    WebGL与OpenGL不同。

    在WebGL中,只要有一个附加到属性的缓冲区,并且(a)如果使用了缓冲区,它的大小足以满足draw调用,或者(b)没有使用缓冲区,就明确允许启用数组。

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

    证明:

    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>

    另一个例子是,只需启用所有属性,然后使用不使用属性的着色器进行绘制(无错误),还可以使用使用1个属性的着色器(同样无错误)进行绘制,无需调用 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');
    画布{边框:1px纯黑;}
    <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>
        3
  •  2
  •   Adria    12 年前

    对于webGL,我将采用 ,调用gl.disableVertexAttribArray很重要。

    Chrome向我发出警告:

    WebGL: INVALID_OPERATION: drawElements: attribs not setup correctly
    

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

    如果所有程序都使用相同数量的属性,那么在初始化时调用gl.enableVertexAttribArray一次就可以了。否则,您需要在更改程序时对其进行管理。

        4
  •  -2
  •   Arijit Nandi    5 年前

    可以将其视为属性是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.vertexAttripPointer(location,…)时,您不会更改着色器程序中属性的任何特性(它们只是有一个属性编号,该编号指的是VAO中用于访问数据的属性槽之一)。实际执行的操作是使用VAO中的位置编号修改属性窗的状态。因此,为了使程序中的属性能够访问数据,必须启用VAO中相应的属性槽(gl.enableVertexAttribArray())。我们必须配置属性槽,以便它能够正确地从绑定到ARRAY_buffer的缓冲区读取数据(gl.vertexAttripPointer()),即使我们将其从目标解除绑定,只要它在GPU存储器中,属性槽csn仍然是VBO的红色。此外,必须有一些缓冲区绑定到VAO的目标(gl.bindBuffer())。因此,gl.enableVertexAttribArray(location)将启用当前VAO中由“location”指定的属性槽。gl.disableVertexAttribArray(location)将禁用它。不过它与着色器程序无关。即使使用不同的着色器程序,这些属性槽的状态也不会受到影响。

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

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

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

    推荐文章