Three.js ‧ 載入外部模型 – GLTFLoader 載入 glTF / GLB
簡介
在 WebGL 與 Three.js 的開發流程中,模型的載入往往是最具挑戰性的環節。
與傳統的 OBJ、FBX 等格式相比,glTF(GL Transmission Format)和其二進位變體 GLB 以「JPEG of 3D」的美譽,提供了小檔案、快速解析、完整材質與動畫支援的優勢,已成為目前最主流的即時 3D 資源格式。
本單元將聚焦於 Three.js 官方提供的 GLTFLoader,說明如何在瀏覽器中快速、穩定地載入 glTF/GLB 檔案,並配合實務範例展示常見需求(材質調整、動畫播放、環境貼圖等)。不論你是剛踏入 Three.js 的新手,或是已具備基礎的前端開發者,都能從本文獲得可直接套用的技巧與最佳實踐。
核心概念
1. 為什麼選擇 glTF / GLB
| 特性 | glTF (JSON) | GLB (binary) |
|---|---|---|
| 檔案大小 | 可壓縮的 JSON + 外部貼圖 | 單一二進位檔,通常更小 |
| 載入速度 | 需要多次 HTTP 請求 | 只需一次請求 |
| 支援度 | 完整支援 PBR、動畫、skin、morph | 同上 |
| 適用情境 | 開發階段方便檢視 | 上線或行動裝置最佳化 |
結論:開發時可先使用
.gltf方便除錯,正式部署時建議轉為.glb以減少請求數量。
2. GLTFLoader 的基本使用流程
- 匯入
GLTFLoader(ES6 模組或 CDN) - 建立
loader實例 - 呼叫
load( url, onLoad, onProgress, onError ) - 取得
gltf.scene、gltf.animations、gltf.asset等屬性 - 加入 Three.js 場景並視需求調整
3. 重要屬性說明
| 屬性 | 型別 | 說明 |
|---|---|---|
gltf.scene |
THREE.Group |
包含所有模型、網格與子物件的根節點 |
gltf.animations |
Array<THREE.AnimationClip> |
若檔案內有動畫,會以 AnimationClip 陣列回傳 |
gltf.scenes |
Array<THREE.Group> |
多場景 glTF 時的所有根節點 |
gltf.parser |
GLTFParser |
內部解析器,可自行取得貼圖、材質等資源 |
程式碼範例
範例 1️⃣ 基本載入與顯示
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
// 建立基本場景
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.set(0, 1.5, 3);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 加入簡易環境光
const light = new THREE.AmbientLight(0xffffff, 0.8);
scene.add(light);
// 建立 GLTFLoader
const loader = new GLTFLoader();
loader.load(
// 檔案 URL(可使用 .gltf 或 .glb)
'models/Duck.glb',
// 成功載入回呼
(gltf) => {
// 將模型加入場景
scene.add(gltf.scene);
// 調整模型大小
gltf.scene.scale.set(0.01, 0.01, 0.01);
},
// 讀取進度回呼(可顯示 loading bar)
(xhr) => {
console.log(`模型載入進度: ${(xhr.loaded / xhr.total * 100).toFixed(1)}%`);
},
// 錯誤回呼
(error) => {
console.error('GLTF 載入失敗', error);
}
);
// 動畫迴圈
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
這段程式碼展示了 最小化的載入流程:匯入
GLTFLoader→ 載入模型 → 加入場景 → 渲染。對於新手來說,只要把模型檔放在models/目錄,即可立即看到結果。
範例 2️⃣ 使用 DRACOLossless 壓縮的 glTF
DRACO 壓縮可以大幅減小網格資料大小,但需要額外載入 DRACOLoader。
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
// 設定 DRACOLoader 路徑(CDN 或本機)
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/');
// 把 DRACOLoader 注入 GLTFLoader
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load('models/CompressedModel.glb', (gltf) => {
scene.add(gltf.scene);
});
重點:若檔案內使用了 DRACO 壓縮,必須先設定
DRACOLoader,否則載入會失敗並拋出THREE.DRACOLoader: No decoder available的錯誤訊息。
範例 3️⃣ 播放模型內建動畫
let mixer; // THREE.AnimationMixer
loader.load('models/Robot.glb', (gltf) => {
const model = gltf.scene;
scene.add(model);
// 建立 AnimationMixer,控制所有動畫
mixer = new THREE.AnimationMixer(model);
// 假設檔案內有多段動畫,全部播放
gltf.animations.forEach((clip) => {
const action = mixer.clipAction(clip);
action.play();
});
});
// 在 render loop 中更新 mixer
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
if (mixer) mixer.update(delta);
renderer.render(scene, camera);
}
AnimationMixer是 Three.js 處理動畫的核心物件,一定要在每幀更新(mixer.update(delta)),才能讓動畫流暢播放。
範例 4️⃣ 動態更換環境貼圖(IBL)
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
import { PMREMGenerator } from 'three';
// 產生 PMREM(pre‑filtered mip‑mapped radiance environment map)
const pmremGenerator = new PMREMGenerator(renderer);
pmremGenerator.compileEquirectangularShader();
new RGBELoader()
.setPath('textures/equirectangular/')
.load('royal_esplanade_1k.hdr', (hdrTex) => {
const envMap = pmremGenerator.fromEquirectangular(hdrTex).texture;
scene.environment = envMap; // 設定全域環境光
hdrTex.dispose();
pmremGenerator.dispose();
});
只要把
scene.environment指向一個 PBR 兼容的環境貼圖,載入的 glTF 材質(如MeshStandardMaterial)會自動使用 IBL 產生更真實的光照效果。
範例 5️⃣ 自訂材質或遮蔽貼圖
有時候模型的原始材質不符合需求,我們可以在載入後遍歷所有 Mesh,替換或調整材質參數。
loader.load('models/Car.glb', (gltf) => {
gltf.scene.traverse((child) => {
if (child.isMesh) {
// 以原始材質為基礎,調整金屬度與粗糙度
child.material.metalness = 0.9;
child.material.roughness = 0.15;
// 若需要加入遮蔽貼圖
if (child.material.map) {
const aoMap = child.material.map.clone();
aoMap.encoding = THREE.sRGBEncoding;
child.material.aoMap = aoMap;
child.material.aoMapIntensity = 1.0;
}
}
});
scene.add(gltf.scene);
});
這種 後處理方式 常用於「統一風格」或「快速調整」的情境,避免每次在 3D 建模軟體中重新匯出材質。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方案 |
|---|---|---|
| 路徑錯誤 | 相對路徑或 CORS 限制導致 404 或 NetworkError |
使用 相對於 HTML 的路徑,或在本機開發時使用 http-server、vite 等本地伺服器 |
| 貼圖未載入 | glTF 內的貼圖路徑錯誤或未設定 crossOrigin |
設定 loader.setCrossOrigin('anonymous'),或把貼圖與模型放同一目錄 |
| DRACO 未設定 | 壓縮模型出現 THREE.DRACOLoader 錯誤 |
如範例 2,先載入 DRACOLoader 並 setDecoderPath |
| 動畫不播放 | AnimationMixer 未更新或 clipAction 被暫停 |
在渲染迴圈中 必須 呼叫 mixer.update(delta),且 action.play() 後不要自行 stop() |
| 材質顏色偏暗 | 沒有環境光或 IBL,PBR 材質需要光源 | 加入 AmbientLight、DirectionalLight,或設定 scene.environment(範例 4) |
| GLB 大小過大 | 高解析度貼圖或未壓縮的幾何 | 使用 貼圖壓縮(KTX2、Basis)或 DRACO,並在建模階段減少多餘的頂點 |
最佳實踐
- 開發階段先用
.gltf:可直接在 Chrome DevTools → Sources 中檢視 JSON,除錯更方便。 - 上線前轉
.glb:使用gltf-pipeline、obj2gltf或 Blender 的匯出功能一次性壓縮。 - 使用 CDN:
https://cdn.jsdelivr.net/npm/three@0.164.0/examples/jsm/...可快速取得最新 Loader,減少自行維護。 - 記得釋放資源:
texture.dispose()、geometry.dispose()、material.dispose(),避免記憶體洩漏。 - 把 Loader 包裝成 Promise:讓程式碼更易於 async/await 管理。
function loadGLTF(url) {
return new Promise((resolve, reject) => {
const loader = new GLTFLoader();
loader.load(url, resolve, undefined, reject);
});
}
// 使用方式
(async () => {
const gltf = await loadGLTF('models/House.glb');
scene.add(gltf.scene);
})();
實際應用場景
| 場景 | 為何選擇 glTF / GLB | 可能的實作方式 |
|---|---|---|
| 線上商品展示(e‑commerce) | 小檔案、即時渲染、支援 PBR | 透過 GLTFLoader 載入商品模型,結合 OrbitControls 讓使用者旋轉、縮放 |
| AR/VR 互動體驗 | 支援動畫、骨骼、環境貼圖,且可在手機上快速下載 | 使用 WebXR + GLTFLoader,將模型放入虛擬空間,配合 XRControllerModelFactory |
| 即時遊戲角色 | 支援多動畫剪輯、skin、morph,且可使用 DRACO 壓縮減少帶寬 | 載入角色模型後,使用 AnimationMixer 切換走路、跑步、攻擊等動畫 |
| 資料視覺化(建築、工程) | 可保留層級結構與材質資訊,方便在程式中動態顯示/隱藏 | 讀取模型後,利用 scene.traverse 依層級控制可見性或顏色 |
| 教育平台(3D 教學) | 兼容多平台(桌面、行動),且可嵌入交互式 UI | 透過 GLTFLoader 載入解剖模型,結合 Raycaster 實作點擊即彈出說明框 |
總結
- glTF / GLB 是目前 Web 3D 最佳的資源格式,提供輕量、完整的材質與動畫支援。
- GLTFLoader 為 Three.js 官方推薦的載入器,只要正確設定路徑、必要的解碼器(如 DRACO),即可在瀏覽器中即時呈現高品質模型。
- 透過 AnimationMixer、環境貼圖、材質後處理 等技巧,我們可以將單純的模型轉化為互動且具備真實感的場景。
- 常見的陷阱(路徑、CORS、DRACO 未設定)只要提前檢查並遵循最佳實踐(使用 CDN、資源釋放、Promise 包裝),就能大幅降低錯誤率。
掌握了上述概念與範例後,你就能在任何 Three.js 專案中,快速導入、調整與運用 glTF/GLB 模型,為使用者帶來流暢且具沉浸感的 3D 體驗。祝開發順利,期待在你的作品裡看到精彩的 3D 模型! 🚀