WebGL 2 何时清除绘图缓冲区?

WebGL 2 When to clear the drawing buffer?

我目前正在开发调用 readPixels 的演示。

This answer on SO 是我在 preserveDrawingBuffer 选项上可以找到的大部分信息。

在测试时我观察到在 WebGL 2 中 this answer 仍然成立 - 您必须将 preserveDrawingBuffer 设置为 true

这真的正确吗?

是否有 preserveDrawingBuffer 的 OpenGL 等效项?

有什么方法可以将 preserveDrawingBuffer 设置为 false 并仍然调用 readPixels 吗?

This answer 使您似乎可以改为调用 gl.flush

preserveDrawingBuffer 与刷新上下文有什么区别?

您不需要 preserveDrawingBuffer: true 来调用 readPixels。您需要的是在退出当前事件之前调用 readPixels

规范说,如果您调用任何影响 canvas 的函数(gl.clear、gl.drawXXX),那么浏览器将在下一次复合操作后清除 canvas .复合操作何时发生取决于浏览器。它可能是在它处理了几个鼠标事件或键盘事件或单击事件之后。订单未定义。定义的是直到当前事件退出才会做所以

render
read

const gl = document.querySelector("canvas").getContext("webgl2");

render();
read();  // read in same event

function render() {
  gl.clearColor(.25, .5, .75, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);
}

function read() {
  const pixel = new Uint8Array(4);
  gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
  log(pixel);
}

function log(...args) {
  const elem = document.createElement("pre");
  elem.textContent = [...args].join(' ');
  document.body.appendChild(elem);
}
<canvas></canvas>

的地方工作
render
setTimeout(read, 1000);  // some other event

无效

const gl = document.querySelector("canvas").getContext("webgl2");

render();
setTimeout(read, 1000);  // read in other event

function render() {
  gl.clearColor(.25, .5, .75, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);
}

function read() {
  const pixel = new Uint8Array(4);
  gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
  log(pixel);
}

function log(...args) {
  const elem = document.createElement("pre");
  elem.textContent = [...args].join(' ');
  document.body.appendChild(elem);
}
<canvas></canvas>

请注意,由于是复合操作(浏览器实际在页面上绘制 canvas 和 HTML 的其余部分)触发清除,如果 canvas 是不在页面上那么它就不会合成,也不会被清除。

换句话说,上面不起作用的情况在这里确实起作用

// create an offscreen canvas. Because it's offscreen it won't be composited
// and therefore will not be cleared.
const gl = document.createElement("canvas").getContext("webgl2");

render();
setTimeout(read, 1000);  // read in other event

function render() {
  gl.clearColor(.25, .5, .75, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);
}

function read() {
  const pixel = new Uint8Array(4);
  gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
  log(pixel);
}

function log(...args) {
  const elem = document.createElement("pre");
  elem.textContent = [...args].join(' ');
  document.body.appendChild(elem);
}

如果您想在某些其他事件中调用 readPixels,例如当用户单击某个元素时,那么您至少有 2 个选项

  1. 设置preserveDrawingBuffer: true

  2. 在您的活动中再次渲染

    screenshotButton.addEventListener('click', () => {
       render();
       read();
    });
    

来自the spec section 2.2

WebGL presents its drawing buffer to the HTML page compositor immediately before a compositing operation, but only if at least one of the following has occurred since the previous compositing operation:

  • Context creation
  • Canvas resize
  • clear, drawArrays, or drawElements has been called while the drawing buffer is the currently bound framebuffer

Before the drawing buffer is presented for compositing the implementation shall ensure that all rendering operations have been flushed to the drawing buffer. By default, after compositing the contents of the drawing buffer shall be cleared to their default values, as shown in the table above.

This default behavior can be changed by setting the preserveDrawingBuffer attribute of the WebGLContextAttributes object. If this flag is true, the contents of the drawing buffer shall be preserved until the author either clears or overwrites them. If this flag is false, attempting to perform operations using this context as a source image after the rendering function has returned can lead to undefined behavior. This includes readPixels or toDataURL calls, or using this context as the source image of another context's texImage2D or drawImage call.