Chargement de modèles glTF/GLB dans three.js

Charge des modèles .gltf/.glb dans three.js r187 et joue leurs animations : GLTFLoader, décodeurs DRACO, Meshopt et KTX2, fondus enchaînés AnimationMixer et clones SkeletonUtils. À utiliser lorsqu'un modèle est invisible ou minuscule, que les fichiers décodeurs renvoient une erreur 404, que les animations ne se jouent jamais ou que des clones partagent un seul squelette. Ne concerne pas la configuration de scène (`threejs-scene-setup`) ni l'éclairage (`threejs-materials-lighting`).

Qu'est-ce que GLB dans three.js ?

Cette compétence permet de charger des modèles 3D au format glTF/GLB dans three.js r187, y compris avec des géométries compressées (DRACO, Meshopt) et des textures KTX2, et de lire leurs animations. Elle couvre l'importation, l'inspection de la hiérarchie, la lecture et le fondu enchaîné de clips d'animation, le clonage de personnages animés avec SkeletonUtils, et le débogage des problèmes courants (modèle invisible, fichiers décodeurs manquants, animations qui ne démarrent pas). Utilisez cette compétence lorsque vous codez directement dans un projet three.js existant avec `GLTFLoader` et que vous devez gérer des modèles animés ou compressés. Elle ne traite pas de la configuration de la scène (rendu, caméra, boucle) ni de l'éclairage, qui sont couverts par des compétences séparées.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/mext-ai/elearning-agent-skills/tree/HEAD/third-party/gamedev/threejs-gltf-loading

Installed? Explore more Développement et programmation skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Que fait GLB dans three.js ?

Charge des modèles .gltf / .glb, y compris avec géométrie compressée (DRACO, Meshopt) et textures (KTX2), et joue leurs clips d'animation. Les patrons ciblent r187 ; un projet existant conserve sa version épinglée sauf si l'utilisateur demande une mise à niveau. glTF est le format d'exécution par défaut : OBJ n'a ni graphe de scène ni animation, et FBX est lourd à analyser dans un navigateur.

Quand l'utiliser

  • Importer un modèle ou un personnage, inspecter sa hiérarchie de nœuds, jouer ou fondre des clips, générer de nombreuses copies d'un ennemi animé, ou corriger un modèle qui n'apparaît pas.
  • Les fichiers sont .gltf / .glb, ou le code importe GLTFLoader, DRACOLoader ou KTX2Loader depuis three/addons/loaders/....
  • Ne concerne pas le rendu, la caméra et la boucle (threejs-scene-setup), l'éclairage et les matériaux du modèle (threejs-materials-lighting), ni les paramètres de modélisation et d'exportation dans un outil DCC.

Flux de travail

  1. Inspecter le fichier avant d'écrire le code de chargement lorsque gltf-transform est disponible : npx @gltf-transform/cli inspect model.glb liste les maillages, extensions (par exemple KHR_draco_mesh_compression, EXT_meshopt_compression, KHR_texture_basisu), clips et tailles de texture. Chaque extension de compression listée nécessite que son décodeur soit attaché.
  2. Charger avec GLTFLoader (loadAsync en code asynchrone). Le résultat a gltf.scene, le Object3D racine, et gltf.animations, un AnimationClip[].
  3. Ajouter gltf.scene et le cadrer. Les unités glTF sont en mètres ; enregistrer les noms et les limites pour trouver les parties que le code contrôlera.
  4. Un AnimationMixer par racine animée, actions via mixer.clipAction(clip), et mixer.update(secondes) à chaque image.
  5. Attacher les décodeurs pour les extensions trouvées à l'étape 1. Depuis r185, DRACOLoader et KTX2Loader trouvent leurs fichiers décodeurs intégrés sans chemin.
  6. Vérifier avec Vérification.

Patrons

1. Charger et cadrer un modèle

import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

const gltf = await new GLTFLoader().loadAsync('assets/robot.glb');
const root = gltf.scene;
scene.add(root);

const box = new THREE.Box3().setFromObject(root);
console.log('taille (m) :', box.getSize(new THREE.Vector3()).toArray());
console.log('clips :', gltf.animations.map((c) => c.name));

2. Jouer un clip

const timer = new THREE.Timer();            // THREE.Clock est déprécié depuis r183
const mixer = new THREE.AnimationMixer(gltf.scene);
const run = THREE.AnimationClip.findByName(gltf.animations, 'Run') ?? gltf.animations[0];
mixer.clipAction(run).play();

renderer.setAnimationLoop(() => {
  timer.update();
  mixer.update(timer.getDelta());           // secondes, pas millisecondes
  renderer.render(scene, camera);
});

3. Fondu enchaîné entre des clips

const actions = Object.fromEntries(gltf.animations.map((c) => [c.name, mixer.clipAction(c)]));
let current = actions.Idle;
current.play();

function transitionTo(name, duration = 0.3) {
  const next = actions[name];
  if (!next || next === current) return;
  next.reset().play();                      // rembobiner et effacer un ancien fondu
  current.crossFadeTo(next, duration, false);
  current = next;
}

4. Géométrie et textures compressées

import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader, DRACO_GLTF_CONFIG } from 'three/addons/loaders/DRACOLoader.js';
import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js';
import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js';

const loader = new GLTFLoader()
  .setDRACOLoader(new DRACOLoader().setDecoderPath(DRACO_GLTF_CONFIG)) // build WASM glTF uniquement (r185+)
  .setKTX2Loader(new KTX2Loader().detectSupport(renderer))             // nécessite le rendu
  .setMeshoptDecoder(MeshoptDecoder);
// Partager ce seul chargeur pour chaque modèle afin que les décodeurs et workers soient créés une fois.

Sur les versions antérieures à r185, ou lorsque l'hôte ne sert pas les fichiers intégrés, copier examples/jsm/libs/draco/ et libs/basis/ depuis le dossier three installé et passer ces dossiers à setDecoderPath / setTranscoderPath.

5. Trouver une partie nommée

const wheels = [];
gltf.scene.traverse((node) => {
  if (node.name.startsWith('Wheel')) wheels.push(node);
});
// Dans la boucle : for (const w of wheels) w.rotation.x += dt * 4;

6. Réduire un modèle sans perdre les parties dont le code a besoin

# Compression Meshopt, textures WebP de 1024 px max, conserver les nœuds nommés et la hiérarchie.
npx @gltf-transform/cli optimize in.glb out.glb \
  --compress meshopt --texture-compress webp --texture-size 1024 \
  --flatten false --join-named false
npx @gltf-transform/cli validate out.glb

optimize aplatit la hiérarchie et fusionne les maillages, y compris les nommés, par défaut. Cela réduit les appels de dessin, mais le code qui cherche Wheel_FL ou anime un nœud de porte ne trouve alors rien. Conserver les deux indicateurs ci-dessus pour tout modèle que le code manipule. La sortie nécessite setMeshoptDecoder (Patron 4).

Vérification

  1. Vérification du fichier : npx @gltf-transform/cli validate model.glb pour chaque fichier nouveau ou modifié ; corriger les erreurs signalées avant de déboguer le code de chargement.
  2. Build : npm run build (ou npx tsc --noEmit) passe.
  3. Son de runtime : exposer scene et le mixer sur window (Object.assign(window, { scene, mixer })). Passer cette expression à page.evaluate de Playwright sous forme de chaîne, pour qu'elle s'exécute dans la page et non dans Node (le script de test dans threejs-scene-setup fonctionne), ou demander à l'utilisateur de la coller dans la console du navigateur avec await devant et de rapporter la sortie :
(async () => {
  const before = window.mixer?.time ?? null;
  await new Promise((r) => setTimeout(r, 500));
  const meshes = [];
  window.scene.traverse((o) => { if (o.isMesh) meshes.push(o.name || o.type); });
  return { meshes: meshes.length, mixerAdvanced: before !== null && window.mixer.time > before };
})();

Attendre un nombre de maillages non nul et mixerAdvanced: true, puis vérifier une capture d'écran pour l'échelle et l'orientation.

Pièges

  • Le modèle se charge mais rien ne s'affiche → Matériaux PBR sans lumière ni environnement (voir threejs-materials-lighting), ou l'échelle est erronée d'un facteur 100 (export en centimètres). Enregistrer la boîte englobante du Patron 1.
  • gltf est indéfini en dehors du callback → load est asynchrone. Utiliser loadAsync, ou assigner des références partagées à l'intérieur du callback.
  • L'animation ne bouge jamais → mixer.update n'est pas appelé à chaque image, on lui donne des millisecondes, ou play() n'a jamais été appelé.
  • Erreur 404 pour les fichiers draco_* ou basis_*, ou une erreur de décodage → les fichiers décodeurs ne sont pas servis, ou proviennent d'une autre version de three.js. Servir les copies depuis le dossier three installé et pointer le chargeur vers elles.
  • setKTX2Loader must be called before loading KTX2 textures ou setMeshoptDecoder must be called before loading compressed files → le fichier utilise KHR_texture_basisu ou la compression meshopt ; attacher le décodeur (Patron 4).
  • .hasFeature() called before the backend is initialized → KTX2Loader.detectSupport a été exécuté sur un WebGPURenderer avant la fin de await renderer.init().
  • Ajouter le même modèle deux fois le déplace → un Object3D a un seul parent. Le cloner, et utiliser SkeletonUtils.clone pour les maillages skin (voir la référence).
  • Les clones s'animent ensemble, ou un seul bouge → un .clone() simple partage le squelette ; utiliser SkeletonUtils.clone et un mixer séparé par clone.
  • Les parties nommées disparaissent après gltf-transform optimize → l'aplatissement et la fusion par défaut les ont fusionnées. Relancer avec --flatten false --join-named false.
  • Les décalages cuits au four entrent en conflit avec le contrôle runtime → les exportateurs peuvent cuire l'échelle ou la rotation dans les nœuds enfants. Vider d'abord les noms et les transformations locales ; reassigner une partie sous un nouveau Object3D pour un pivot propre, ou demander une ré-exportation avec transformations appliquées.

Références

  • Lire references/loaders-and-animation.md pour une barre de progression LoadingManager pour plusieurs chargeurs, générer de nombreux ennemis animés avec SkeletonUtils.clone, les détails de bouclage et de mélange AnimationAction, les notes d'hébergement de décodeur, et une liste de contrôle pour l'exportateur à remettre à un artiste.

Compétences connexes

  • threejs-scene-setup : rendu, caméra et boucle.
  • threejs-materials-lighting : éclairage et environnement pour que les modèles PBR aient un bon aspect.
  • create-game-assets : briefing, recherche et vérification des actifs 3D eux-mêmes.
  • fps-shooter : un genre 3D qui s'appuie sur ces compétences.

Notes de version

Dernière vérification : 2026-11-11 avec three r187 (npm [email protected], publié le 2026-10-30) et @gltf-transform/cli 4.5.1.

  • r185 : DRACOLoader et KTX2Loader se réfèrent par défaut à leurs dossiers de décodeurs intégrés ; DRACO_GLTF_CONFIG ajouté ; DRACOLoader.setDecoderConfig() déprécié (suppression prévue pour r194). r183 : KHR_meshopt_compression supporté en plus de EXT_meshopt_compression. r181 : KTX2Loader.detectSupportAsync() déprécié.
  • Paramètres par défaut de gltf-transform 4.5.1 optimize : --compress meshopt, --flatten true, --join true, --join-named true, --simplify true, --texture-compress auto. --texture-compress ktx2 nécessite la commande ktx de KTX-Software 4.4.0 ou plus récent sur la variable d'environnement PATH.
  • Sources : Notes de version r187, r187 DRACOLoader.js, r187 GLTFLoader.js, CLI glTF Transform, npx @gltf-transform/cli optimize --help.

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

Skills associés