为什么我们应该在将一些数据放入此缓冲区之前调用 webgl.bindBuffer?

Why we should call webgl.bindBuffer before putting some data to this buffer?

我试图弄清楚缓冲区在 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 上创建一个空缓冲区,并 returns 在此缓冲区上引用 JS。现在变量 buffer 是一个指针。
  3. 将缓冲区指针设置为 gl.ARRAY_BUFFER
  4. 现在我们将数据从 RAM 复制 到 GPU 缓冲区。
  5. gl.ARRAY_BUFFER 解除绑定缓冲区(但缓冲区在 GPU 上仍然可用,我们可以重新绑定它更多次)。

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

后重新分配 positions 大小更快

因为那是 API 唯一真实的答案。

很多人同意你的看法,换一个 API 会更好。这是出现新 API(DirectX11/12、Vulkan、Metal、WebGPU)

的原因之一

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

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

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

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

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

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

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

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

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,则复制它。很多 driver 都是这样做的。有关 driver 不将数据复制到 GPU 的更具体示例,它似乎会看到 this answer and this one