threejs-gltf-loading
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesethree.js glTF Loading
three.js glTF 加载
Load / models and play their animations in three.js, including
compressed geometry (DRACO/Meshopt) and textures (KTX2). Patterns target
r165+, verified against r184.
.gltf.glb在three.js中加载 / 模型并播放其动画,包括支持压缩几何体(DRACO/Meshopt)和纹理(KTX2)。以下模式适用于 r165+ 版本,已在 r184 版本中验证。
.gltf.glbWhen to use
使用场景
- Use to import a 3D model, add it to the scene, inspect its node hierarchy, and
play baked/skinned animation clips with an .
AnimationMixer - Use when files are /
.gltf, or code imports.glb/GLTFLoader/DRACOLoaderfromKTX2Loader.three/addons/loaders/...
When not to use: creating the renderer/camera/loop → .
Tuning surface look, lights, or shadows on the loaded model →
. Authoring/exporting the model itself (Blender) is out
of scope; prefer glTF over OBJ/FBX for runtime.
threejs-scene-setupthreejs-materials-lighting- 用于导入3D模型、将其添加到场景中、检查节点层级结构,以及通过 播放烘焙/蒙皮动画片段。
AnimationMixer - 当文件格式为 /
.gltf,或代码从.glb导入three/addons/loaders/.../GLTFLoader/DRACOLoader时使用。KTX2Loader
不适用场景:创建渲染器/相机/循环 → 使用 。调整已加载模型的表面外观、灯光或阴影 → 使用 。模型的创作/导出(如Blender)不在本技能范围内;Web运行时优先使用glTF而非OBJ/FBX。
threejs-scene-setupthreejs-materials-lightingCore workflow
核心工作流程
- Why glTF. It's a transmission format: binary vertex data, PBR materials, and animations are ready to render with minimal parsing. Prefer it over OBJ (no scene graph, no animation) and FBX (heavy) for the web.
- Load with .
GLTFLoader. The resultloader.load(url, onLoad, onProgress, onError)hasgltf(thegltf.sceneroot),Object3D(gltf.animations),AnimationClip[], andgltf.cameras.gltf.asset - Add to your scene and frame it. Inspect the hierarchy with
gltf.scene/traverseto find the parts you'll control.getObjectByName - Play animations with an . One mixer per animated root;
AnimationMixer; advance withmixer.clipAction(clip).play()every frame.mixer.update(delta) - Decode compressed assets. Attach a (and/or
DRACOLoader+ Meshopt) so DRACO meshes and KTX2 textures load; point the decoders at their files.KTX2Loader - Verify what loaded — log the scene graph and , and confirm the model is visible (right scale, lit) and the clip actually plays.
gltf.animations
- 为什么选择glTF:它是一种传输格式,包含二进制顶点数据、PBR材质和动画,只需极少解析即可渲染。在Web环境中,它比OBJ(无场景图、无动画)和FBX(体积大)更合适。
- 使用 加载:调用
GLTFLoader。返回的loader.load(url, onLoad, onProgress, onError)对象包含gltf(gltf.scene根节点)、Object3D(gltf.animations数组)、AnimationClip[]和gltf.cameras。gltf.asset - 将 添加到场景并调整视角:使用
gltf.scene/traverse检查层级结构,找到需要控制的部分。getObjectByName - 通过 播放动画:每个动画根节点对应一个mixer;调用
AnimationMixer;每帧通过mixer.clipAction(clip).play()更新动画进度。mixer.update(delta) - 解码压缩资源:附加 (和/或
DRACOLoader+ Meshopt)以加载DRACO网格和KTX2纹理;将解码器指向对应的文件。KTX2Loader - 验证加载结果 —— 打印场景图和 ,确认模型可见(缩放正确、已打光)且动画片段正常播放。
gltf.animations
Patterns
示例模式
1. Load a model and frame it
1. 加载模型并调整视角
js
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const loader = new GLTFLoader();
loader.load(
'assets/robot.glb',
(gltf) => {
const root = gltf.scene;
scene.add(root);
// Inspect: gltf.animations is an array of AnimationClip.
console.log('clips:', gltf.animations.map((c) => c.name));
},
(event) => console.log(`${(event.loaded / event.total) * 100}% loaded`),
(error) => console.error('glTF load failed:', error)
);js
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const loader = new GLTFLoader();
loader.load(
'assets/robot.glb',
(gltf) => {
const root = gltf.scene;
scene.add(root);
// Inspect: gltf.animations is an array of AnimationClip.
console.log('clips:', gltf.animations.map((c) => c.name));
},
(event) => console.log(`${(event.loaded / event.total) * 100}% loaded`),
(error) => console.error('glTF load failed:', error)
);2. Play a skinned animation with AnimationMixer
2. 使用AnimationMixer播放蒙皮动画
js
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
let mixer; // declare outside so the loop can see it
const clock = new THREE.Clock();
new GLTFLoader().load('assets/character.glb', (gltf) => {
scene.add(gltf.scene);
mixer = new THREE.AnimationMixer(gltf.scene); // one mixer per animated root
const clip = THREE.AnimationClip.findByName(gltf.animations, 'Run')
?? gltf.animations[0];
mixer.clipAction(clip).play();
});
renderer.setAnimationLoop(() => {
const dt = clock.getDelta();
if (mixer) mixer.update(dt); // advance the animation by real seconds
renderer.render(scene, camera);
});js
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
let mixer; // declare outside so the loop can see it
const clock = new THREE.Clock();
new GLTFLoader().load('assets/character.glb', (gltf) => {
scene.add(gltf.scene);
mixer = new THREE.AnimationMixer(gltf.scene); // one mixer per animated root
const clip = THREE.AnimationClip.findByName(gltf.animations, 'Run')
?? gltf.animations[0];
mixer.clipAction(clip).play();
});
renderer.setAnimationLoop(() => {
const dt = clock.getDelta();
if (mixer) mixer.update(dt); // advance the animation by real seconds
renderer.render(scene, camera);
});3. Cross-fade between two clips
3. 两个动画片段之间交叉淡入淡出
js
const actions = {};
mixer = new THREE.AnimationMixer(gltf.scene);
for (const clip of gltf.animations) {
actions[clip.name] = mixer.clipAction(clip);
}
actions['Idle'].play();
function transitionTo(name, duration = 0.3) {
const next = actions[name];
next.reset().play();
for (const [n, action] of Object.entries(actions)) {
if (n !== name) action.crossFadeTo(next, duration, false);
}
}js
const actions = {};
mixer = new THREE.AnimationMixer(gltf.scene);
for (const clip of gltf.animations) {
actions[clip.name] = mixer.clipAction(clip);
}
actions['Idle'].play();
function transitionTo(name, duration = 0.3) {
const next = actions[name];
next.reset().play();
for (const [n, action] of Object.entries(actions)) {
if (n !== name) action.crossFadeTo(next, duration, false);
}
}4. DRACO-compressed geometry
4. DRACO压缩几何体
js
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
const draco = new DRACOLoader();
// Point at the decoder files you ship (or a pinned CDN copy of the same version).
draco.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.184.0/examples/jsm/libs/draco/');
const loader = new GLTFLoader();
loader.setDRACOLoader(draco);
loader.load('assets/city-draco.glb', (gltf) => scene.add(gltf.scene));js
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
const draco = new DRACOLoader();
// Point at the decoder files you ship (or a pinned CDN copy of the same version).
draco.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.184.0/examples/jsm/libs/draco/');
const loader = new GLTFLoader();
loader.setDRACOLoader(draco);
loader.load('assets/city-draco.glb', (gltf) => scene.add(gltf.scene));5. Find and animate a named part
5. 查找并动画化指定名称的部件
js
new GLTFLoader().load('assets/car.glb', (gltf) => {
scene.add(gltf.scene);
const wheels = [];
gltf.scene.traverse((node) => {
if (node.name.startsWith('Wheel')) wheels.push(node);
});
renderer.setAnimationLoop(() => {
const dt = clock.getDelta();
for (const w of wheels) w.rotation.x += dt * 4;
renderer.render(scene, camera);
});
});js
new GLTFLoader().load('assets/car.glb', (gltf) => {
scene.add(gltf.scene);
const wheels = [];
gltf.scene.traverse((node) => {
if (node.name.startsWith('Wheel')) wheels.push(node);
});
renderer.setAnimationLoop(() => {
const dt = clock.getDelta();
for (const w of wheels) w.rotation.x += dt * 4;
renderer.render(scene, camera);
});
});Pitfalls
常见陷阱
- Model loads but is invisible → it has lit (PBR) materials and the scene has no
light or environment. Add a light or (see
scene.environment), and check scale — glTF is in metres, so a 0.01-scaled asset is tiny.threejs-materials-lighting - is async →
loadonly exists inside the callback; declaregltf/refs outside and assign them in the callback, or usemixer.await loader.loadAsync(url) - Animation never moves → you didn't call each frame, or you passed milliseconds instead of seconds (use
mixer.update(delta)), or you forgotclock.getDelta().action.play() - DRACO/KTX2 model fails → the decoder/transcoder path is wrong or version-
mismatched. /
setDecoderPathmust point at files matching your three.js version.setTranscoderPath - Multiple mixers fighting → use one per animated root and create all actions from it; don't make a new mixer per clip.
AnimationMixer - Baked-in transforms surprise you → exporters sometimes bake scale/rotation onto child nodes. Dump the hierarchy (names + position/rotation/scale) before relying on a node's local transform; re-export from the source if the rig is unusable.
- Origins are off → re-parent a part under a fresh to give it a clean pivot rather than fighting baked offsets.
Object3D
- 模型已加载但不可见 → 模型使用了受光照影响的(PBR)材质,但场景中没有灯光或环境。添加灯光或设置 (参考
scene.environment),并检查缩放比例——glTF使用米作为单位,所以缩放0.01的资产会非常小。threejs-materials-lighting - 是异步操作 →
load仅在回调函数内存在;在外部声明gltf/引用并在回调中赋值,或使用mixer。await loader.loadAsync(url) - 动画始终不播放 → 未在每帧调用 ,或传入的是毫秒而非秒(使用
mixer.update(delta)),或忘记调用clock.getDelta()。action.play() - DRACO/KTX2模型加载失败 → 解码器/转码器路径错误或版本不匹配。/
setDecoderPath必须指向与你的three.js版本匹配的文件。setTranscoderPath - 多个mixer冲突 → 每个动画根节点使用一个 ,并从中创建所有动作;不要为每个动画片段创建新的mixer。
AnimationMixer - 烘焙变换导致意外问题 → 导出器有时会将缩放/旋转烘焙到子节点上。在依赖节点的局部变换之前,先导出层级结构(名称 + 位置/旋转/缩放);如果绑定不可用,从源文件重新导出。
- 原点偏移 → 将部件重新父化到新的 下,以获得干净的轴心点,而非修改烘焙的偏移量。
Object3D
References
参考资料
- For the full decode/transcode setup (DRACO + Meshopt + KTX2 together),
loadAsync- a progress bar, reusing models with
LoadingManager, and exporter guidance (apply transforms, one clean root), readSkeletonUtils.clone.references/loaders-and-animation.md
- a
- 如需完整的解码/转码设置(同时使用DRACO + Meshopt + KTX2)、+
loadAsync进度条、使用LoadingManager复用模型,以及导出指南(应用变换、单一干净根节点),请阅读SkeletonUtils.clone。references/loaders-and-animation.md
Related skills
相关技能
- — the renderer, camera, and loop this model renders into.
threejs-scene-setup - — lighting/environment so PBR models look right.
threejs-materials-lighting - — a 3D genre that composes three.js skills.
fps-shooter
- —— 模型渲染所需的渲染器、相机和循环。
threejs-scene-setup - —— 使PBR模型显示正常的灯光/环境设置。
threejs-materials-lighting - —— 组合多种three.js技能的3D游戏类型。
fps-shooter