Skip to main content

glTF Extension Support

Fromv9.3

This page summarizes the glTF extensions that @luma.gl/gltf handles in its default loader-to-scenegraph path. Each extension name links to its official Khronos-managed specification page in the glTF extension registry repository.

For a complete application walkthrough, including source JSON, public TypeScript APIs, real instanced draws, material selection, recursive mesh/light visibility, typed camera and light animation, capability diagnostics, and official sample coverage, see native glTF extensions.

Status meanings:

  • : works end-to-end in the default luma.gl glTF pipeline.
  • ✅ *️⃣: works in the default pipeline, with low-level decoding handled by @loaders.gl/gltf during load.
  • 🚧: partially implemented; the linked row states which paths are available and which limitations remain.
  • *️⃣: extension data is preserved or exposed, but applications still need to decide how to use it at runtime.
  • App: applications are responsible for selecting and applying the feature.
  • : no built-in handling today.

GLTFLoader already owns container parsing, linked resources, typed/deinterleaved accessor postprocessing, Draco and EXT_meshopt_compression decoding, Basis/KTX2 texture selection, conditional WebP selection, and supported 3D Tiles feature/structural metadata. The rendering package consumes those results; it does not need independent mesh codecs, accessor decoders, or GLB container serialization. postProcessGLTF() must still be called explicitly in loaders.gl v4.

ExtensionSupportNotes
✅ *️⃣

@loaders.gl/gltf decompresses Draco meshes and converts them to regular glTF meshes.

✅ *️⃣

@loaders.gl/gltf already decodes EXT meshopt-compressed buffer views during load; luma.gl consumes the resulting accessor values.

The newer Khronos release-candidate spelling is not implemented by the installed @loaders.gl/gltf decoder. Supporting it belongs in loaders.gl first.

✅ *️⃣

loaders.gl materializes typed quantized accessor values and normalization metadata; luma.gl preserves the GPU attribute format or expands morph bases only when needed.

*️⃣

@loaders.gl/gltf decodes supported feature identifiers. Automatic rendering, picking, and application-specific feature queries are not provided.

*️⃣

@loaders.gl/gltf decodes supported structural metadata and property tables. Applications remain responsible for interpreting and presenting that data.

Accessor-backed translation, rotation, and scale become one real instanced draw per source primitive on WebGL and WebGPU. Aggregate bounds include every instance, and application-defined _NAME attributes remain available.

loaders.gl exposes the authored light definitions; luma.gl creates directional, point, and spot lights that preserve intensity, range, source-node world transforms, and spotlight inner/outer cones. Use parseGLTFLights(gltf, {useByteColors: false}) for source-linear RGB colors.

loaders.gl preserves the unlit marker; luma.gl bypasses the default lighting calculations when creating the material.

✅ 🚧

Emissive strength now multiplies the emissive contribution in the stock PBR shader.

✅ 🚧

The stock shader now applies specular intensity and color factors and textures to the dielectric F0 term.

✅ 🚧

The stock shader now drives dielectric reflectance from the glTF IOR value.

✅ 🚧

Material factors and textures are preserved. The shared experimental SceneRenderer and ANARI retained path refract captured opaque-scene color; the standalone glTF scenegraph path retains its alpha-based approximation.

✅ 🚧

Experimental, unratified proposal. Linear red-channel height textures perturb the existing tangent-space normal, preserve per-slot UV transforms, and compose with conventional normal maps.

✅ 🚧

Khronos release candidate, not ratified. Canonical WebGL and WebGPU PBR shading implements opposite-hemisphere punctual lighting and IBL, energy-conserving front-diffuse reduction, linear alpha factor maps, and sRGB transmission colors.

✅ 🚧

Thickness factor plus attenuation color and distance now tint transmitted light in the stock shader.

✅ 🚧

Authored dispersion is parsed into the canonical PBR material. Shared experimental and ANARI rendering separate transmitted RGB wavelengths using the ratified wavelength-dependent IOR. A supported material animation pointer can update the same physical dispersion uniform.

🚧

Active, unratified draft. A local thickness-aware, anisotropic single-scattering approximation is wired into diffuse transmission. Requires KHR_materials_volume; screen-space diffusion and random-walk multiple scattering are not implemented.

✅ 🚧

The stock shader now adds a secondary clearcoat lobe from the clearcoat and clearcoat-roughness inputs.

✅ 🚧

The stock shader now adds a sheen lobe using the sheen color and roughness inputs.

✅ 🚧

The stock shader now tints specular response with a view-dependent thin-film iridescence approximation driven by the extension inputs.

✅ 🚧

The stock shader now shapes highlights and IBL response with an anisotropy-direction approximation.

*️⃣

loaders.gl can preserve the extension data, but @luma.gl/gltf does not translate it into the default metallic-roughness shader path.

scenegraphs.variants.selectVariant(name) switches authored primitive materials without replacing scenegraph nodes; resetVariant() restores source defaults and pipeline state.

✅ *️⃣

BasisU / KTX2 textures are passed through as compressed textures when supported by the device. The runtime checks the transcoded GPU format before upload; optional unsupported formats use a 1×1 fallback, and required BasisU assets fail strictExtensions instead of issuing an invalid backend texture operation.

✅ *️⃣

@loaders.gl/gltf resolves the texture source; runtime support still depends on browser and device decode support.

The generic image loader can decode AVIF when the browser supports it, but the installed glTF loader does not select texture sources from this extension.

All 21 supported material slots retain independent offset, rotation, scale, and authored TEXCOORD_0 / TEXCOORD_1 selection. The runtime shader path must not be combined with duplicate legacy loader-side UV baking.

✅ 🚧

Node TRS, morph-weight, and recursive boolean visibility pointers; selected material factor pointers; and animated KHR_texture_transform offset/rotation/scale pointers across all 21 supported slots are wired to runtime updates. Perspective/orthographic camera properties and typed punctual-light color, intensity, range, spotlight angles, and physical chromatic-dispersion factors are also supported. Boolean visibility uses authored STEP interpolation and refreshes punctual lights. Still unsupported: extras, structural material switches such as alphaMode / doubleSided / unlit, animated KHR_texture_transform.texCoord, and texture slots that resolve to TEXCOORD_2+. When used with KHR_materials_transmission / KHR_materials_volume, the shared experimental renderer and ANARI can refract captured scene color, while the standalone glTF scenegraph path still uses its alpha approximation.

Source-authored visibility recursively hides mesh descendants and punctual lights. Visibility-pointer animation updates existing scenegraph nodes and preserves the identity of the exported light array.

Metadata payloads remain in the loaded glTF, but luma.gl does not interpret them.

Metadata is available in the loaded glTF, but luma.gl does not interpret it.

Use loadPBREnvironment() or custom environment setup instead of relying on automatic extension handling. The current Khronos sample-asset catalog does not include an official EXT_lights_image_based sample.

Video textures are not created automatically by the glTF pipeline.

MSFT_lodFromv10.0

Animated crowds resolve authored node levels and MSFT_screencoverage thresholds, select independently animated actors by projected screen size, and compact each occupied mesh level into one instanced draw per primitive on WebGPU and WebGL 2. Optional index-only mesh decimation generates levels for assets that do not author them. Material-level LOD, GPU-driven selection, and progressive streaming are not implemented.

Inspect runtime capabilities

Source extension declarations can be inspected before scenegraph creation:

import {
assertSupportedGLTFExtensions,
createScenegraphsFromGLTF,
getGLTFExtensionSupport,
getGLTFExtensionSupportSummary,
getRegisteredGLTFExtensions,
getUnsupportedRequiredGLTFExtensions
} from '@luma.gl/gltf';

const registrySummary = getGLTFExtensionSupportSummary();
console.log(
`${registrySummary.supported}/${registrySummary.total} registered extensions have complete runtime support`
);

for (const registeredExtension of getRegisteredGLTFExtensions()) {
console.log(
registeredExtension.extensionName,
registeredExtension.supportLevel,
registeredExtension.standardStatus
);
}

for (const capability of getGLTFExtensionSupport(gltf).values()) {
console.log(capability.extensionName, {
required: capability.required,
supported: capability.supported,
level: capability.supportLevel,
standardStatus: capability.standardStatus,
explanation: capability.comment
});
}

const unsupportedRequired = getUnsupportedRequiredGLTFExtensions(gltf);

if (unsupportedRequired.length > 0) {
assertSupportedGLTFExtensions(gltf);
}

const scenegraphs = createScenegraphsFromGLTF(device, gltf, {
strictExtensions: true
});

built-in and parsed-and-wired capabilities satisfy required-extension checks. loader-only capabilities, including browser-dependent WebP selection and decoded feature/structural metadata, do not promise complete portable runtime support. AVIF source selection, unknown extensions, and other none capabilities also fail when required. Optional unsupported extensions remain visible in the report without preventing scene creation.

The returned scenegraphs.extensionSupport preserves the document-specific capability report. Strict checks run before model creation, so an unsupported required feature does not leave a partially constructed GPU scene behind.

Use authored runtime extensions

import {getGLTFNodeInstancing} from '@luma.gl/gltf';

scenegraphs.variants.selectVariant('Midnight');
scenegraphs.variants.resetVariant();

const instancing = getGLTFNodeInstancing(gltf, gltf.nodes[0]);
console.log(instancing?.matrices.length);

scenegraphs.animator.selectClip('Night lighting', {
crossFadeDuration: 0.3
});
scenegraphs.animator.setTime(1000);

console.log(scenegraphs.cameras[0]);
console.log(scenegraphs.lights);

Variant selection preserves existing scenegraph node/model identities and restores unmapped primitives to their authored default materials. Instancing submits one GPU draw per source primitive on both WebGL and WebGPU. Visibility and punctual-light pointers update stable retained scene objects through the existing shared animation mixer. Automatic source skin palettes and morph targets are evaluated in the same frame.

Notes

  • getGLTFExtensionSupport(gltf) distinguishes optional and required extensions. Use getUnsupportedRequiredGLTFExtensions(gltf), assertSupportedGLTFExtensions(gltf), or createScenegraphsFromGLTF(device, gltf, {strictExtensions: true}) to reject unsupported required features instead of silently degrading them.
  • getGLTFNodeInstancing(gltf, node) exposes resolved source matrices and all authored instance-accessor semantics, including application-defined _NAME attributes.
  • The built-in material extension rows reuse the shared pbrMaterial shader. The experimental SceneRenderer and ANARI capture opaque scene color for screen-space transmission/refraction; the standalone createScenegraphsFromGLTF() rendering path does not perform that capture and retains its alpha fallback. Iridescence and anisotropy still use pragmatic shared-shader approximations.
  • The supported material extensions wire all 21 canonical core/extension texture slots, including auxiliary specular, clearcoat, sheen, iridescence, anisotropy, transmission, and volume maps. The physical response of some advanced effects remains an approximation.
  • @luma.gl/gltf relies on @loaders.gl/gltf for low-level extension decoding, decompression, accessor post-processing, and binary GLB container encoding. The loader's legacy CPU texture-transform preprocessing and the runtime's animated shader transforms must not both apply the same authored UV transform.
  • Core glTF skin attributes and animated morph targets are supported independently of these extension rows; source skin palettes are updated automatically once per animation frame. See glTF animation and deformation, native glTF extensions, and glTF materials and textures.