WebGL 渲染缓冲区

标签 webgl

阅读 this文章

WebGL is never single-buffered, meaning that the image that you are currently rendering is never the one that is currently displayed in the Canvas element. This ensures that half-rendered frames never show up in the browser’s window. The image being rendered is called the WebGL framebuffer or backbuffer. Talking of framebuffers is made more complicated by the fact that WebGL also allows additional off-screen framebuffers, but let’s ignore that in this article. The image currently being displayed is called the frontbuffer. Of course, the contents of the backbuffer will at some point be copied into the frontbuffer — otherwise WebGL drawing would have no user-visible effect!

我们当前正在渲染的图像永远不是 Canvas 中显示的图像,这怎么可能?

然后它说正在渲染的图像称为 webGL 帧缓冲区或后缓冲区,正在显示的图像是前缓冲区。有什么不同。有人可以对这篇文章有所启发吗?

最佳答案

这很简单。当你制作一个 WebGL Canvas 时,它有 2 个缓冲区,绘图缓冲区(也称为后缓冲区)和显示缓冲区(也称为前缓冲区)。

你绘制到绘图缓冲区。

如果你在 Canvas 上画东西,这意味着你调用 gl.cleargl.draw??? 当它们设置为渲染到 Canvas 时,然后浏览器将 Canvas 标记为“需要合成”。当前事件退出后,下一次浏览器合成页面(将所有元素绘制在一起)时,它要么将绘图缓冲区复制到显示缓冲区,要么交换绘图缓冲区和显示缓冲区。

它的作用取决于浏览器和许多其他因素。如果您设置 preserveDrawingBuffer: true 那么它总是将 drawingubuffer 复制到 displaybuffer。如果 preserveDrawingBuffer 为 false(默认值),则交换或复制取决于浏览器和一系列其他因素,但无论如何,当 preserveDrawingBuffer 为 false 后,WebGL 将清除绘图缓冲区它会交换或复制,这样您就无法区分,因此无论选择哪个,结果都是相同的。

它有 2 个缓冲区,因为浏览器希望能够并行运行。通过这种设计,它可以在任何需要或需要的时候使用显示缓冲区绘制页面,因为它包含您上次呈现的任何结果。没有它,如果您只有绘图缓冲区并再次开始绘图,并且浏览器将所有元素并行合成在一起,它可能会在最终需要使用它时从绘图缓冲区中获取您绘制一半的图像。

请注意,有 2 个缓冲区这一事实在很大程度上是您可以忽略的。从编程 WebGL 的角度来看,实际上只有 2 个结果

  1. 如果 preserveDrawingBuffer 为 false(默认值),则绘图缓冲区将被清除,然后浏览器合成页面。其效果是,每次更新都需要绘制所有内容。你不能每帧都画一点。如果你想看到 3 个圆圈,你需要在同一帧中绘制 3 个圆圈。这对于大约 99% 的游戏和可能 99% 的 WebGL 应用程序来说都是正常的。在 OpenGL、DirectX、Metal、Vulkan 等中也是如此。在这些系统中编写的游戏也会在每一帧绘制所有内容。

  2. 如果您要使用 canvas.toDataURLcanvas.toBlobgl.readPixels 或任何其他方式从 WebGL Canvas 获取数据,除非您在同一事件中读取它,否则当您尝试读取它时它可能会很清楚。

    换句话说,如果你这样做

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

    屏幕截图可能会失败,因为当用户单击 someButton 时,WebGL 可能会清除绘图缓冲区。

    解决方案是设置 preserveDrawingBuffer: true 或确保在同一事件中渲染

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

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

为了增加更多的混淆,您提到了渲染缓冲区和帧缓冲区。这些是 WebGL 中的特定内容。

渲染缓冲区就像一个纹理,除了它不像纹理,它不能用作着色器的输入。它只能用作输出。

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

参见 https://webglfundamentals.org/webgl/lessons/webgl-render-to-texture.html举个例子。

关于WebGL 渲染缓冲区,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56303648/

相关文章:

opengl-es - WebGL 着色器 : maximum number of varying variables

reactjs - 显示: none on Deck. gl React组件导致巨大的性能问题

opengl-es - 在 3D 世界中渲染 2D Sprite ?

javascript - WebGL - 如何在 Typescript 中正确地将程序参数传递给 gl.attachShader?

javascript - 添加到 YouTube 的 Canvas 不可见

javascript - 无法使 three.js 中的 FirstPersonControls 正常工作

javascript - 如何用WebGL2绘制3个矩形

javascript - WebGL 片段着色器构造函数错误 - 参数太多

opengl-es - 如何读取 WebGL 中的深度缓冲区?

javascript - Canvas 变换后,如何获取正在绘制的对象的 2d 尺寸,以便在 Canvas 2d 上进行 HitTest ?