如何在不冻结浏览器的情况下使用webgl将大图像上传到GPU(想想高分辨率的天空盒或纹理地图集)?
我一开始以为是想找到一种方法来texImage2D异步地做它的事情(将图像上传到GPU就是IO-ish,对吧?),但我找不到任何办法.然后我尝试使用texSubImage2D上传适合16毫秒时间窗口的小块(我的目标是60 fps).但是texSubImage2D只有在传入时才需要偏移AND宽度/高度参数ArrayBufferView- 当传入Image对象时,你只能指定偏移量,它会(我猜)上传整个图像.我想首先将图像绘制到画布上(将其作为缓冲区)与将整个事物上传到GPU一样慢.
这是我的意思的最小例子:http://jsfiddle.net/2v63f/3/.我需要~130 ms才能将此图像上传到GPU.
与jsfiddle完全相同的代码:
var canvas = document.getElementById('can');
var gl = canvas.getContext('webgl');
var image = new Image();
image.crossOrigin = "anonymous";
//image.src = 'http://i.imgur.com/9Tq28Qj.jpg?1';
image.src = 'http://i.imgur.com/G0qL97y.jpg'
image.addEventListener('load', function () {
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
var now = performance.now();
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
console.log(performance.now() - now);
});
Run Code Online (Sandbox Code Playgroud)
小智 4
引用的图像似乎是 3840x1920
对于高分辨率天空盒,您通常可以放弃对 Alpha 通道的需求,然后决定其他像素格式结构是否可以在质量和数据大小之间提供合理的权衡。
指定的 RGBA 编码意味着这将需要 29,491,200 字节的图像解码后数据传输。我尝试使用 RGB 进行测试,放弃 Alpha,但看到类似的 111 毫秒传输时间。假设您可以在使用之前对图像进行预处理,并且只关心测量到 GPU 的数据传输时间,则可以对数据执行某种形式的有损编码或压缩,以减少所需的带宽量。转移。
一种更简单的编码方法是将数据大小减半,并使用 RGB 565 将数据发送到芯片。这会将数据大小减少到 14,745,600 字节,但代价是颜色范围。
var buff = new Uint16Array(image.width*image.height);
//encode image to buffer
//on texture load-time, perform the following call
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, image.width, image.height, 0, gl.RGB, gl.UNSIGNED_SHORT_5_6_5, buff);
Run Code Online (Sandbox Code Playgroud)
假设您可以确认对 S3TC 扩展的支持(https://developer.mozilla.org/en-US/docs/Web/WebGL/Using_Extensions#WEBGL_compressed_texture_s3tc),您还可以以 DXT1 格式存储和下载纹理并减少内存传输要求降至 3,686,400 字节。看来任何形式的 S3TC 都会导致相同的 RGB565 颜色范围减少。
var ext = (
gl.getExtension("WEBGL_compressed_texture_s3tc") ||
gl.getExtension("MOZ_WEBGL_compressed_texture_s3tc") ||
gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc")
);
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, ext.COMPRESSED_RGB_S3TC_DXT1_EXT, image.width, image.height, 0, buff);
Run Code Online (Sandbox Code Playgroud)
大多数块压缩会降低近距离图像质量,但会在远距离提供更丰富的图像。如果您需要近距离的高分辨率,则最初可以加载较低分辨率或压缩纹理以用作较差的 LOD 远景,并且可以在接近相关纹理时加载较高分辨率的较小子集。
在我在http://jsfiddle.net/zy8hkby3/2/进行的简单测试中,纹理有效负载大小的减少可能会导致数据传输的时间要求呈指数级减少,但这很可能取决于 GPU。