WebGL渲染缓冲区

3

阅读此文

WebGL从不使用单一缓冲区,这意味着您当前渲染的图像从未是当前显示在Canvas元素中的图像。这确保了半渲染帧永远不会出现在浏览器窗口中。正在渲染的图像称为WebGL帧缓冲区或后备缓冲区。谈论帧缓冲区变得更加复杂,因为WebGL还允许额外的离屏帧缓冲区,但在本文中我们将忽略它。当前显示的图像称为前缓冲区。当然,后备缓冲区的内容最终会被复制到前缓冲区中 - 否则,WebGL绘图将没有用户可见效果!

我们当前正在渲染的图像从未在画布中显示,这怎么可能?

此外,文章中提到正在渲染的图像被称为webGL帧缓冲区或后备缓冲区,而正在显示的图像被称为前缓冲区。这有什么区别?能否有人解释一下?

1个回答

8
很简单。当你创建一个WebGL画布时,它有两个缓冲区,分别为绘制缓冲(也称为后备缓冲)和显示缓冲(也称为前台缓冲)。
你可以在绘制缓冲中绘制图像。
如果你将图像绘制到画布上,意味着你调用了gl.clear或者gl.draw???,浏览器会将画布标记为"需要合成"。在当前事件退出后,下一次浏览器合成页面(将所有元素绘制在一起)时,它会将绘图缓冲复制到显示缓冲中,或者交换绘图缓冲和显示缓冲。
这要取决于浏览器和其他一些因素。如果你设置preserveDrawingBuffer: true,则它总是将绘图缓冲复制到显示缓冲。如果preserveDrawingBuffer为false(默认值),则交换或复制由浏览器和许多其他因素决定,但无论如何,当preserveDrawingBuffer为false时,WebGL会在交换或复制后清除绘图缓冲,以便你看不出差异,无论选择哪种方式,结果都是相同的。
之所以有两个缓冲区,是因为浏览器希望能够并行运行。使用这种设计,它可以随时使用显示缓冲绘制页面,因为它包含了你最后绘制的任何内容。如果没有它,如果只有绘图缓冲,并且你在并行绘制时,浏览器正在合成所有元素,当它最终需要使用它时,可能会从绘图缓冲中获取到你绘制一半的图像。
请注意,从WebGL编程的角度来看,有两个缓冲区实际上是您可以忽略的大部分内容。只有两个后果需要注意。
  1. If preserveDrawingBuffer is false (the default) then the drawingbuffer will be cleared then the browser composites the page. The effect of which is, you need to draw everything every time you update. You can't draw a little each frame. If you want to see 3 circles you need to draw 3 circles all in the same frame. This is normal for probably 99% of games and probably 99% of WebGL apps. It's the same in OpenGL, DirectX, Metal, Vulkan, etc. Games written in those systems also draw everything every frame.

  2. If you're going to use canvas.toDataURL or canvas.toBlob or gl.readPixels or any other way of getting data from a WebGL canvas, unless you read it in the same event then it will likely be clear when you try to read it.

    In other words if you do this

    function render() {
       // draw stuff with WebGL
    }
    
    function renderLoop() {
      render();
      requestAnimationFrame(renderLoop);
    }
    
    someButton.addEventListener('click', () => {
      // take screenshot
      const screenshotDataURL = webglCanvas.toDataURL();
    });
    

    The screenshot will likely fail because WebGL will likely have cleared the drawingbuffer when the user clicks someButton.

    The solution is either to set preserveDrawingBuffer: true or to make sure you render in the same event

    someButton.addEventListener('click', () => {
    
      // !!! render in the click event
      render();                 
    
      // take screenshot
      const screenshotDataURL = webglCanvas.toDataURL();
    });
    

同样地,如果您想要在多个帧上绘画,比如绘画程序,那么最简单的解决方案就是在创建WebGL上下文时设置preserveDrawingBuffer: true

更令人困惑的是,您提到了渲染缓冲和帧缓冲。这些都是WebGL中具体的东西。

渲染缓冲类似于纹理,但与纹理不同的是它不能用作着色器的输入。它只能用作输出。

帧缓冲是纹理和渲染缓冲的集合。当您想要渲染到纹理时,您需要将一个或多个纹理和渲染缓冲附加到帧缓冲上。然后告诉WebGL您想要渲染到帧缓冲而不是画布。完成后,您可以使用结果来渲染到画布。

请参见https://webglfundamentals.org/webgl/lessons/webgl-render-to-texture.html获取示例。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接