【问题标题】:Do we need rebind IBO after binding VAO during drawing?绘制过程中绑定VAO后是否需要重新绑定IBO?
【发布时间】:2019-06-24 11:35:46
【问题描述】:

你好,我一直在学习 webgl。

我一直在阅读这本名为 Real-Time 3D Graphics with WebGL 2 的书,这里说这个顶点数组对象允许我们将一组缓冲区的所有顶点/索引绑定信息存储在一个易于管理的对象中。

它为 VAO 提供了这个示例。

 function initBuffers() {
      /*
        V0                    V3
        (-0.5, 0.5, 0)        (0.5, 0.5, 0)
        X---------------------X
        |                     |
        |                     |
        |       (0, 0)        |
        |                     |
        |                     |
        X---------------------X
        V1                    V2
        (-0.5, -0.5, 0)       (0.5, -0.5, 0)
      */
      const vertices = [
        -0.5, 0.5, 0,
        -0.5, -0.5, 0,
        0.5, -0.5, 0,
        0.5, 0.5, 0
      ];

      // Indices defined in counter-clockwise order
      indices = [0, 1, 2, 0, 2, 3];

      // Create VAO instance
      squareVAO = gl.createVertexArray();

      // Bind it so we can work on it
      gl.bindVertexArray(squareVAO);

      const squareVertexBuffer = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexBuffer);
      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

      // Provide instructions for VAO to use data later in draw
      gl.enableVertexAttribArray(program.aVertexPosition);
      gl.vertexAttribPointer(program.aVertexPosition, 3, gl.FLOAT, false, 0, 0);

      // Setting up the IBO
      squareIndexBuffer = gl.createBuffer();
      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, squareIndexBuffer);
      gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);

      // Clean
      gl.bindVertexArray(null);
      gl.bindBuffer(gl.ARRAY_BUFFER, null);
      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
    }

    // We call draw to render to our canvas
    function draw() {
      // Clear the scene
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
      gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

      // Bind the VAO
      gl.bindVertexArray(squareVAO);

      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, squareIndexBuffer);

      // Draw to the scene using triangle primitives
      gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);

      // Clean
      gl.bindVertexArray(null);
      gl.bindBuffer(gl.ARRAY_BUFFER, null);
      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
    }

    // Entry point to our application
    function init() {
      // Retrieve the canvas
      const canvas = utils.getCanvas('webgl-canvas');

      // Set the canvas to the size of the screen
      canvas.width = window.innerWidth;
      canvas.height = window.innerHeight;

      // Retrieve a WebGL context
      gl = utils.getGLContext(canvas);
      // Set the clear color to be black
      gl.clearColor(0, 0, 0, 1);

      // Call the functions in an appropriate order
      initProgram();
      initBuffers();
      draw();
    }

这里的问题是,我们在draw()中绑定VAO后是否需要gl.bindBuffer();

我查看了这个链接What are Vertex Arrays in OpenGL & WebGL2?,上面写着 At draw time it then only takes one call to gl.bindVertexArray to setup all the attributes and the ELEMENT_ARRAY_BUFFER。所以我想在我们将VAO绑定到draw()之后就不需要gl.bindBuffer();了吗? 教科书上的代码是否具有误导性?

【问题讨论】:

    标签: graphics webgl webgl2


    【解决方案1】:

    不,你不需要重新绑定缓冲区

    ELEMENT_ARRAY_BUFFER 绑定是当前顶点数组状态的一部分,正如您链接到的答案所指出的那样。

    您示例中的这些行也无关紧要

    initBuffers

      // Clean
      gl.bindVertexArray(null);
      gl.bindBuffer(gl.ARRAY_BUFFER, null);
      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
    

    这些行都不是真正需要的。即使不需要,也只有第一行有任何实际意义

    这一行

      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
    

    实际上什么都不做,因为如上所述 ELEMENT_ARRAY_BUFFER 状态是当前顶点数组的一部分,所以只需使用 gl.bindVertexArray 更改当前顶点数组就已经更改了该绑定。

    这一行

      gl.bindBuffer(gl.ELEMENT_BUFFER, null);
    

    真的没有意义,因为 AFAIK 几乎没有程序只是假设当前的 ARRAY_BUFFER 绑定设置为任何值。他们总是在对缓冲区进行操作之前绑定缓冲区。拥有它还不错,我相信你可以找到一些复杂的方法来让它变得重要,但在现实生活中我还没有见过。

    这条线确实有一点。

      gl.bindVertexArray(null);
    

    通常将顶点缓冲区与顶点属性分开设置。如果您要为每个要绘制的事物制作一个顶点数组并且您的模式是这样的

      // at init time
      for each thing I plan to draw
        (1) create buffers and fill with positions/normals/texcoords/indices
        (2) create/bind vertex array
        (3) setup attributes and ELEMENT_ARRAY_BUFFER
    

    那么如果在第 3 步之后不绑定 null,第 1 步最终会更改之前绑定的顶点数组的 ELEMENT_ARRAY_BUFFER 绑定

    换句话说,也许这条线

      gl.bindVertexArray(null);
    

    有道理。不过,还是有争议的。如果您交换了第 1 步和第 2 步并将初始化更改为

      // at init time
      for each thing I plan to draw
        (1) create/bind vertex array
        (2) create buffers and fill with positions/normals/texcoords/indices
        (3) setup attributes
    

    然后问题就解决了

    draw 中存在同样的 3 行

      // Clean
      gl.bindVertexArray(null);
      gl.bindBuffer(gl.ARRAY_BUFFER, null);
      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
    

    他们又没有意义

    【讨论】:

    • 您好,感谢您的回复!真的很翔实。还有一个问题我想问:当您说“通常将顶点缓冲区与顶点属性分开设置。如果您为每个要绘制的事物制作一个顶点数组并且您的模式是这样的......”模式下面包含(1) create buffers and fill with positions/normals/texcoords/indices 和`(3) 设置属性和ELEMENT_ARRAY_BUFFER. I am not sure I understand this. First step has indices, so you fill in the gl.ELEMENT_ARRAY_BUFFER` 使用indices 对吗?怎么第三步还包括ELEMENT_ARRAY_BUFFER
    • 另外,为什么当我们交换步骤 1 和 2 时,问题就消失了,即如果我们不绑定 gl.bindVertexArray(null); 就可以了。是不是因为我们为每个 ELEMENT_ARRAY_BUFFER 创建了一个新的 VAO,因此绑定 ELEMENT_ARRAY_BUFFER 改变了之前绑定的顶点数组的绑定?
    • 第 3 步包含元素数组缓冲区,因为如果您在创建索引时没有绑定 VAO,因为创建索引是第 1 步,而创建 VAO 是第 2 步,那么您的 ELEMENT_ARRAY_BUFFER将绑定到以前的 VAO 或默认 VAO,以您创建索引时绑定的为准。交换顺序意味着您的新 VAO 首先被绑定,因此当您创建索引时,它们会绑定到您的新 VAO。查看您在问题中发布的链接。它显示了这些功能是如何工作的。看看 ELEMENT_ARRAY_BUFFER 是如何工作的。应该清楚为什么
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-04
    • 1970-01-01
    • 2013-09-22
    • 2013-06-03
    • 1970-01-01
    • 1970-01-01
    • 2013-09-09
    相关资源
    最近更新 更多