Export a GLB from Blender with every texture setting maxed out and you'll still get plain PNG or JPEG packed inside the file. Blender's glTF exporter has no option to write KTX2 textures, even though the `KHR_texture_basisu` extension has been part of the glTF 2.0 spec for years and can cut a texture-heavy GLB by a third or more. The fix isn't a checkbox buried in the export panel — it's a second pass with a command-line tool after export. Here's the exact command, what it actually changes, and which engines will load a KTX2-compressed GLB once you have one.
Why Blender won't do this for you
![]()
KTX2 is a container format built for GPU textures. Basis Universal is the compression inside it — it transcodes to whatever block-compressed format the target GPU actually wants (BC7 on desktop, ASTC on mobile) instead of shipping a full-resolution PNG that the engine has to decode and recompress at load time. The glTF spec exposes this through `KHR_texture_basisu`, an extension for pointing a material texture slot at a KTX2 image instead of a PNG or JPEG.
Blender's glTF I/O addon — the same one behind File > Export > glTF 2.0 — doesn't write that extension, and it can't read it back in either. Open a GLB that already carries KTX2 textures in Blender and the addon errors with something close to "Extension KHR_texture_basisu is not available on this addon version." That's not a bug waiting on a patch; it's an open feature request against glTF-Blender-IO that's sat unresolved for years. Whatever quality slider gets dragged in the export panel, the textures that come out are still PNG or JPEG, every time.
Compress the GLB's textures to KTX2
The actual conversion happens outside Blender, with the `gltf-transform` CLI — the same tooling most of the glTF ecosystem uses for post-export processing. Normal maps and metallic/roughness maps need the higher-fidelity UASTC codec or they band visibly at grazing angles; flat color textures compress fine with the smaller ETC1S codec.
```bash
npx @gltf-transform/cli uastc model.glb tmp.glb --slots "normalTexture,occlusionTexture,metallicRoughnessTexture"
npx @gltf-transform/cli etc1s tmp.glb model-ktx2.glb --quality 200
```
Run both passes in sequence on the same file — UASTC on the maps that need the fidelity, then ETC1S on everything else. The `--slots` flag matches glTF texture slot names exactly; get the name wrong and the pass silently no-ops instead of throwing an error, so check the output file size against the input before assuming it worked. Khronos's own FlightHelmet sample model, a standard glTF test asset, drops from roughly 43MB to about 29MB through this exact pass — close to a third smaller. Your actual result depends on how many high-resolution maps the model carries; a prop with one 1K color texture won't shrink nearly as much as a vehicle with six 4K PBR sets across paint, chrome, and glass.
Which engines actually load it
![]()
| Engine | KTX2 / Basis Universal support | What to do |
|---|---|---|
| Blender (re-import) | No — same addon gap blocks reading it back in | Keep an uncompressed master file; only compress the GLB you ship |
| Godot 4.6+ | Yes — glTF import loads KTX2/BasisU textures | Older 4.x throws `KHR_texture_basisu is not supported`; update the engine rather than the texture |
| Unity | Yes, via official packages | Install `com.unity.cloud.gltfast` and `com.unity.cloud.ktx` together, then import the GLB normally |
| Unreal Engine 5.7 | Not confirmed in the Interchange glTF path | Ship PNG/JPEG for UE5 targets, or pre-convert to BC7/DDS instead |
Get the base export right before any of this matters — our Blender 5.0 glTF export settings guide covers the settings that keep materials and scale intact on the way in.
Test it before you script the whole library
Grab one texture-heavy asset and run both commands on it before pointing the pipeline at hundreds of files. A model with chrome, glass, and painted metal in the same material set is a good stress test, since that combination is exactly where ETC1S banding shows up first — the Concept Hypercar works well for this. A free account's two monthly downloads cover evaluation; commercial use is included with paid memberships — see pricing. Load the compressed output next to the original and check the chrome panels at a shallow viewing angle — that's where a wrong codec choice shows up first, not in a straight-on screenshot.
Where this still breaks
The Godot 4.6 support confirmed above was verified against the WebGL2 export target specifically. If you're shipping desktop or mobile, verify KTX2 actually loads on your export preset before committing a whole texture library to it — Godot's platform-specific texture pipelines don't always match feature-for-feature. `npx` also re-downloads the CLI package on every run unless it's cached locally, which is fine for one file and slow for a batch job; install `@gltf-transform/cli` globally instead once you're running it against more than a handful of models.
If you're pairing this with mesh compression too, `KHR_draco_mesh_compression` handles geometry the same way KTX2 handles textures — our glTF extensions and engine support breakdown covers what each engine actually decodes. Delivering the result to a browser instead of a game engine changes the loader setup again — see loading Draco and KTX2 GLBs in Three.js for the decoder paths WebGL and WebGPU need before either codec will render.
---
*This post is part of the Ultimate Guide to Free 3D Game Assets — BitSoul's complete reference for formats, texturing, rigging, optimization, and engine integration.*