Skip to content

glTF extensions

Sebastien DUMETZ edited this page Nov 3, 2026 · 1 revision

glTF extensions

Support status for the extensions handled by three.js's GLTFLoader, plus the others found in Khronos's sample assets. An extension is supported when running objtools import or objtools inspect on a .gltf or .glb that uses it works as expected.

Status measured at commit 0ff8c03 on 2026-11-03. glTF and GLB input describes what import does with the rest of the file.

Summary

Status Count Meaning
Supported 18 import keeps the extension in its output and inspect reports the right figures
Incomplete 2 Works, except for --convert-images true, which must decode the image
Not supported yet 3 Geometry compression. A file that needs it gets a clear error.

GLTFLoader's extensions

"Blender checks" counts the end-to-end comparisons of a sample using the extension whose render matched its source after import. See how this was tested for what those can and can't show.

Extension Applies to Status Blender checks Notes
KHR_lights_punctual Nodes Supported 40 --center moves lights with the model
KHR_materials_anisotropy Material Supported 24
KHR_materials_clearcoat Material Supported 49
KHR_materials_diffuse_roughness Material Supported None No sample. Checked on one generated file.
KHR_materials_dispersion Material Supported 16
KHR_materials_emissive_strength Material Supported 22
KHR_materials_ior Material Supported 65
KHR_materials_iridescence Material Supported 36
KHR_materials_sheen (1) Material Supported 35
KHR_materials_specular Material Supported 36
KHR_materials_transmission Material Supported 129
KHR_materials_unlit Material Supported 20
KHR_materials_volume Material Supported 95
EXT_materials_bump Material Supported None No sample. Checked on one generated file.
KHR_mesh_quantization Geometry Supported 4 Attributes keep their encoding
KHR_texture_transform Textures Supported 60 Always listed in extensionsRequired
EXT_mesh_gpu_instancing Nodes Supported 4 Instance transforms count in the --center bounds
EXT_texture_webp Textures Supported 6 --convert-images true converts it
KHR_texture_basisu Textures Incomplete None Blender can't load these files. --convert-images true is an error: it needs a KTX2 decoder.
EXT_texture_avif Textures Incomplete None No sample, covered by the test suite. --convert-images true is an error: it needs an AVIF decoder.
EXT_meshopt_compression Buffer views Not supported yet Refused Meshopt decoder, medium difficulty
KHR_meshopt_compression Buffer views Not supported yet Refused Same decoder, plus the v1 format and COLOR filter. Low difficulty once the EXT_ decoder exists.
KHR_draco_mesh_compression Primitives Not supported yet Refused Draco decoder, high difficulty

(1) KHR_materials_sheen is registered by GLTFLoader but missing from its documented list.

inspect only measures base-colour textures, so it ignores what the material extensions add. Textures using KHR_texture_basisu, EXT_texture_webp or EXT_texture_avif are measured from the extension's image, in the order GLTFLoader registers its plugins, and only their headers are read.

Required and unsupported extensions

  • An unsupported entry in extensionsRequired is an error, because the file can't be read correctly. That includes Draco and meshopt.
  • An optional Draco or meshopt extension with uncompressed data available uses that data. import drops the extension from its output with a warning.
  • An optional Draco extension without uncompressed data is an error. BoxDraco.gltf is such a file: it doesn't list Draco as required, and none of its accessors have a bufferView. The spec reads an accessor without a bufferView as zeros, which here would give zeroed geometry.
  • Any other unsupported extension is dropped with one counted warning.

Other extensions

These aren't in GLTFLoader's list, but Khronos's samples use them. None are supported. Each is dropped with a warning when optional, and refused when required.

Extension What is lost
KHR_materials_variants The alternative looks. It has its own warning: each primitive keeps its default material, and import drops the materials and images only the variants use. inspect takes occupancy from the default materials, so before import a texture only the variants use counts as unoccupied.
KHR_animation_pointer Animations that target a property through a pointer, such as a material's or a light's. The channels stay in the output with no target, so the validator gives each a VALUE_NOT_IN_LIST warning.
KHR_node_visibility Hidden nodes become visible.
KHR_materials_diffuse_transmission, KHR_materials_retroreflection, KHR_materials_volume_scatter The material effect. The rest of the material stays.
KHR_xmp, KHR_xmp_json_ld The metadata.
KHR_materials_pbrSpecularGlossiness Its only sample requires it, so it is refused.

Extension details

Image decoding

--convert-images true is the only operation on these formats that has to decode pixels. Sizes come from headers, and the transparency scan doesn't apply to glTF input.

Format --convert-images true What full support needs
WebP Works Nothing
AVIF Error: image is built without an AVIF decoder A decoder such as image's avif-native feature, which adds a native dav1d dependency
KTX2 Error: image has no KTX2 support A Basis Universal transcoder, plus handling for the transcoded output

KHR_texture_basisu and EXT_texture_avif stay incomplete until these are available.

Geometry compression

Draco and meshopt are left out for now.

When compression is added, data should be decoded where it is read rather than when a file is imported, and import should copy compressed data unchanged. Decoding at import would use more disk and add reads later, when most reads are already limited by I/O. It would also commit the file to uncompressed output: recompressing can add rounding, because Draco and meshopt filters quantize values, and a new encoder may choose a different grid.

Three consequences for import need designing at that point:

  • It still has to decode to run its checks.
  • Without an encoder, it can't remove zero-area triangles from compressed indices, so it can only count them in a warning.
  • --center has to apply at the scene roots.

These notes are estimates from reading the code. No decoder crate has been compiled, and the effect of recompression on geometry hasn't been measured.

  • Meshopt: decode each view on first use into a mapped workspace file, behind the reader's buffer-view lookup. This keeps heap use bounded, and inspect never needs to decode POSITION. The meshopt 0.7 crate (official C++ code through FFI) is the first choice, and its public ffi module accepts a caller's destination. meshopt-pure 1.3 is an unofficial pure Rust port and the alternative.
  • Draco: compressed data must be decoded as a whole, and draco-core 2.3 stores the result in Vec<u8> buffers. Heap use therefore grows with the largest primitive, which conflicts with the streaming constraint for a model stored as one large primitive. That crate is pure Rust and Apache-2.0, and claims parity with Draco 1.5.7, but is unofficial.

How this was tested

The end-to-end harness ran over all 150 Khronos sample models in Blender 5.2.1, with 634 checks in total: every variant of each model, and the plain glTF variant again with --center --up-axis z and as a re-import of that output. It ran on ff51884; the later commits only changed warnings and corrected mimeType.

Result Checks Meaning
Same 536 Pixels and scene data match
Pixels match, scene counts differ 54 Zero-area faces, omitted points and lines, or images only material variants use
Refused 25 16 Draco, 5 meshopt, 4 SpecGlossVsMetalRough
Not comparable 16 Blender can't load the source: KTX2, KHR_node_visibility, and a Blender error on AnimationPointerUVs
Different 3 MeshoptCubeTest, from a Blender bug: it decodes meshopt's COLOR filter with the exponential one, which miscolours six cubes of the source only

The largest difference among matching views was 0.013% of pixels, and no outline changed. Deliberately broken outputs of two models (a moved model, lost textures, flipped UVs, a lost primitive, a changed roughness, a wrong recorded origin) were all flagged.

What this doesn't show:

  • A match says nothing about an effect Blender doesn't draw, and nothing has been loaded in three.js.
  • Each model renders at a single frame, so skinning and morphs aren't compared in motion. Only the number of animations is compared.
  • The extensions without a sample rest on the test suite or one generated file, not on a visual comparison.

CI passes on Linux, macOS and Windows.

References

Conversion · Documentation home

Clone this wiki locally