凯文瑞德的preserveDrawingBuffer
建议是正确的,但有(通常)更好的选择。 tl; dr是最后的代码。
将渲染的网页的最终像素放在一起并且协调渲染WebGL内容更加昂贵。通常的流程为:
- JavaScript的问题的绘图命令,以WebGL的上下文
- JavaScript的回报,控制返回到主浏览器事件循环
- WebGL的上下文接通绘图超过缓冲液(或其内容)的合成为融入网页目前正在屏幕上呈现的
- 页,支持WebGL内容,显示在屏幕上
注意,这是大多数OpenGL应用程序不同。在这些内容中,渲染的内容通常是直接显示的,而不是与网页上的一堆其他内容合成,其中一些实际上可能位于WebGL内容之上并与其混合。
将WebGL规范更改为在步骤3之后将绘图缓冲区视为基本为空。在devtools中运行的代码在步骤4之后,这就是为什么您会得到空缓冲区的原因。对规范的这种改变允许在步骤3之后的空白基本上是硬件中实际发生的情况(如许多移动GPU)的平台上的大幅性能改进。如果你想要解决这个问题,有时在步骤3之后制作WebGL内容的副本,浏览器必须在步骤3之前制作一份绘图缓冲区副本,以便在某些平台上让你的帧率急剧下降。
您可以这样做,并强制浏览器进行复制并通过将preserveDrawingBuffer
设置为true来保持图像内容可访问。从规范:
此默认行为可以通过设置WebGLContextAttributes对象的preserveDrawingBuffer属性进行更改。如果该标志为真,则绘图缓冲区的内容应保留,直到作者清除或覆盖它们。如果此标志为false,则在渲染函数返回后尝试使用此上下文作为源图像执行操作可能会导致未定义的行为。这包括readPixels或toDataURL调用,或者将此上下文用作另一个上下文的texImage2D或drawImage调用的源图像。
在你提供的示例,代码只是改变了上下文创建行:
gl = canvas.getContext("experimental-webgl", {preserveDrawingBuffer: true});
请记住,这将迫使一些浏览器的慢路径和性能将受到影响,这取决于什么和你如何渲染。在大多数桌面浏览器中,您应该没有问题,但实际上并不需要制作副本,而且这些浏览器构成绝大多数支持WebGL的浏览器......但仅限于现在。
但是,还有另一种选择(在规范的下一段中有些令人费解的提及)。
基本上,您在第2步之前自己制作副本:完成所有绘制调用之后,但在将控制权从代码返回给浏览器之前。这是WebGL绘图缓冲区仍然完好并且可以访问的时候,并且您应该在访问像素时没有问题。您可以使用与您使用相同的toDataUrl
或readPixels
呼叫,否则,这只是重要的时机。
在这里你得到了两全其美。您可以获得绘图缓冲区的副本,但是您不需要在每一帧中支付该副本,即使是那些不需要副本(可能大部分都是副本)的副本,也可以使用preserveDrawingBuffer
设置为true。
在你提供的例子,只是增加你的代码的drawScene
底部,您应该看到画布右下方的副本:
function drawScene() {
...
var webglImage = (function convertCanvasToImage(canvas) {
var image = new Image();
image.src = canvas.toDataURL('image/png');
return image;
})(document.querySelectorAll('canvas')[0]);
window.document.body.appendChild(webglImage);
}
你得到任何错误? '$$'是一个错字,还是你有jQuery使用双倍而不是单一的美元符号? – MrOBrian
'$$'是Chrome浏览器JavaScript控制台的内置函数,其功能与jQuery相似。我没有得到任何错误,没有。 – Randomblue
啊,不知道......我在想这个网页有什么问题。如果我在该页面的canvas上执行'toDataURL()',我会得到一张500x500的空白图像,并且画布清除。如果我在网站上执行完全相同的代码,我正在使用该画布,我得到正确的数据并且画布不清晰。 – MrOBrian