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 工具中的建模和匯出設定。
工作流程
- 在編寫載入器程式碼前檢查檔案(若
gltf-transform可用):npx @gltf-transform/cli inspect model.glb可列出網格、擴展(例如KHR_draco_mesh_compression、EXT_meshopt_compression、KHR_texture_basisu)、剪輯和紋理大小。每個列出的壓縮擴展都需要附加其解碼器。 - 使用
GLTFLoader載入(在非同步程式碼中使用loadAsync)。結果包含gltf.scene(根目錄Object3D)和gltf.animations(一個AnimationClip[])。 - 新增
gltf.scene並框選它。 glTF 單位是公尺;記錄名稱和邊界以找到程式碼將控制的部分。 - 每個動畫根目錄一個
AnimationMixer,從mixer.clipAction(clip)取得動作,並在每一幀呼叫mixer.update(seconds)。 - 附加解碼器以處理步驟 1 中找到的擴展。從 r185 開始,
DRACOLoader和KTX2Loader無需路徑即可找到其捆綁的解碼器檔案。 - 驗證,參考 驗證 部分。
模式
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)。
驗證
- 檔案檢查:對每個新檔案或變更的檔案執行
npx @gltf-transform/cli validate model.glb;在除錯載入器程式碼之前先修復回報的錯誤。 - 建置:
npm run build(或npx tsc --noEmit)通過。 - 執行時期探查:將
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。