Community编程与开发github.com

threejs-gltf-loading

将 glTF/GLB 模型加载到 three.js r187 中并播放其动画:支持 GLTFLoader、DRACO、Meshopt 和 KTX2 解码器、AnimationMixer 交叉淡入淡出以及 SkeletonUtils 克隆。适用于模型不可见或极小、解码器文件 404、动画从未播放或克隆共享同一骨骼的情况。不适用于场景设置(`threejs-scene-setup`)或光照/材质(`threejs-materials-lighting`)。

threejs-gltf-loading 是什么?

该技能专注于在 three.js r187 环境中高效加载和动画化 glTF/GLB 模型。它提供了完整的解决方案:从基础加载(使用 GLTFLoader)到处理压缩几何体(支持 Draco、Meshopt 解码器)和压缩纹理(KTX2),再到复杂的动画控制,包括跨片段淡入淡出、动画混合以及使用 SkeletonUtils 克隆带有蒙皮的模型。技能还强调了通过 gltf-transform CLI 进行文件验证和优化,并提供了详细的故障排除指南(Gotchas),涵盖常见问题如模型不显示、动画不播放、解码器 404 错误以及克隆共享骨骼问题。它适用于需要导入角色、检查节点层次、播放或混合动画片段、批量生成动画敌人的开发者,但明确不与场景设置或光照/材质技能重叠。

兼容平台~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 编程与开发 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

three.js glTF 加载

加载 .gltf / .glb 模型,包括压缩几何体(DRACO、Meshopt)和纹理(KTX2),并播放其动画片段。代码模式针对 r187;现有项目将保持其固定的 three.js 版本,除非用户要求升级。glTF 是默认的首选运行时格式:OBJ 没有场景图或动画,而 FBX 在浏览器中解析较慢。

使用时机

  • 导入模型或角色、检查其节点层次、播放或混合片段、生成多个动画敌人副本,或修复无法显示的模型。
  • 文件是 .gltf / .glb,或者代码从 three/addons/loaders/... 导入 GLTFLoader、DRACOLoader 或 KTX2Loader。
  • 不适用于渲染器、相机和循环(threejs-scene-setup)、模型的材质和光照(threejs-materials-lighting),或 DCC 工具中的建模和导出设置。

工作流程

  1. 在编写加载代码之前检查文件(当 gltf-transform 可用时): npx @gltf-transform/cli inspect model.glb 列出网格、扩展(例如 KHR_draco_mesh_compression、EXT_meshopt_compression、KHR_texture_basisu)、片段和纹理尺寸。每个列出的压缩扩展都需要附加其解码器。
  2. 使用 GLTFLoader 加载(在异步代码中使用 loadAsync)。结果为 gltf.scene(根 Object3D)和 gltf.animations(一个 AnimationClip[])。
  3. 添加 gltf.scene 并为其定界。glTF 单位是米;记录名称和边界以找到代码将控制的部分。
  4. 每个动画根一个 AnimationMixer,从 mixer.clipAction(clip) 创建动作,并每帧调用 mixer.update(seconds)。
  5. 附加解码器,用于步骤 1 中找到的扩展。自 r185 起,DRACOLoader 和 KTX2Loader 会自动找到其捆绑的解码器文件,无需指定路径。
  6. 验证,参考 验证 部分。

模式

1. 加载并定界模型

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('尺寸(米):', box.getSize(new THREE.Vector3()).toArray());
console.log('剪辑:', gltf.animations.map((c) => c.name));

2. 播放片段

const timer = new THREE.Timer();            // THREE.Clock 自 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());           // 秒,不是毫秒
  renderer.render(scene, camera);
});

3. 片段间交叉淡入淡出

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();                      // 倒带并清除旧的淡出效果
  current.crossFadeTo(next, duration, false);
  current = next;
}

4. 压缩几何体和纹理

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)) // 仅 glTF 的 WASM 构建(r185+)
  .setKTX2Loader(new KTX2Loader().detectSupport(renderer))             // 需要渲染器
  .setMeshoptDecoder(MeshoptDecoder);
// 所有模型共享此加载器,以便解码器和工作线程仅创建一次。

在 r185 之前的版本,或当主机不提供捆绑文件时,请从已安装的 three 复制 examples/jsm/libs/draco/ 和 libs/basis/ 文件夹,并将这些文件夹路径传递给 setDecoderPath / setTranscoderPath。

5. 查找指定名称的部分

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

6. 在不丢失代码所需部分的情况下缩小模型

# Meshopt 压缩,WebP 纹理最大 1024 像素,保留命名节点和层次结构。
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 默认会展平层次结构并合并网格(包括命名的)。这减少了绘制调用,但查找 Wheel_FL 或动画化门节点的代码将找不到任何内容。对于代码需要访问的任何模型,请保留上述两个标志。输出需要 setMeshoptDecoder(模式 4)。

验证

  1. 文件检查:对每个新的或更改的文件执行 npx @gltf-transform/cli validate model.glb;在调试加载器代码之前修复报告的错误。
  2. 构建:npm run build(或 npx tsc --noEmit)通过。
  3. 运行时探测:将 scene 和 mixer 暴露在 window 上(Object.assign(window, { scene, mixer }))。将此表达式作为字符串传递给 Playwright 的 page.evaluate,以便在页面中而非 Node 中运行(烟雾脚本在 threejs-scene-setup 中有效),或要求用户将其粘贴到浏览器控制台中,前面加上 await 并报告输出:
(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 };
})();

期望网格计数非零且 mixerAdvanced: true,然后检查屏幕截图以获得正确的缩放和方向。

注意事项

  • 模型加载成功但未显示 → 可能是 PBR 材质没有光照或环境(参见 threejs-materials-lighting),或比例偏差 100 倍(以厘米为单位导出)。记录模式 1 中的包围盒。
  • 回调外部 gltf 未定义 → load 是异步的。使用 loadAsync,或在回调内部分配共享引用。
  • 动画从不移动 → 未每帧调用 mixer.update,或者传递了毫秒而非秒,或者未调用 play()。
  • draco_* 或 basis_* 文件返回 404,或解码错误 → 解码器文件未提供,或来自另一个 three.js 版本。提供来自已安装 three 的副本,并将加载器指向它们。
  • setKTX2Loader must be called before loading KTX2 textures 或 setMeshoptDecoder must be called before loading compressed files → 文件使用了 KHR_texture_basisu 或 meshopt 压缩;附加解码器(模式 4)。
  • .hasFeature() called before the backend is initialized → KTX2Loader.detectSupport 在 await renderer.init() 完成之前在 WebGPURenderer 上运行。
  • 两次添加同一模型导致其移动 → 一个 Object3D 只能有一个父级。克隆它,对于蒙皮网格使用 SkeletonUtils.clone(参见参考资料)。
  • 克隆同时动画或仅一个移动 → 简单的 .clone() 共享骨骼;使用 SkeletonUtils.clone 并为每个克隆创建一个单独的 mixer。
  • 命名部分在 gltf-transform optimize 后消失 → 默认的展平和合并合并了它们。使用 --flatten false --join-named false 重新运行。
  • 烘焙的偏移与运行时控制冲突 → 导出器可能将缩放或旋转烘焙到子节点。首先转储名称和局部变换;将部分在新 Object3D 下重新父级以获得干净的轴点,或要求使用应用了变换的重新导出。

参考资料

  • 阅读 references/loaders-and-animation.md 以了解:用于多个加载器的 LoadingManager 进度条、使用 SkeletonUtils.clone 生成多个动画敌人、AnimationAction 循环和混合细节、解码器托管说明,以及交给艺术家的导出检查清单。

相关技能

  • threejs-scene-setup:渲染器、相机和循环。
  • threejs-materials-lighting:光照和环境,使 PBR 模型看起来正确。
  • create-game-assets:简报、采购和检查 3D 资产本身。
  • fps-shooter:一个 3D 游戏类型,建立在这些技能之上。

版本说明

最后验证:2026-11-11,针对 three r187(npm [email protected],2026-10-30 发布)和 @gltf-transform/cli 4.5.1。

  • r185:DRACOLoader 和 KTX2Loader 默认使用其捆绑的解码器文件夹;添加了 DRACO_GLTF_CONFIG;DRACOLoader.setDecoderConfig() 已弃用(计划在 r194 中移除)。r183:KHR_meshopt_compression 与 EXT_meshopt_compression 一同受支持。r181:KTX2Loader.detectSupportAsync() 已弃用。
  • gltf-transform 4.5.1 optimize 默认值:--compress meshopt、--flatten true、--join true、--join-named true、--simplify true、--texture-compress auto。--texture-compress ktx2 需要 PATH 中有 KTX-Software 4.4.0 或更新版本的 ktx 命令。
  • 来源:r187 发布说明、r187 DRACOLoader.js、r187 GLTFLoader.js、glTF Transform CLI、npx @gltf-transform/cli optimize --help。

Individual skills in this repo

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

相关技能