本文 AI 產出,尚未審核

Three.js 與工具整合 – Skybox / HDR 環境製作


簡介

在 3D 網頁應用中,環境光是決定畫面真實感與沉浸感的關鍵因素。
傳統的光源(點光、平行光)只能提供局部的照明效果,而 SkyboxHDR (High Dynamic Range) 環境貼圖 則能一次性為整個場景提供全向的光線與反射,讓模型看起來彷彿真的置身於戶外或室內的特定氛圍中。

本篇文章將帶你從概念出發,一步步在 Three.js 中建構 Skybox 與 HDR 環境,並說明如何結合 dat.GUI、OrbitControls 等常用工具,快速產出既美觀又效能友善的 3D 場景。文章適合有基本 Three.js 使用經驗的初學者,也能為中階開發者提供實務上的最佳實踐與除錯技巧。


核心概念

1. Skybox 與環境貼圖的原理

  • Skybox:利用六張正方形圖(正、背、左、右、上、下)組成一個立方體,將相機放在立方體中心,讓使用者永遠看見遠景。它本身不參與光照計算,只提供背景
  • HDR 環境貼圖:使用 32‑bit 浮點色深的全景圖(常見 equirectangular 格式),可直接作為 Environment Map,供 PMREMGenerator 產生預先濾波的環境貼圖,進而驅動 物理渲染 (PBR)金屬度粗糙度等屬性,產生真實的 反射與折射

重點:Skybox 只負責視覺上的「遠景」,而 HDR 環境貼圖才是「光照」的來源。兩者常會同時使用,Skybox 作為背景、HDR 作為光源。

2. 為什麼要使用 PMREMGenerator?

在 PBR 流程中,環境貼圖需要 多層 MIPMAP 以支援不同粗糙度的材質。
THREE.PMREMGenerator 會將 HDR 圖轉換為 預濾波環境貼圖 (Prefiltered Mipmapped Radiance Environment Map),讓 MeshStandardMaterialMeshPhysicalMaterial 在不同粗糙度下仍能得到正確的反射能量。

3. 常見檔案格式

類型 常見副檔名 說明
Skybox (CubeTexture) .png, .jpg 6 張正方形圖,順序必須為 px, nx, py, ny, pz, nz
HDR (Equirectangular) .hdr, .exr 32‑bit 浮點圖,適合作為環境光源
低階備援 (LDR) .jpg, .png 若沒有 HDR,仍可使用 LDR equirectangular 產生環境貼圖(光照會較暗)

程式碼範例

以下範例使用 Three.js r162(或更新版本)與 ES6 模組 寫法,請先在 HTML 中加入 type="module"<script>,或使用 bundler (Vite、Webpack) 匯入。

1️⃣ 基本場景與相機設定

import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.outputEncoding = THREE.sRGBEncoding;   // 正確的色彩空間
document.body.appendChild(renderer.domElement);

const scene = new THREE.Scene();

// 相機
const camera = new THREE.PerspectiveCamera(
  60, window.innerWidth / window.innerHeight, 0.1, 1000
);
camera.position.set(0, 1.5, 3);

// 控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;

提示renderer.outputEncoding = THREE.sRGBEncoding 能確保 HDR 轉換後的顏色在螢幕上正確呈現。

2️⃣ 加載 Skybox(CubeTexture)

// Skybox 圖片路徑(請自行替換成自己的六張圖)
const skyboxPath = '/assets/skybox/';
const skyboxFiles = [
  'px.jpg', 'nx.jpg',
  'py.jpg', 'ny.jpg',
  'pz.jpg', 'nz.jpg'
];

const loader = new THREE.CubeTextureLoader();
const skyboxTexture = loader.setPath(skyboxPath).load(skyboxFiles, () => {
  console.log('Skybox loaded');
});

scene.background = skyboxTexture;   // 設為背景

小技巧:若想讓 光照 也使用同一張 CubeTexture,只要把 scene.environment = skyboxTexture; 即可,但建議還是使用 HDR 取得更自然的光照。

3️⃣ 加載 HDR 環境貼圖並產生 PMREM

import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';

const rgbeLoader = new RGBELoader();
rgbeLoader.setDataType(THREE.UnsignedByteType); // 兼容舊版瀏覽器

rgbeLoader.load('/assets/hdr/royal_esplanade_1k.hdr', (hdrEquirect) => {
  // 產生預濾波環境貼圖
  const pmremGenerator = new THREE.PMREMGenerator(renderer);
  pmremGenerator.compileEquirectangularShader();

  const envMap = pmremGenerator.fromEquirectangular(hdrEquirect).texture;

  // 設定場景環境光
  scene.environment = envMap;
  // 釋放原始 HDR 圖
  hdrEquirect.dispose();
  pmremGenerator.dispose();

  console.log('HDR environment map ready');
});

注意RGBELoader 會自動把 .hdr 讀成 linear 色彩空間,不要 再套用 renderer.outputEncoding,否則會出現過曝。

4️⃣ 建立 PBR 材質的物件

// 讓所有材質自動使用環境貼圖
function createPBRMesh(geometry, color = 0xffffff) {
  const material = new THREE.MeshStandardMaterial({
    color,
    metalness: 0.7,   // 金屬度
    roughness: 0.2,   // 粗糙度
    envMap: scene.environment,
    envMapIntensity: 1.0
  });

  const mesh = new THREE.Mesh(geometry, material);
  mesh.castShadow = true;
  mesh.receiveShadow = true;
  return mesh;
}

// 範例:放置一個金屬球
const sphere = createPBRMesh(
  new THREE.SphereGeometry(0.5, 64, 64),
  0xdddddd
);
sphere.position.set(-1, 0.5, 0);
scene.add(sphere);

// 範例:放置一個玻璃盒子
const box = createPBRMesh(
  new THREE.BoxGeometry(1, 1, 1),
  0xffffff
);
box.material.transparent = true;
box.material.opacity = 0.6;
box.material.roughness = 0.05;
box.material.metalness = 1.0;
box.position.set(1, 0.5, 0);
scene.add(box);

5️⃣ 使用 dat.GUI 動態調整環境強度

import GUI from 'lil-gui';

const gui = new GUI();
const params = {
  envIntensity: 1.0,
  exposure: 1.0
};

gui.add(params, 'envIntensity', 0, 2, 0.01).name('環境強度')
   .onChange(v => {
     // 所有使用環境貼圖的材質都會受影響
     scene.traverse(obj => {
       if (obj.isMesh && obj.material.envMap) {
         obj.material.envMapIntensity = v;
         obj.material.needsUpdate = true;
       }
     });
   });

gui.add(params, 'exposure', 0.5, 2, 0.01).name('曝光')
   .onChange(v => renderer.toneMappingExposure = v);

小提醒renderer.toneMapping = THREE.ACESFilmicToneMapping; 可以讓 HDR 看起來更自然,若需要加入可自行啟用。

完整渲染迴圈

function animate() {
  requestAnimationFrame(animate);
  controls.update();          // 讓 OrbitControls 有阻尼效果
  renderer.render(scene, camera);
}
animate();

常見陷阱與最佳實踐

陷阱 說明 解決方式
HDR 讀取失敗 RGBELoader 需要伺服器正確回傳 Content-Type: image/vnd.radiance,本機檔案直接 file:// 會 404。 使用本機開發伺服器(vite, http-server)或在 Node.js 搭配 express
環境貼圖顏色過亮 未設定 renderer.toneMappingrenderer.toneMappingExposure 建議使用 THREE.ACESFilmicToneMapping,並透過 renderer.toneMappingExposure 微調。
CubeTexture 與 HDR 同時使用時衝突 scene.backgroundscene.environment 只能分別設定,若同時給予同一個 CubeTexture,會導致光照不正確。 分別設定 scene.background = skyboxTexture;scene.environment = envMap;
PMREM 產生過慢 大尺寸 HDR(4K+)在手機端會卡頓。 先在 Photoshop、HDRShop 等工具將 HDR 解析度降至 1K~2K,或使用 WebWorker 產生 PMREM。
材質沒有收到環境貼圖 MeshStandardMaterialenvMapnullenvMapIntensity 為 0。 確認 scene.environment 已被設定,且在材質建立前已完成 HDR 加載(可使用 Promise.all)。

最佳實踐小結

  1. 先載入 HDR,等 PMREMGenerator 完成後再建立需要環境光的 Mesh,避免「先渲染再換貼圖」的閃爍。
  2. 使用 sRGBEncoding線性空間renderer.outputEncoding)保持一致,避免顏色偏差。
  3. 針對不同平台:桌面端使用 2K~4K HDR,手機端使用 1K LDR 或低解析度 HDR,以兼顧效能。
  4. 把環境參數抽成 UI(如 dat.GUI),方便即時微調,特別適合在開發階段快速找出最佳曝光與環境強度。

實際應用場景

場景 為何需要 Skybox / HDR 實作要點
虛擬展覽(Art Gallery) 需要真實的室內光線與牆面反射,使作品呈現自然光澤。 使用 HDR 室內環境貼圖(ex. studio.hdr),再加上簡單的白色 Skybox 作為背景。
車輛展示(Car Configurator) 車體金屬與玻璃材質對光線非常敏感,環境反射決定外觀。 高解析度 HDR(4K) + PMREM,並在 UI 中提供「環境強度」滑桿讓使用者模擬不同天氣。
遊戲關卡(Adventure Game) 大型戶外場景需要遠景(天空)與環境光的雙重效果。 結合 Skybox(雲層)與 HDR(日光)+ DirectionalLight 以加強陽光方向感。
AR/VR 互動體驗 眼睛對光線變化極為敏感,HDR 能提升沉浸感。 在 WebXR 中使用 renderer.xr.enabled = true,同時保持 renderer.toneMappingACESFilmicToneMapping
資料視覺化(Data Viz) 3D 圖表放在有深度感的背景中更易閱讀。 只需要 Skybox 作為背景,搭配低強度 HDR 以免干擾資料顏色。

總結

  • SkyboxHDR 環境貼圖 是提升 Three.js 場景真實感的兩大利器。
  • 透過 CubeTextureLoader 快速建立背景,並使用 RGBELoader + PMREMGenerator 產生可供 PBR 使用的光照貼圖。
  • 正確的色彩空間(sRGB ↔ Linear)與 Tone Mapping 設定是避免過曝或暗沉的關鍵。
  • 在開發過程中,UI 調整(dat.GUI)與 資源大小管理(HDR 解析度、PMREM 產生)能顯著提升使用者體驗與效能。

只要掌握以上概念與程式範例,你就能在 Three.js 中快速打造出 光影交織、反射自然 的 3D 場景,無論是遊戲、產品展示或是資料視覺化,都能讓作品在網頁上散發出專業級的視覺效果。祝開發順利,期待看到你用 Skybox & HDR 創造的精彩作品!