很简单。当你创建一个WebGL画布时,它有两个缓冲区,分别为绘制缓冲(也称为后备缓冲)和显示缓冲(也称为前台缓冲)。
你可以在绘制缓冲中绘制图像。
如果你将图像绘制到画布上,意味着你调用了
gl.clear
或者
gl.draw???
,浏览器会将画布标记为"需要合成"。在当前事件退出后,下一次浏览器合成页面(将所有元素绘制在一起)时,它会将绘图缓冲复制到显示缓冲中,或者交换绘图缓冲和显示缓冲。
这要取决于浏览器和其他一些因素。如果你设置
preserveDrawingBuffer: true
,则它总是将绘图缓冲复制到显示缓冲。如果
preserveDrawingBuffer
为false(默认值),则交换或复制由浏览器和许多其他因素决定,但无论如何,当
preserveDrawingBuffer
为false时,WebGL会在交换或复制后清除绘图缓冲,以便你看不出差异,无论选择哪种方式,结果都是相同的。
之所以有两个缓冲区,是因为浏览器希望能够并行运行。使用这种设计,它可以随时使用显示缓冲绘制页面,因为它包含了你最后绘制的任何内容。如果没有它,如果只有绘图缓冲,并且你在并行绘制时,浏览器正在合成所有元素,当它最终需要使用它时,可能会从绘图缓冲中获取到你绘制一半的图像。
请注意,从WebGL编程的角度来看,有两个缓冲区实际上是您可以忽略的大部分内容。只有两个后果需要注意。
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.
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() {
}
function renderLoop() {
render();
requestAnimationFrame(renderLoop);
}
someButton.addEventListener('click', () => {
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();
const screenshotDataURL = webglCanvas.toDataURL();
});
同样地,如果您想要在多个帧上绘画,比如绘画程序,那么最简单的解决方案就是在创建WebGL上下文时设置preserveDrawingBuffer: true
。
更令人困惑的是,您提到了渲染缓冲和帧缓冲。这些都是WebGL中具体的东西。
渲染缓冲类似于纹理,但与纹理不同的是它不能用作着色器的输入。它只能用作输出。
帧缓冲是纹理和渲染缓冲的集合。当您想要渲染到纹理时,您需要将一个或多个纹理和渲染缓冲附加到帧缓冲上。然后告诉WebGL您想要渲染到帧缓冲而不是画布。完成后,您可以使用结果来渲染到画布。
请参见https://webglfundamentals.org/webgl/lessons/webgl-render-to-texture.html获取示例。