【问题标题】:WebGL 2 When to clear the drawing buffer?WebGL 2 何时清除绘图缓冲区?
【发布时间】:2017-11-05 10:53:13
【问题描述】:

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

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

在测试时,我发现在 WebGL 2 中 this answer 仍然正确 - 您必须将 preserveDrawingBuffer 设置为 true

这真的正确吗?

preserveDrawingBuffer 是否有 OpenGL 等效项?

有没有办法将preserveDrawingBuffer 设置为false 并仍然调用readPixels?

This answer 使您看起来可以致电 gl.flush

preserveDrawingBuffer 与刷新上下文有何相同之处?

【问题讨论】:

    标签: webgl2


    【解决方案1】:

    您无需preserveDrawingBuffer: true 即可致电readPixels。您需要在退出当前事件之前调用readPixels

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

    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>

    请注意,由于触发清除的是复合操作(浏览器实际上在页面上绘制画布与其余 HTML),如果画布不在页面上,则它不会被合成并且不会被清除.

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

    // 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,比如当用户点击一个元素时,那么你至少有两个选项

    1. 设置preserveDrawingBuffer: true

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

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

    来自the spec section 2.2

    WebGL 在合成操作之前立即将其绘图缓冲区呈现给 HTML 页面合成器,但前提是自上一次合成操作以来至少发生了以下情况之一:

    • 上下文创建
    • 画布调整大小
    • 在绘图缓冲区是当前绑定的帧缓冲区时调用了 clear、drawArrays 或 drawElements

    在呈现绘图缓冲区以进行合成之前,实现应确保所有渲染操作都已刷新到绘图缓冲区。默认情况下,合成后绘图缓冲区的内容会被清除为默认值,如上表所示。

    可以通过设置 WebGLContextAttributes 对象的 preserveDrawingBuffer 属性来更改此默认行为。如果此标志为真,则绘图缓冲区的内容将被保留,直到作者清除或覆盖它们。如果此标志为假,则在渲染函数返回后尝试使用此上下文作为源图像执行操作可能会导致未定义的行为。这包括 readPixels 或 toDataURL 调用,或将此上下文用作另一个上下文的 texImage2D 或 drawImage 调用的源图像。

    【讨论】:

    • 很棒的答案。我假设即使preserveDrawingBuffer 设置为true 合成也需要发生。所以。将其设置为true 如何让readPixels 以“已定义”或一致的方式工作?
    • @Startec gman 对此有另一个很好的答案:stackoverflow.com/a/27747016/3190758 基本上它是“定义的”,因为绘图缓冲区不会再被清除为其默认值,而是会被复制到显示器每次缓冲(可能以牺牲一些性能为代价,因为绘图和显示缓冲区不能再交换)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多