Three.js 課程 – Camera 與 Controls
主題:OrbitControls 基礎
簡介
在 3D 網頁應用中,**相機(Camera)**是使用者觀察場景的「眼睛」,而 **控制器(Controls)**則是讓相機能夠以直覺的方式移動、旋轉、縮放。OrbitControls 是 Three.js 官方提供的最常用控制器之一,它模擬「環繞」相機的操作:使用者可以拖曳滑鼠或手指,讓相機圍繞目標點(target)旋轉,亦能透過滾輪或捏合手勢實作縮放(dolly)功能。
掌握 OrbitControls 的基本用法,能讓你在短時間內為 3D 場景加入 交互式觀察,提升使用者體驗,尤其適合模型展示、資料視覺化、遊戲原型等各種應用。
核心概念
1. 為什麼使用 OrbitControls?
| 功能 | 手動實作難度 | OrbitControls 提供的好處 |
|---|---|---|
| 繞目標旋轉 | 需要自行計算球面坐標、四元數 | 已封裝好,支援平滑阻尼 |
| 平移(Pan) | 需要把相機與目標平行移動 | 可直接開關,支援螢幕座標轉換 |
| 縮放(Zoom/Dolly) | 需要根據相機型別調整距離或視角 | 自動辨識 PerspectiveCamera 與 OrthographicCamera |
| 滑鼠/觸控手勢 | 需自行監聽多種事件 | 支援滑鼠、觸控、滾輪、鍵盤等多種輸入 |
結論:若只是想要快速得到「可拖曳、可縮放」的相機交互,
OrbitControls是最省時的選擇。
2. 基本安裝與引用
OrbitControls 並非 Three.js 核心模組,而是位於 examples/jsm/controls/OrbitControls.js。在使用 ES6 模組時,只要透過 import 載入即可:
// main.js
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
小技巧:若使用 bundler(如 Vite、Webpack)或 CDN(如 unpkg)皆可直接引用,請確認版本號與 Three.js 本體保持一致。
3. 建立基本場景與 OrbitControls
以下是一個最小化範例,展示如何在 PerspectiveCamera 上套用 OrbitControls:
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
function init() {
// 1. Renderer
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 2. Scene
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x202020);
// 3. Camera
const camera = new THREE.PerspectiveCamera(
60, // fov
window.innerWidth / window.innerHeight,
0.1, 1000 // near, far
);
camera.position.set(5, 5, 5); // 初始位置
// 4. OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 0, 0); // 環繞的目標點
controls.update(); // 必須在第一次渲染前呼叫
// 5. 加入簡單幾何體作為參考
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshNormalMaterial();
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// 6. 監聽視窗變動
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// 7. 動畫迴圈
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
}
init();
說明:
controls.target定義相機繞旋的中心點。預設為(0,0,0),若想觀察其他模型,只要把它移到模型的中心即可。controls.update()在第一次渲染前必須呼叫,否則相機位置不會根據target立即同步。renderer.domElement為事件監聽的目標,確保滑鼠/觸控操作只在畫布上觸發。
4. 常用屬性與方法
| 屬性 / 方法 | 類型 | 功能說明 |
|---|---|---|
controls.enableDamping |
Boolean | 開啟阻尼(慣性)效果,需在 animate 內呼叫 controls.update() |
controls.dampingFactor |
Number | 阻尼係數,建議 0.05~0.1 |
controls.enableZoom |
Boolean | 允許縮放(滾輪或捏合) |
controls.minDistance / maxDistance |
Number | 限制相機與目標的最遠/最近距離(Perspective) |
controls.minZoom / maxZoom |
Number | 限制正交相機的縮放倍率 |
controls.enablePan |
Boolean | 允許平移(按住滑鼠右鍵或中鍵) |
controls.panSpeed |
Number | 平移速度係數 |
controls.autoRotate |
Boolean | 自動繞目標旋轉(常用於展示模型) |
controls.autoRotateSpeed |
Number | 自動旋轉速度(度/秒) |
controls.maxPolarAngle / minPolarAngle |
Number | 限制垂直旋轉角度(弧度),防止相機翻到地面以下 |
controls.saveState() / reset() |
方法 | 儲存當前狀態、或回到最初狀態 |
5. 進階範例:自訂限制與自動旋轉
以下範例示範 限制垂直俯仰角度(只允許在 30°~150° 之間)以及 自動旋轉(適合商品模型展示):
// ... 先前的 init 基礎設定同上
// 1. 限制垂直俯仰角度(以弧度計算)
controls.minPolarAngle = THREE.MathUtils.degToRad(30); // 30°
controls.maxPolarAngle = THREE.MathUtils.degToRad(150); // 150°
// 2. 開啟阻尼與自動旋轉
controls.enableDamping = true;
controls.dampingFactor = 0.08;
controls.autoRotate = true;
controls.autoRotateSpeed = 2.0; // 每秒 2 度
// 3. 在 animate 中持續更新
function animate() {
requestAnimationFrame(animate);
controls.update(); // 必須放在此,才能讓阻尼與自動旋轉生效
renderer.render(scene, camera);
}
animate();
重點:
THREE.MathUtils.degToRad()讓角度轉成弧度,避免手動計算錯誤。- 開啟阻尼後,即使使用者停止拖曳,相機仍會「滑行」到停止點,感覺更自然。
autoRotate只在使用者未互動時才會啟動,若使用者手動旋轉,會暫時停下。
6. 多相機切換與 OrbitControls
在同一個場景中,有時會需要 切換不同類型的相機(例如從 PerspectiveCamera 切到 OrthographicCamera),只要保留同一個 OrbitControls 實例,並在切換後重新指派相機即可:
let currentCamera = perspectiveCamera; // 預設相機
const controls = new OrbitControls(currentCamera, renderer.domElement);
controls.enableDamping = true;
// 切換相機的函式
function switchCamera() {
// 先保存目前的控制狀態
const target = controls.target.clone();
const position = currentCamera.position.clone();
// 刪除舊的控制器(或直接改變相機屬性)
controls.dispose();
// 切換相機
currentCamera = (currentCamera.isPerspectiveCamera) ? orthographicCamera : perspectiveCamera;
// 重新建立控制器
const newControls = new OrbitControls(currentCamera, renderer.domElement);
newControls.target.copy(target);
currentCamera.position.copy(position);
newControls.enableDamping = true;
newControls.update();
// 替換全局變數
controls = newControls;
}
此方式確保 使用者的視角與目標點不會因切換相機而重置,提升使用者體驗。
常見陷阱與最佳實踐
| 陷阱 | 可能的症狀 | 解決方案 |
|---|---|---|
忘記呼叫 controls.update() |
相機不會跟隨阻尼或自動旋轉 | 在每個動畫迴圈(requestAnimationFrame)內呼叫 |
| 相機與目標距離過遠或過近 | 滾輪縮放失效、或控制器卡住 | 設定 minDistance / maxDistance(透視)或 minZoom / maxZoom(正交) |
| 視窗大小改變後未更新相機比例 | 畫面變形、比例失真 | 在 resize 事件中呼叫 camera.updateProjectionMatrix() |
使用 OrbitControls 時與其他控制器衝突 |
滑鼠事件被覆寫、控制失效 | 確保同一時間只掛載一個控制器,或使用 controls.enabled = false 暫時關閉 |
無意間把 OrbitControls 加到錯誤的 DOM 元素 |
滑鼠事件無反應 | 應傳入渲染器的 domElement(renderer.domElement) |
最佳實踐
- 阻尼(Damping):對於大多數 UI,開啟
enableDamping並設定dampingFactor0.05~0.1,能讓操作更順滑。 - 限制角度:使用
minPolarAngle/maxPolarAngle防止相機翻到地面以下,尤其在地形或模型展示時。 - 自動旋轉作為預設姿態:在模型載入完成前,可設定
autoRotate,提供「自動播放」的預覽效果。 - 儲存與還原狀態:在需要「重設視角」的 UI(如「回到初始視角」按鈕),呼叫
controls.saveState()與controls.reset()。 - 適當的事件節流:若在
resize或pointermove中做大量計算,建議使用requestAnimationFrame或throttle以降低效能衝擊。
實際應用場景
| 場景 | 為何選擇 OrbitControls | 具體實作要點 |
|---|---|---|
| 產品 3D 展示(商品輪播) | 需要使用者自由旋轉、縮放,同時提供自動旋轉預覽 | 開啟 autoRotate、設定 maxDistance 限制過遠視角、加入 UI 按鈕切換 enableZoom |
| 資料視覺化(3D 圖表) | 使用者要檢視不同角度的圖表,同時避免相機穿透圖形 | 設定 minPolarAngle / maxPolarAngle、使用 pan 只在 X‑Y 平面移動 |
| 建築模型導覽 | 大型模型需要平滑的相機移動與限制俯仰角,以免「掉下去」 | minDistance 與 maxDistance 依模型大小調整、配合 OrbitControls 的 target 設為建築中心 |
| VR/AR 前端預覽 | 在 WebXR 中仍可使用 OrbitControls 作為非沉浸式的預覽模式 |
把 controls.enabled 設為 !renderer.xr.isPresenting,在 XR 進入時自動關閉 |
| 多相機切換(俯視、透視) | 同一場景需要切換不同視角以提供不同資訊 | 參考上方「多相機切換」範例,保持 controls.target 不變,僅改變相機參數 |
總結
OrbitControls 是 Three.js 中最實用且易上手的相機控制器之一。透過 簡單的匯入、初始化與屬性調整,開發者即可為 3D 場景加入自然的旋轉、平移與縮放交互。
- 核心概念:
target為環繞中心、enableDamping提升流暢度、min/maxPolarAngle控制垂直限制。 - 常見陷阱:忘記
update()、未設定距離限制、視窗變更未同步相機。 - 最佳實踐:適度使用阻尼、限制角度、保存/還原狀態、在需要時關閉控制器。
掌握這些要點後,你就能在 商品展示、資料視覺化、建築導覽 等多種實務情境中,快速打造出 直觀、流暢且可客製化 的 3D 互動體驗。祝你玩得開心,創作出更多令人驚艷的 Three.js 作品!