【问题标题】:Why we should call webgl.bindBuffer before putting some data to this buffer?为什么我们应该在将一些数据放入这个缓冲区之前调用 webgl.bindBuffer?
【发布时间】:2020-11-07 21:55:49
【问题描述】:

我试图弄清楚缓冲区在 WebGL 中的工作原理,但我有点卡在这里。以下是我的猜测 - 请确认或否认。

const positions = new Float32Array([
 -1, 1,
 -0.5, 0,
 -0.25, 0.25,
]);

let buffer = gl.createBuffer();

gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
  1. 我们通过 JS 在 RAM 上创建一个浮点数组。
  2. WebGL 直接在 GPU 上创建一个空缓冲区,并将此缓冲区上的引用返回给 JS。现在变量buffer 是一个指针。
  3. 将缓冲区上的指针设置为gl.ARRAY_BUFFER
  4. 现在我们将数据从 RAM 复制到 GPU 缓冲区。
  5. gl.ARRAY_BUFFER 取消绑定缓冲区(但缓冲区在 GPU 上仍然可用,我们可以重新绑定更多次)。

那么为什么我们不能只用positions 调用createBuffer() 而不是使用ARRAY_BUFFER 作为JS 和GPU 之间的桥梁呢?这些仅仅是 OpenGL API 的限制,还是我们有充分的理由不这样做?如果我错了,请纠正我,但是在我们调用bufferData 之后,分配已知大小的内存比使用positions 大小分配一些内存和重新分配要快。

【问题讨论】:

    标签: javascript webgl


    【解决方案1】:

    因为 API 是唯一真正的答案。

    许多人同意您的观点,即不同的 API 会更好。这也是为什么会有新的 api(DirectX11/12、Vulkan、Metal、WebGPU)的原因之一

    但问题中的描述在技术上并不正确

    1. 我们通过 JS 在 RAM 上创建一个浮点数组。

    2. WebGL 创建一个表示 GPU 缓冲区的对象(GPU 上没有分配任何内容)

    3. 将缓冲区上的指针设置为 gl.ARRAY_BUFFER。

    4. 现在我们分配一个缓冲区并将数据从 RAM 复制到 GPU 缓冲区。

    5. 从 gl.ARRAY_BUFFER 取消绑定缓冲区(但缓冲区在 GPU 上仍然可用,我们可以重新绑定更多次)。

    不需要第 5 步。没有理由取消绑定缓冲区。

    你可以这样想。想象一下,您有一个 javascript 函数,可以将图像绘制到画布上,但图像的传递方式与示例中的缓冲区相同。这是代码

    class Context {
      constructor(canvas) {
        this.ctx = canvas.getContext('2d');
      }
      bindImage(img) {
        this.img = img;
      }
      drawImage(x, y) {
        this.ctx.drawImage(this.img, x, y);
      }
    }
    

    假设您要绘制 3 张图像

    const ctx = new Context(someCanvas);
    ctx.bindImage(image1);
    ctx.drawImage(0, 0);
    ctx.bindImage(image2);
    ctx.drawImage(10, 10);
    ctx.bindImage(image3);
    ctx.drawImage(20, 20);
    

    可以正常工作。没有理由这样做

    const ctx = new Context(someCanvas);
    ctx.bindImage(image1);
    ctx.drawImage(0, 0);
    ctx.bindImage(null);    // not needed
    ctx.bindImage(image2);
    ctx.drawImage(10, 10);
    ctx.bindImage(null);    // not needed
    ctx.bindImage(image3);
    ctx.drawImage(20, 20);
    ctx.bindImage(null);    // not needed
    

    在 WebGL 中也是如此。有时会将 null 绑定到某些东西,例如

    gl.bindFramebuffer(gl.FRAMEBUFFER, null);  // start drawing to the canvas
    

    但大多数时候解除绑定只是程序员的个人喜好,而不是 API 本身需要的东西

    参考:

    请注意,即使我上面的描述在技术上也不正确。 step4 是否将数据复制到 GPU 是未定义的。它可以只将数据复制到 RAM,并且仅在绘制时,如果使用了缓冲区,并且尚未将其复制到 GPU,则复制它。很多司机都是这样做的。一个更具体的例子,一个驱动程序没有将数据复制到 GPU,当它看起来会看到 this answerthis one

    【讨论】:

    • 感谢您的精彩回答! :) 我同意第 5 步 - 它根本不需要,它只是为了演示 gl.ARRAY_BUFFER 只是 buffer 上的一个指针,如果你用另一个缓冲区引用替换它,buffer 将不会被删除。但是为什么您认为问题的描述在技术上不正确?如我所见,我们不能直接通过 WebGL 状态机将一些数据放入缓冲区,对吗?
    • 或者你的意思是我们只有在调用gl.bindBuffer时才在GPU上创建一个真正的缓冲区,并且在此之前我们不能设置数据?
    • 您的描述说第 2 步在 GPU 上创建了一个缓冲区。这是不正确的。它只是在 CPU 上创建一些跟踪信息。 bufferData 创建实际的缓冲区,见最后一段。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-23
    • 1970-01-01
    相关资源
    最近更新 更多