我知道这个问题在SO上已经至少回答过5次,但...
Kaiido提到的方法可以解决问题,但真正的问题是,在使用WebGL时,默认情况下canvas有两个缓冲区。一个缓冲区用于绘制,另一个缓冲区用于显示。
当您开始在WebGL画布上绘制时,一旦退出当前事件(例如requestAnimationFrame回调),canvas就会被标记为交换这两个缓冲区。当浏览器重新绘制页面时,它会执行交换操作。您正在绘制到的缓冲区与正在显示的缓冲区交换位置。现在您正在绘制到另一个缓冲区。该缓冲区被清除。
之所以清除缓冲区而不仅仅留下它是因为实际上浏览器是否交换缓冲区或执行其他操作取决于浏览器。例如,如果打开了抗锯齿(默认情况下打开),则它实际上不执行交换操作。它执行"解析"。它将您刚刚绘制的高分辨率缓冲区转换为正常分辨率的抗锯齿副本,并将其复制到显示缓冲区中。
因此,为了使其更加一致,无论浏览器使用其默认方式的方式是什么,它都会始终清除您要绘制到的任何缓冲区。否则,您将不知道它是否具有1帧旧数据或2帧旧数据。
将preserveDrawingBuffer: true
设置为告诉浏览器“始终复制,永远不要交换”。在这种情况下,它不必清除绘图缓冲区,因为所在的绘图缓冲区总是已知的,不需要交换。
所有这些的意义是什么?意义在于,如果您想调用toDataURL
或gl.readPixels
,您需要在同一事件中调用它。
因此,例如,您的代码可以按照以下方式工作:
var capture = false;
$('button').click(function() {
capture = true;
});
function render() {
renderer.render(...);
if (capture) {
capture = false;
var data = renderer.view.toDataURL("image/png", 1);
$('img').attr('src', data);
}
requestAnimationFrame(render);
}
requestAnimationFrame(render);
在这种情况下,因为您在相同的JavaScript事件中调用toDataURL
并呈现给它,所以无论preserveDrawingBuffer
是true还是false,您始终会得到正确的结果。
如果您编写的应用程序不经常进行渲染,您也可以执行类似操作:
$('button').click(function() {
renderer.render(...);
var data = renderer.view.toDataURL("image/png", 1);
$('img').attr('src', data);
});
默认情况下,preserveDrawingBuffer
为 false 的原因是交换速度比复制速度更快,因此这使得浏览器可以尽可能地快。
另请参见此答案以获取其他细节
renderer.view
引用了正确的画布对象吗?当你调用它的toDataURL
方法时,这个画布上有任何东西被绘制了吗? - KaiidotoDataURL
。 - Kaiido