如何使用 WebGL 异步加载图像、创建纹理、渲染和保存图像?

Posted

技术标签:

【中文标题】如何使用 WebGL 异步加载图像、创建纹理、渲染和保存图像?【英文标题】:How to load image, create texture, render, and save image asynchronously with WebGL? 【发布时间】:2011-11-09 09:42:34 【问题描述】:

我正在尝试编写一个使用非常大的纹理的应用程序。这个想法是您在实时修改着色器时处理纹理的缩放版本,完成后应用程序会将您的更改应用于原始未缩放(大)纹理。问题是分析显示如下内容:

img.src = 文件名(500 毫秒) texImage2d(...) (1500ms) 绑定/渲染(100 毫秒) 读取像素(300 毫秒) 放入画布(1000 毫秒) 将画布保存到文件(300 毫秒)

本质上,这意味着浏览器在保存较大的未缩放纹理时会锁定近四秒钟,而用户无法执行任何操作。是否可以异步执行此操作以使浏览器保持响应?这一切都需要在 javascript 和客户端完成,因为我使用的是本地文件(html5 文件/文件系统)。

Web worker 听起来是个好主意,但他们无法访问 DOM,因此我无法使用浏览器的图像加载和保存功能,而且他们无法访问 WebGL 上下文,因此无法调用 texImage2d ,最耗时。

由于图像的大小和数量,我无法在页面最初加载时将它们全部作为纹理加载到内存中。

有什么办法可以让这对用户更敏感吗?我希望他们能够在前一个图像渲染的同时继续处理下一个图像。

【问题讨论】:

【参考方案1】:

图像加载应该在后台进行,您不会知道它的进度,但您可以使用texSubImage2D 逐步上传纹理。总体而言,这可能需要更长的时间,但您应该能够向用户提供一些反馈并响应其他事件。

另外,您可以直接将 webgl 画布绘制到 2D 画布中。 drawImage() 将图像、视频和画布(2D 或 webgl)元素作为参数。这应该几乎立即发生,并节省大约 1300 毫秒。

【讨论】:

谢谢,将画布直接传递给 drawImage 可以节省大量时间。使用 texSubImage2D 似乎也有很大帮助。 嗯,再想想,我错了。 drawImage 调用更快,但事实证明它只是写入黑色矩形而不是图像数据。 现在很奇怪。它适用于我在 Win7 和 OS X 上的 FF,但是当我在 Chrome 中尝试它时,它不仅不起作用,当我尝试将它绘制到 2D 时它会清除 WebGL 画布! 看来 preserveDrawingBuffer WebGL 上下文属性将修复此行为,例如canvas.getContext('webgl', preserveDrawingBuffer: true );设置后它现在可以在 Chrome 中正确绘制,所以我实际上可以使用 drawImage!【参考方案2】:

这个问题很老,但对于其他找到它的人来说,这里有一些更新的信息

您可以考虑使用自 2019 年 1 月起在 Chrome 中发货的 OffscreenCanvas。不幸的是,它没有在其他任何 ATM 上发货

这 3 个同步步骤

readPixels (300ms)
Put into canvas (1000ms)
Save canvas to file (300ms)

可以变成1个异步步骤

gl.canvas.toBlob((blob) => 
  const url = URL.createObjectURL(blob);
  // url is now something you can give the user to download
);

要渲染非常大的图像(比如 16k x 16k),您可以渲染较小的部分并将它们组合成更大的图像。

这里有一个库:https://greggman.github.io/dekapng/

【讨论】:

以上是关于如何使用 WebGL 异步加载图像、创建纹理、渲染和保存图像?的主要内容,如果未能解决你的问题,请参考以下文章

WebGL - 从渲染缓冲区读取像素数据

WebGL 理论基础 - 图像处理 上

webgl 纹理

计算机图形学基于WebGL的纹理贴图

WebGL纹理颜色原理

WebGL入门(四十一)-使用帧缓冲区对象(FBO)实现将渲染结果作为纹理绘制到另一个物体上