Three.js 基礎概念 – 架構與核心組件
簡介
在現代 Web 開發中,3D 互動體驗已成為提升使用者黏著度的重要手段。Three.js 作為最成熟、最廣為使用的 WebGL 包裝函式庫,讓開發者不必直接與低階的 WebGL API 打交道,就能在瀏覽器裡建立、渲染與控制 3D 內容。本單元將從 架構 與 核心組件 兩個層面,說明 Three.js 的設計哲學與實作概念,幫助你快速掌握「從零到可用」的基本流程。
為什麼要先了解架構?
只有真正理解 Three.js 各個模組之間的關係,才能在開發過程中正確選擇、擴充與優化,避免「只會套用範例」的表層使用,進而打造高效、可維護的 3D 專案。
核心概念
1. Scene(場景)
Scene 是所有 3D 物件的容器,類似於「舞台」的概念。所有的 Mesh、Light、Camera 等都必須加入到 Scene 中,渲染器才會把它們畫出來。
// 建立一個空的場景
const scene = new THREE.Scene();
// 也可以設定背景色或背景貼圖
scene.background = new THREE.Color(0x202020);
2. Camera(相機)
相機決定了「觀察者」的視角與投影方式。Three.js 主要提供兩種相機:
| 類型 | 用途 | 主要屬性 |
|---|---|---|
| PerspectiveCamera | 模擬人眼透視效果,適合大多數 3D 應用 | fov、aspect、near、far |
| OrthographicCamera | 平行投影,常用於 2.5D 或 UI 叠加 | left、right、top、bottom、near、far |
// 建立透視相機,視野 75°、寬高比根據視窗自動調整
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(0, 2, 5); // 把相機拉遠一點
3. Renderer(渲染器)
渲染器負責把 Scene 內的物件依照相機的視角,以 WebGL 的方式繪製到 <canvas> 上。最常用的是 WebGLRenderer,它支援抗鋸齒、陰影、HDR 等功能。
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 開啟陰影
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
4. Mesh(網格)與 Geometry(幾何)
Mesh 是「形狀」+「材質」的組合。Geometry(或 BufferGeometry)定義頂點、索引與其他屬性;Material 則決定表面的光照、顏色與貼圖。
// 建立一個簡單的盒子幾何
const geometry = new THREE.BoxGeometry(1, 1, 1);
// 使用標準材質,支援光照與環境貼圖
const material = new THREE.MeshStandardMaterial({
color: 0x156289,
metalness: 0.5,
roughness: 0.4
});
const cube = new THREE.Mesh(geometry, material);
cube.castShadow = true; // 允許投射陰影
cube.receiveShadow = true;
scene.add(cube);
5. Light(光源)
光源提供場景中的光照資訊,常見的光源類型有:
AmbientLight:全局均勻光,無方向性,常用來提升基礎亮度。DirectionalLight:類似太陽光,光線平行,可投射陰影。PointLight:點光源,光線向四面八方擴散。SpotLight:聚光燈,有錐形範圍與衰減。
// 環境光
const ambient = new THREE.AmbientLight(0xffffff, 0.4);
scene.add(ambient);
// 平行光(模擬太陽)
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(5, 10, 7);
dirLight.castShadow = true;
scene.add(dirLight);
6. 控制器(Controls)
為了讓使用者能在瀏覽器中自由旋轉、平移或縮放視角,Three.js 提供了多種控制器(在 three/examples/jsm/controls/ 中)。最常用的是 OrbitControls。
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // 平滑慣性
controls.dampingFactor = 0.05;
7. Animation Loop(動畫迴圈)
Three.js 並不自動啟動渲染,必須自行寫一個渲染迴圈,通常使用 requestAnimationFrame。
function animate() {
requestAnimationFrame(animate);
// 讓立方體持續旋轉
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
controls.update(); // 只在 enableDamping 時需要呼叫
renderer.render(scene, camera);
}
animate();
程式碼範例
以下提供 5 個實用範例,涵蓋從最簡單的「Hello Three」到加入貼圖與陰影的完整流程。
範例 1:最小 Hello Three
import * as THREE from 'three';
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, innerWidth / innerHeight, 0.1, 100);
camera.position.z = 3;
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
// 只要一個紅色方塊
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
render();
範例 2:加入光照與陰影
// ...(前半段同上)
// 替換 MeshBasicMaterial 為 MeshStandardMaterial
const material = new THREE.MeshStandardMaterial({ color: 0x156289 });
cube.castShadow = true;
cube.receiveShadow = true;
// 設定渲染器陰影
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
// 平行光投射陰影
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(5, 10, 5);
dirLight.castShadow = true;
scene.add(dirLight);
// 加一個平面接收陰影
const planeGeo = new THREE.PlaneGeometry(10, 10);
const planeMat = new THREE.MeshStandardMaterial({ color: 0x808080 });
const plane = new THREE.Mesh(planeGeo, planeMat);
plane.rotation.x = -Math.PI / 2;
plane.position.y = -0.5;
plane.receiveShadow = true;
scene.add(plane);
範例 3:載入外部貼圖(Texture)
import { TextureLoader } from 'three';
// 建立貼圖載入器
const loader = new TextureLoader();
loader.load('textures/brick_diffuse.jpg', (texture) => {
const mat = new THREE.MeshStandardMaterial({ map: texture });
const box = new THREE.Mesh(new THREE.BoxGeometry(2, 2, 2), mat);
box.castShadow = true;
scene.add(box);
});
範例 4:使用 GLTF 模型
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const gltfLoader = new GLTFLoader();
gltfLoader.load('models/scene.gltf', (gltf) => {
const model = gltf.scene;
model.traverse((obj) => {
if (obj.isMesh) obj.castShadow = true;
});
scene.add(model);
});
範例 5:結合 OrbitControls 與自適應視窗
window.addEventListener('resize', () => {
const w = window.innerWidth;
const h = window.innerHeight;
camera.aspect = w / h;
camera.updateProjectionMatrix();
renderer.setSize(w, h);
});
常見陷阱與最佳實踐
| 陷阱 | 可能原因 | 解決方式 / 最佳實踐 |
|---|---|---|
| 模型載入後不顯示 | 未加入場景、相機遠近裁切範圍不夠、材質未啟用光照 | 確認 scene.add(gltf.scene)、調整 camera.near/far、使用 MeshStandardMaterial |
| 陰影看不見 | 渲染器未開啟陰影、光源未設置 castShadow、物件未設定 receiveShadow |
renderer.shadowMap.enabled = true、light.castShadow = true、mesh.castShadow / receiveShadow = true |
| 畫面卡頓 | 幾何體過於複雜、貼圖過大、未使用 requestAnimationFrame 的節流 |
使用 BufferGeometry、壓縮貼圖(DDS / KTX2)、在 animate 中只更新需要改變的部分 |
| 視窗變動後比例失真 | 相機寬高比未更新、渲染器尺寸未同步 | 監聽 resize 事件,重新設定 camera.aspect 與 renderer.setSize |
| 控制器失靈 | 控制器未傳入正確的 DOM 元素或未呼叫 update() |
new OrbitControls(camera, renderer.domElement),在動畫迴圈中 controls.update()(若使用阻尼) |
最佳實踐
- 模組化:把
scene、camera、renderer、controls分別封裝成獨立檔案,提升可維護性。 - 資源預載:使用
LoadingManager統一管理模型、貼圖的載入狀態,避免畫面閃爍。 - 效能監控:利用
stats.js或 Chrome DevTools 的 GPU 時間,持續檢測幀率與渲染成本。 - 適度使用後處理:
EffectComposer提供 Bloom、SSAO 等效果,但過度堆疊會大幅降低效能,務必根據目標裝置做取捨。
實際應用場景
| 領域 | 典型案例 | 為何選 Three.js |
|---|---|---|
| 電商 | 3D 商品展示、虛擬試穿 | 輕量、即時渲染、支援手機瀏覽器 |
| 教育 | 交互式分子結構、天文模擬 | 可自訂動畫、結合資料驅動視覺化 |
| 建築 | 虛擬看房、室內佈局規劃 | 支援 GLTF/OBJ、可加入實時光照與陰影 |
| 遊戲 | 簡易 WebGL 遊戲、AR/VR 體驗 | 完整的渲染管線、與 WebXR API 無縫整合 |
| 資料視覺化 | 3D 圖表、地圖投影 | 支援自訂 Shader、可與 D3.js、Three.js 共同使用 |
以上範例均展示了 Three.js 在 即時互動、跨平台、低門檻 等特性上的優勢,使其成為前端 3D 開發的首選。
總結
- Three.js 的核心組件:
Scene、Camera、Renderer、Mesh、Light、Controls,相互協作完成 3D 渲染。 - 透過 模組化、資源預載與效能監控,可以在保持開發效率的同時,確保最終作品在桌面與行動裝置上流暢運行。
- 熟悉 光照、陰影與貼圖 的概念,能讓你的作品從「平面」躍升為「具備真實感」的 3D 體驗。
- 最後,別忘了 持續練習與閱讀官方範例(
three/examples),那裡匯聚了最完整的實務技巧與最新的 API 變更。
掌握了架構與核心組件之後,你就能自如地在任何 Web 專案中加入 3D 元素,為使用者創造更豐富、更具沉浸感的互動體驗。祝你玩得開心,創作無限!