I'm trying to load images in WebGL, and then uploading them to the GPU. I'd like to use a compressed texture format, even though the original images are uncompressed/lossless.
To upload, this is what I'm doing:
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textureSource);
In the above code, textureSource
is a loaded (say, "texture.png").
It all works well, but I'd like to load WEBGL_compressed_texture_s3tc
formats (COMPRESSED_RGB_S3TC_DXT1_EXT
) to store the image in a compressed fashion.
I make sure that the extension is available and enabled...
var ext = gl.getExtension("WEBGL_compressed_texture_s3tc");
var fmt = ext.COMPRESSED_RGBA_S3TC_DXT5_EXT;
console.log(fmt); // 33779
But then I can't use it as a format. Using texImage2D()
doesn't work:
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'
The expected method is compressedTexImage2D()
, but that's not very helpful either:
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'.
This is obviously because compressedTexImage2D()
expects an Uint8Array
with the actual DDS/DXT data, not a JavaScript image like what I'm passing.
The obvious solution is to uploading files in their native DDS format - files that have been compressed somewhere else. But that's what I'm trying to avoid: in my current workflow, it'd make sense to have the image in their original format rather than pre-compress them (or having duplicates).
My question then is as such: can I still use the original PNG images, loading them, and having them upload to the GPU on their compressed format? In other words, can I compress textures to their DXT1/5 formats on the fly?
I'm a little bit constrained by video memory in what I'm doing, so any saving would be great. I managed to minimize the space used by the textures by using UNSIGNED_SHORT_4_4_4_4
and the other data types, which is a good start, but I'd like to try using the native compression too.
I haven't found much documentation on the topic, nor found relevant code on other popular libraries (Three.js, Pixi, etc), which leads me to believe that my request is super stupid, but I'd like to understand why. This page hints at licensing issues, which might be why WebGL doesn't feature a way to properly compress the file, nor allow for browser support of image objects.
As far as I am informed: NO.
I only work on desktop and embedded GL, but even there is no chance to compress textures on the fly without dedicated code or a library.
(And, well, those DXT formats are not very good, either, if your textures are too detailed or have lots of different colors within small buckets. Most likely you're better off just using smaller textures, as the DXT1 compresses to 1/8th and DXT5 to 1/4 of the original size (which is like halving the resolution of the texture).)