本文 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 為基礎,視材質的 metalness、roughness 調整,必要時使用 tone‑mapping。 |
| 忘記開啟陰影 | 設定 castShadow = true 但渲染器未開啟 shadowMap,結果看不到陰影。 |
renderer.shadowMap.enabled = true 必須在建立光源前設定。 |
| 陰影相機範圍不夠 | 陰影只在光源相機視錐體內渲染,範圍太小會出現「陰影消失」的問題。 | 調整 directional.shadow.camera.{left,right,top,bottom,near,far},或使用 helper 觀察。 |
| 使用過多光源 | 每個光源都會增加渲染成本,特別是投射陰影的光源。 | 僅在必要的光源上開啟陰影,其他光源使用 AmbientLight 或 HemisphereLight 補光。 |
| 顏色空間不一致 | 直接使用 sRGB 顏色但未啟用 tone‑mapping,會導致顏色失真。 | 在渲染器設定 renderer.outputEncoding = THREE.sRGBEncoding; 並使用 ACESFilmicToneMapping。 |
| 忘記更新環境貼圖的 PMREM | 變更環境貼圖後未重新產生 PMREM,材質會仍使用舊的環境光。 | 每次更換 HDR 時呼叫 pmremGenerator.fromEquirectangular() 重新生成。 |
最佳實踐
- 先以 AmbientLight 建立基礎亮度,再加 DirectionalLight 產生光照方向與陰影。
- 調整光源位置與強度時,盡量使用 GUI 或 開發者工具 觀察即時效果。
- 陰影品質:
renderer.shadowMap.type = THREE.PCFSoftShadowMap能在效能與畫質間取得良好平衡。 - 光源顏色:利用 色輪 或 參考自然光色溫(如 6500K 為白光),避免過於鮮豔的顏色影響材質真實感。
- 效能優化:若場景較大,考慮使用 光照貼圖(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 體驗。祝開發順利,玩得開心! 🚀