GLTFExporter: Add EXT_texture_webp support.#33117
Conversation
EXT_texture_webp support.
There was a problem hiding this comment.
Thanks @BorisKourt! I agree it'd be good to support EXT_texture_webp, but it won't be enough to append EXT_texture_webp to the extensionsUsed list. We also need to extend the texture definition, as shown in the "Using without a fallback" section of the spec:
I'd also recommend testing the output by exporting a file with WebP and then running it through https://github.khronos.org/glTF-Validator/ for validation.
|
@BorisKourt Updating async processTextureAsync( map ) {
const writer = this;
const options = writer.options;
const cache = this.cache;
const json = this.json;
if ( cache.textures.has( map ) ) return cache.textures.get( map );
if ( ! json.textures ) json.textures = [];
// make non-readable textures (e.g. CompressedTexture) readable by blitting them into a new texture
if ( map instanceof CompressedTexture ) {
map = await this.decompressTextureAsync( map, options.maxTextureSize );
}
const mimeType = map.userData.mimeType;
const imageIndex = this.processImage( map.image, map.format, map.flipY, mimeType );
const textureDef = {
sampler: this.processSampler( map )
};
if ( mimeType === 'image/webp' ) {
textureDef.extensions = textureDef.extensions || {};
textureDef.extensions[ 'EXT_texture_webp' ] = {
source: imageIndex
};
this.extensionsUsed[ 'EXT_texture_webp' ] = true;
this.extensionsRequired[ 'EXT_texture_webp' ] = true;
} else {
textureDef.source = imageIndex;
}
if ( map.name ) textureDef.name = map.name;
await this._invokeAllAsync( async function ( ext ) {
ext.writeTexture && await ext.writeTexture( map, textureDef );
} );
const index = json.textures.push( textureDef ) - 1;
cache.textures.set( map, index );
return index;
} |
|
Thanks for catching this @donmccurdy and thanks for the sample @Mugen87! I already started noodling so let me know if I properly adapted everything. Attached is an exported file. Should I add an example directly to examples for this update or is it too small? {
"uri": "webp_test_synthetic.glb",
"mimeType": "model/gltf-binary",
"validatorVersion": "2.0.0-dev.3.10",
"validatedAt": "2026-03-05T08:14:16.292Z",
"issues": {
"numErrors": 0,
"numWarnings": 0,
"numInfos": 0,
"numHints": 0,
"messages": [],
"truncated": false
},
"info": {
"version": "2.0",
"generator": "three.js test",
"extensionsUsed": [
"EXT_texture_webp"
],
"extensionsRequired": [
"EXT_texture_webp"
],
"resources": [
{
"pointer": "/buffers/0",
"mimeType": "application/gltf-buffer",
"storage": "glb",
"byteLength": 104
},
{
"pointer": "/images/0",
"mimeType": "image/webp",
"storage": "buffer-view",
"image": {
"width": 1,
"height": 1,
"format": "rgb",
"primaries": "srgb",
"transfer": "srgb",
"bits": 8
}
}
],
"animationCount": 0,
"materialCount": 1,
"hasMorphTargets": false,
"hasSkins": false,
"hasTextures": true,
"hasDefaultScene": true,
"drawCallCount": 1,
"totalVertexCount": 3,
"totalTriangleCount": 1,
"maxUVs": 1,
"maxInfluences": 0,
"maxAttributes": 2
}
} |
|
There is no need to add a new glTF asset to the repository. Instead, can you please add a box mesh with a WebP texture to misc_exporter_gltf? You can place it next to the The idea is to add a new button "Export WebP Model" so we can easily verify the |
|
Will add what you suggest 👍 This was just an example export as per @donmccurdy's request, wasn't planning to add it to the repo! |
|
Looks like screenshots need to be updated, should I do that as part of this pr? |
|
That would be good. You can use below screenshot and replace it with the one in |
|
Thanks, I have added it now. |
|
Looking all good now. Thanks for your work on this! |
|
Glad I could help! |

Fixed #33116
In #23592 (r139),
userData.mimeTypewas added so the exporter preservesJPEG instead of inflating everything to PNG. WebP was deferred
to be handled via
EXT_texture_webp: #23592 (comment)We remove the
image/webp → image/pngguard inprocessTextureAsyncand register
EXT_texture_webpinextensionsUsedwhen a WebP texture isencountered.