我正在尝试在 WebGL 中加载图像,然后将它们上传到 GPU。我想使用压缩纹理格式,即使原始图像是未压缩/无损的。
要上传,这就是我正在做的事情:
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textureSource);
在上面的代码中,textureSource
是一个加载的(比如“texture.png”)。
这一切都很好,但我想加载WEBGL_compressed_texture_s3tc
格式(COMPRESSED_RGB_S3TC_DXT1_EXT
)以压缩方式存储图像。
我确保扩展程序可用并已启用...
var ext = gl.getExtension("WEBGL_compressed_texture_s3tc");
var fmt = ext.COMPRESSED_RGBA_S3TC_DXT5_EXT;
console.log(fmt); // 33779
但是我不能将它用作一种格式。使用texImage2D()
不起作用:
gl.texImage2D(gl.TEXTURE_2D, 0, fmt, fmt, gl.UNSIGNED_BYTE, textureSource);
// WebGL: INVALID_ENUM: texImage2D: invalid texture format
// [.WebGLRenderingContext]RENDER WARNING: texture bound to texture unit 0 is not renderable. It maybe non-power-of-2 and have incompatible texture filtering or is not 'texture complete'
预期的方法是compressedTexImage2D()
,但这也不是很有帮助:
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, fmt, 256, 256, 0, texture.source);
// Uncaught TypeError: Failed to execute 'compressedTexImage2D' on 'WebGLRenderingContext': parameter 7 is not of type 'ArrayBufferView'.
这显然是因为compressedTexImage2D()
期望Uint8Array
使用实际的 DDS/DXT 数据,而不是像我传递的那样的 JavaScript 图像。
显而易见的解决方案是以原始 DDS 格式上传文件 - 已在其他地方压缩的文件。但这就是我要避免的:在我当前的工作流程中,将图像保留为原始格式而不是预压缩它们(或重复)是有意义的。
我的问题是这样的:我是否仍然可以使用原始 PNG 图像,加载它们,并让它们以压缩格式上传到 GPU?换句话说,我可以即时将纹理压缩为它们的 DXT1/5 格式吗?
我正在做的事情有点受视频内存的限制,所以任何节省都会很棒。我设法通过使用UNSIGNED_SHORT_4_4_4_4
和其他数据类型来最小化纹理使用的空间,这是一个好的开始,但我也想尝试使用本机压缩。
我没有找到关于该主题的太多文档,也没有在其他流行的库(Three.js、Pixi 等)上找到相关代码,这让我相信我的请求非常愚蠢,但我想了解原因。这个页面暗示了许可问题,这可能是 WebGL 没有正确压缩文件的方法,也不允许浏览器支持图像对象的原因。