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 專案中載入 `.gltf`/`.glb` 模型而設計,目標版本為 r187。它提供完整的模型載入流程,包括使用 `GLTFLoader`、處理 DRACO、Meshopt 和 KTX2 等壓縮格式,以及透過 `AnimationMixer` 播放和混合動畫剪輯。該技能也涵蓋模型除錯,例如模型無法顯示、動畫無法播放、或共用骨架導致克隆動畫同步問題。此外,它也提供使用 `@gltf-transform/cli` 進行模型最佳化和驗證的指引。此技能不處理渲染器、相機、光照或材質設定,這些功能由相關的 `threejs-scene-setup` 和 `threejs-materials-lighting` 技能負責。

相容平台~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;現有專案會保留其固定的發行版本,除非使用者要求升級。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('尺寸 (m):', 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);
// 所有模型共用此載入器,以便解碼器和 worker 僅被建立一次。

在 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.

相關技能