本文 AI 產出,尚未審核

Three.js 燈光教學:AmbientLight 與 DirectionalLight


簡介

在 3D 網頁應用中,燈光是決定畫面質感與真實感的關鍵因素。即使模型本身的幾何與材質已經做好,若缺乏適當的光源,最終呈現的畫面仍會顯得平板、缺乏層次。
Three.js 提供了多種光源類型,其中最常使用、也是最容易上手的就是 AmbientLight(環境光)與 DirectionalLight(方向光)。前者負責提供全局的基礎光照,讓所有物件都能被「看見」;後者則模擬遠距離的平行光源(如太陽),可產生明顯的陰影與光照方向感。

本篇文章將深入說明這兩種光源的概念、使用方式與實務技巧,並提供完整的程式碼範例,幫助 初學者到中級開發者 快速在 Three.js 專案中掌握燈光的應用。


核心概念

1. AmbientLight – 環境光

  • 概念:環境光是一種「無方向」的均勻光源,會把光線均勻地灑向場景中的所有物件。它不會產生陰影,也不會受到物件位置或法線的影響。
  • 用途
    • 為場景提供基礎亮度,避免完全黑暗。
    • 與其他光源(如 DirectionalLight、SpotLight)結合,提升整體光照的柔和度。

2. DirectionalLight – 方向光

  • 概念:方向光模擬遠距離的平行光(例如太陽),光線方向固定且幾乎不衰減。它可以投射陰影,並且光照強度會根據物件表面的法線方向產生明暗變化。
  • 重要屬性
    • position:光源的方向(從光源指向原點的向量)。
    • intensity:光照強度。
    • color:光的顏色。
    • castShadow:是否投射陰影。
    • shadow.camera:陰影相機的視錐體,用於調整陰影範圍與精度。

程式碼範例

以下示範 5 個實用範例,從最簡單的環境光到結合陰影、動態調整參數的完整應用。每段程式碼皆附上說明註解,請依序閱讀與實作。

範例 1️⃣:最簡單的 AmbientLight

// 建立場景、相機與渲染器
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.set(0, 2, 5);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 加入一個簡單的立方體
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: 0x156289 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

// **AmbientLight**:全域光源,顏色為柔和的白色,強度 0.5
const ambient = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambient);

// 基本渲染迴圈
function animate() {
  requestAnimationFrame(animate);
  cube.rotation.y += 0.01;
  renderer.render(scene, camera);
}
animate();

重點:即使沒有 DirectionalLight,加入 AmbientLight 後立方體仍能被看到,只是缺少光照方向感。


範例 2️⃣:加入 DirectionalLight,產生明顯光照方向

// ... (與範例 1 相同的基本設定) ...

// **DirectionalLight**:模擬太陽光
const directional = new THREE.DirectionalLight(0xffffff, 1.0);
directional.position.set(5, 10, 7); // 從右上方射向場景
scene.add(directional);

// 為了更直觀觀察光源方向,加入 Helper
const dirHelper = new THREE.DirectionalLightHelper(directional, 1);
scene.add(dirHelper);

說明DirectionalLightHelper 會在畫面中顯示光源方向的箭頭,方便調整位置。


範例 3️⃣:啟用陰影與陰影相機調整

// 讓渲染器支援陰影
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 柔和陰影

// 讓立方體可以投射與接收陰影
cube.castShadow = true;
cube.receiveShadow = true;

// 設定 DirectionalLight 投射陰影
directional.castShadow = true;

// 調整陰影相機(只需要一次設定,之後可依需求微調)
directional.shadow.camera.left   = -10;
directional.shadow.camera.right  =  10;
directional.shadow.camera.top    =  10;
directional.shadow.camera.bottom = -10;
directional.shadow.camera.near   = 0.5;
directional.shadow.camera.far    = 50;

// 為場景加一個平面,作為接收陰影的地面
const planeGeo = new THREE.PlaneGeometry(20, 20);
const planeMat = new THREE.MeshStandardMaterial({ color: 0x808080 });
const ground = new THREE.Mesh(planeGeo, planeMat);
ground.rotation.x = -Math.PI / 2;
ground.position.y = -0.5;
ground.receiveShadow = true;
scene.add(ground);

提示:陰影相機的 left/right/top/bottom 必須覆蓋到光照範圍,否則會出現「陰影被裁切」的情況。


範例 4️⃣:結合環境貼圖(HDR)與 AmbientLight

// 使用 RGBE/HDR 讀取環境貼圖(需安裝 three/examples/jsm/…)
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', function (hdrEquirect) {
    const envMap = pmremGenerator.fromEquirectangular(hdrEquirect).texture;
    scene.environment = envMap; // 讓 PBR 材質使用環境光照
    hdrEquirect.dispose();
    pmremGenerator.dispose();

    // **AmbientLight** 用環境貼圖的平均亮度作為基礎光源
    const ambient = new THREE.AmbientLight(0xffffff, 0.4);
    scene.add(ambient);
  });

說明:環境貼圖提供了 全方向的光照資訊,配合 AmbientLight 可讓材質呈現更自然的反射與漫反射效果。


範例 5️⃣:使用 GUI 動態調整光源參數(dat.GUI)

import * as dat from 'dat.gui';

// 建立 GUI 介面
const gui = new dat.GUI();

// AmbientLight 參數
const ambientFolder = gui.addFolder('Ambient Light');
ambientFolder.addColor({ color: ambient.color.getHex() }, 'color')
  .onChange(v => ambient.color.setHex(v));
ambientFolder.add(ambient, 'intensity', 0, 2, 0.01);
ambientFolder.open();

// DirectionalLight 參數
const dirFolder = gui.addFolder('Directional Light');
dirFolder.addColor({ color: directional.color.getHex() }, 'color')
  .onChange(v => directional.color.setHex(v));
dirFolder.add(directional, 'intensity', 0, 2, 0.01);
dirFolder.add(directional.position, 'x', -20, 20, 0.1);
dirFolder.add(directional.position, 'y', -20, 20, 0.1);
dirFolder.add(directional.position, 'z', -20, 20, 0.1);
dirFolder.open();

實務應用:在開發階段使用 GUI 可快速找出最適合的光照配置,之後再把參數寫死於程式碼或配置檔中。


常見陷阱與最佳實踐

陷阱 說明 解決方案 / 最佳實踐
光源強度過高或過低 直接使用 0xffffff, 1 常會讓場景過曝或過暗。 先以 0.5~1 為基礎,視材質的 metalnessroughness 調整,必要時使用 tone‑mapping
忘記開啟陰影 設定 castShadow = true 但渲染器未開啟 shadowMap,結果看不到陰影。 renderer.shadowMap.enabled = true 必須在建立光源前設定。
陰影相機範圍不夠 陰影只在光源相機視錐體內渲染,範圍太小會出現「陰影消失」的問題。 調整 directional.shadow.camera.{left,right,top,bottom,near,far},或使用 helper 觀察。
使用過多光源 每個光源都會增加渲染成本,特別是投射陰影的光源。 僅在必要的光源上開啟陰影,其他光源使用 AmbientLightHemisphereLight 補光。
顏色空間不一致 直接使用 sRGB 顏色但未啟用 tone‑mapping,會導致顏色失真。 在渲染器設定 renderer.outputEncoding = THREE.sRGBEncoding; 並使用 ACESFilmicToneMapping
忘記更新環境貼圖的 PMREM 變更環境貼圖後未重新產生 PMREM,材質會仍使用舊的環境光。 每次更換 HDR 時呼叫 pmremGenerator.fromEquirectangular() 重新生成。

最佳實踐

  1. 先以 AmbientLight 建立基礎亮度,再加 DirectionalLight 產生光照方向與陰影。
  2. 調整光源位置與強度時,盡量使用 GUI開發者工具 觀察即時效果。
  3. 陰影品質renderer.shadowMap.type = THREE.PCFSoftShadowMap 能在效能與畫質間取得良好平衡。
  4. 光源顏色:利用 色輪參考自然光色溫(如 6500K 為白光),避免過於鮮豔的顏色影響材質真實感。
  5. 效能優化:若場景較大,考慮使用 光照貼圖(Lightmap)Baked Lighting,減少即時計算。

實際應用場景

場景 使用光源組合 為何適合
室內模型(房間、辦公室) AmbientLight + DirectionalLight + SpotLight AmbientLight 提供均勻基礎光,DirectionalLight 模擬窗外的自然光,SpotLight 用於燈具局部照明。
戶外日景(城市、山景) AmbientLight + DirectionalLight (太陽) + HemisphereLight DirectionalLight 產生明顯的太陽光與陰影,HemisphereLight 補足天空光的散射感,AmbientLight 防止暗部全黑。
夜景或燈光秀 AmbientLight (低強度) + DirectionalLight (微弱) + PointLight / SpotLight 低強度的 AmbientLight 防止完全黑暗,點光源或聚光燈突出光源焦點,營造氛圍。
產品展示(渲染商品) AmbientLight + DirectionalLight + HDR 環境貼圖 HDR 環境貼圖提供真實的反射,DirectionalLight 突出材質高光,AmbientLight 保持整體亮度。
VR/AR 互動體驗 AmbientLight + DirectionalLight (動態) + LightProbe 需要即時調整光源方向與強度,LightProbe 可捕捉環境光的間接光照,提升沉浸感。

總結

  • AmbientLight 為場景提供全局、無方向的基礎光照,是任何 Three.js 場景的「底層」光源。
  • DirectionalLight 則模擬遠距離平行光(如太陽),能產生明顯的光照方向與陰影,是打造寫實感的關鍵。
  • 正確設定 陰影相機、shadowMap 以及 光源強度/顏色,能避免常見的暗部過暗或陰影失真問題。
  • 結合 HDR 環境貼圖dat.GUI光照貼圖,可以在開發階段快速調整、在上線階段提升效能與真實感。

掌握了這兩種光源的特性與最佳實踐後,你就能在 Three.js 中輕鬆打造出 光影交錯、層次豐富 的 3D 體驗。祝開發順利,玩得開心! 🚀