本文 AI 產出,尚未審核

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 的基本使用流程

  1. 匯入 GLTFLoader(ES6 模組或 CDN)
  2. 建立 loader 實例
  3. 呼叫 load( url, onLoad, onProgress, onError )
  4. 取得 gltf.scenegltf.animationsgltf.asset 等屬性
  5. 加入 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 限制導致 404NetworkError 使用 相對於 HTML 的路徑,或在本機開發時使用 http-servervite 等本地伺服器
貼圖未載入 glTF 內的貼圖路徑錯誤或未設定 crossOrigin 設定 loader.setCrossOrigin('anonymous'),或把貼圖與模型放同一目錄
DRACO 未設定 壓縮模型出現 THREE.DRACOLoader 錯誤 如範例 2,先載入 DRACOLoadersetDecoderPath
動畫不播放 AnimationMixer 未更新或 clipAction 被暫停 在渲染迴圈中 必須 呼叫 mixer.update(delta),且 action.play() 後不要自行 stop()
材質顏色偏暗 沒有環境光或 IBL,PBR 材質需要光源 加入 AmbientLightDirectionalLight,或設定 scene.environment(範例 4)
GLB 大小過大 高解析度貼圖或未壓縮的幾何 使用 貼圖壓縮(KTX2、Basis)或 DRACO,並在建模階段減少多餘的頂點

最佳實踐

  1. 開發階段先用 .gltf:可直接在 Chrome DevTools → Sources 中檢視 JSON,除錯更方便。
  2. 上線前轉 .glb:使用 gltf-pipelineobj2gltf 或 Blender 的匯出功能一次性壓縮。
  3. 使用 CDNhttps://cdn.jsdelivr.net/npm/three@0.164.0/examples/jsm/... 可快速取得最新 Loader,減少自行維護。
  4. 記得釋放資源texture.dispose()geometry.dispose()material.dispose(),避免記憶體洩漏。
  5. 把 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 模型! 🚀