本文 AI 產出,尚未審核

Three.js 基礎概念 ── 渲染管線(Renderer → Scene → Camera → Objects)

簡介

在 3D 網頁開發中,Three.js 是最常被採用的高階抽象函式庫。它把 WebGL 那層低階、繁雜的 API 包裝成易於上手的物件導向模型,讓開發者可以把注意力放在 「要呈現什麼」 而不是 「要怎麼把像素送到螢幕」

任何一個 Three.js 應用的核心,都圍繞 渲染管線(Rendering Pipeline)運作。簡單來說,渲染管線的四大要素是:

  1. Renderer – 把資料送到 GPU,產生最終的影像。
  2. Scene – 3D 世界的容器,所有可見的物件都放在這裡。
  3. Camera – 定義觀察者的位置、方向與投影方式。
  4. Objects – 幾何體、材質、光源等組成的實體。

掌握這四者之間的關係與設定順序,是寫出正確且效能良好的 Three.js 程式的第一步。本文將從概念說明、實作範例、常見陷阱與最佳實踐,帶你完整了解渲染管線的運作方式。


核心概念

1. Renderer:橋接 JavaScript 與 GPU

Three.js 內建兩種主要的渲染器:

渲染器 說明 何時使用
WebGLRenderer 直接呼叫 WebGL,支援陰影、後處理等功能 大多數 3D 應用
CanvasRenderer 使用 2D Canvas,功能較少 老舊裝置或教學示範(已逐步淘汰)

基本設定

// 建立 WebGLRenderer 並設定畫布大小
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
// 把 canvas 加入 DOM
document.body.appendChild(renderer.domElement);
  • antialias: true 能減少鋸齒,但會稍微吃效能。
  • setPixelRatio 可配合 Retina 螢幕:renderer.setPixelRatio(window.devicePixelRatio);

2. Scene:3D 世界的容器

Scene 本質上是一個 「節點樹」(scene graph),所有物件都以階層方式掛載於其下。這讓我們可以一次性對整個子樹做變換(例如旋轉、平移),非常適合組合模型或 UI 元件。

const scene = new THREE.Scene();
// 加入環境光(不會產生陰影,只提供基礎亮度)
const ambient = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambient);
  • Scene 本身不會自動清除畫面,渲染器會在每次 render() 前自行執行 clear,除非你手動設定 renderer.autoClear = false

3. Camera:觀察者的視角

Three.js 主要提供兩種相機:

相機類型 特色 常見用途
PerspectiveCamera 透視投影,遠近物件大小會隨距離變化 真實感 3D 場景
OrthographicCamera 正交投影,遠近大小相同 2.5D、平面 UI、等距視圖
// 透視相機:fov、aspect、near、far
const camera = new THREE.PerspectiveCamera(
  75,                                 // 視野角度 (Degree)
  window.innerWidth / window.innerHeight, // 長寬比
  0.1,                                // 最近可見距離
  1000                                // 最遠可見距離
);
camera.position.set(0, 2, 5); // 把相機抬高 2 單位、往後移 5 單位
  • 相機的 nearfar 必須根據場景深度調整,過大會造成 Z‑buffer 精度問題(穿幀)。

4. Objects:幾何體、材質與光源

物件是由 Geometry(形狀) + Material(外觀) 組成的 Mesh。光源則是獨立的 Object,會影響材質的著色結果。

範例 A:建立一個簡單的立方體

// 幾何體:1x1x1 立方體
const geometry = new THREE.BoxGeometry(1, 1, 1);
// 基礎材質:使用 MeshStandardMaterial 以支援光照
const material = new THREE.MeshStandardMaterial({ color: 0x156289, metalness: 0.5, roughness: 0.2 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

範例 B:加入方向光(產生陰影)

const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(5, 10, 7);
dirLight.castShadow = true; // 開啟陰影
scene.add(dirLight);

// 讓立方體投射與接受陰影
renderer.shadowMap.enabled = true;
cube.castShadow = true;
cube.receiveShadow = true;

範例 C:載入外部模型(GLTF)

const loader = new THREE.GLTFLoader();
loader.load('models/scene.gltf', gltf => {
  const model = gltf.scene;
  model.traverse(node => {
    if (node.isMesh) {
      node.castShadow = true;
      node.receiveShadow = true;
    }
  });
  scene.add(model);
}, undefined, err => console.error(err));

範例 D:使用後處理(Composer + Bloom)

// 建立 RenderPass
const renderPass = new THREE.RenderPass(scene, camera);
// Bloom 效果
const bloomPass = new THREE.UnrealBloomPass(
  new THREE.Vector2(window.innerWidth, window.innerHeight),
  1.5, // 強度
  0.4, // 半徑
  0.85 // 閾值
);
const composer = new THREE.EffectComposer(renderer);
composer.addPass(renderPass);
composer.addPass(bloomPass);

// 在動畫迴圈中改用 composer.render()
function animate() {
  requestAnimationFrame(animate);
  cube.rotation.x += 0.01;
  cube.rotation.y += 0.01;
  composer.render();
}
animate();

小技巧:在開發階段先使用 renderer.render(scene, camera); 確認基本渲染,待所有物件正確顯示後再加入 EffectComposer,可避免除錯時被後處理「掩蓋」問題。


常見陷阱與最佳實踐

陷阱 可能的症狀 解決方法或最佳實踐
相機遠近裁剪範圍不合適 物件消失、Z‑fighting(深度抖動) near 設為盡可能大的值,far 設為必要的最遠距離。
忘記啟用陰影或設定陰影地圖大小 陰影不顯示或模糊 renderer.shadowMap.enabled = true;
dirLight.shadow.mapSize.width = 2048;
材質未支援光照 物件看起來像是「平面貼圖」 使用 MeshStandardMaterialMeshPhongMaterialMeshLambertMaterial
場景內物件過多未使用層級管理 FPS 大幅下降 利用 Object3D 分組、FrustumCullingLOD(Level of Detail)等技術。
視口尺寸變更未同步更新 畫面被拉伸或比例錯亂 window.resize 事件中呼叫 renderer.setSize()camera.aspect = ...; camera.updateProjectionMatrix();

最佳實踐

  1. 先建立最小可執行範例(Renderer + Scene + Camera),確認渲染正常後,再逐步加入物件與光源。
  2. 使用 requestAnimationFrame 取代 setInterval,讓瀏覽器自行同步更新頻率。
  3. 分離渲染與更新邏輯:將動畫、物理、UI 等更新寫在 tick 函式,最後統一呼叫 renderer.render()
  4. 針對行動裝置調整解析度renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); 以免過高的解析度拖慢效能。
  5. 使用開發者工具(Three.js Inspector):可即時檢視場景圖、相機參數與材質設定,快速定位問題。

實際應用場景

領域 典型案例 渲染管線的應用重點
產品展示 3D 電子商務(家具、汽車) 高品質材質 + 環境光遮蔽(HDRI)
使用 OrbitControls 讓使用者自由旋轉。
資料視覺化 三維圖表、地圖投影 OrthographicCamera 搭配 InstancedMesh 渲染大量點雲。
互動遊戲 WebGL 小型遊戲 EffectComposer 加上後處理(Bloom、FXAA)提升視覺效果。
虛擬實境 (VR) / 擴增實境 (AR) WebXR 體驗 需要同時建立兩個相機(左/右眼)並使用 WebXRManager,渲染流程仍以 Renderer → Scene → Camera 為核心。
教育與培訓 交互式教學模擬 利用 AnimationMixer 控制模型動畫,渲染管線保持不變,只是每幀更新物件狀態。

重點:不論是哪一種應用,渲染管線的四大要素 都是不可或缺的基礎,只有在此基礎上再加入特效或優化,才能保證程式碼的可維護性與效能。


總結

Three.js 的渲染管線可以簡化為 Renderer → Scene → Camera → Objects 四個步驟。

  1. Renderer 負責把 JavaScript 資料送到 GPU,決定畫布大小與後處理。
  2. Scene 是所有 3D 元素的容器,使用階層結構管理變換與可見性。
  3. Camera 定義觀察者的視角與投影方式,near/far 必須適當調整以避免深度問題。
  4. Objects 包括幾何體、材質、光源與模型,是最終呈現在螢幕上的實體。

透過本文的概念說明與實作範例,你應該已能自行建立一個完整的 Three.js 基礎渲染流程,並了解常見的陷阱與最佳實踐。未來只要在此基礎上擴充光照、後處理、動畫或 XR 功能,就能快速開發出功能強大且效能穩定的 3D 網頁應用。祝你玩得開心,創作出令人驚艷的 Web 3D 體驗!