Three.js 教學 – CubeTexture 與環境貼圖(Environment Map)
簡介
在 3D 網頁應用中,光影與反射往往是提升真實感的關鍵。CubeTexture(立方體貼圖)與 Environment Map(環境貼圖) 正是實作這類效果的核心工具。透過一組六張貼圖(正負 X、Y、Z 方向)組成的立方體貼圖,我們可以讓金屬、玻璃或水面等材質在 Three.js 中呈現出逼真的環境反射,甚至模擬全局光照(IBL)。
本單元將說明:
- CubeTexture 的概念與載入方式
- 如何將 CubeTexture 套用為環境貼圖(
envMap) - 常見的坑與最佳實踐,讓你的專案既美觀又效能友好
適用對象:剛接觸 Three.js 的新手、想在作品中加入環境反射的中階開發者,以及需要優化貼圖載入流程的前端工程師。
核心概念
1. 什麼是 CubeTexture?
CubeTexture 本質上是一組 六張 正方形貼圖,分別對應立方體的六個面:
| 面向 | 細節說明 |
|---|---|
| +X | 右側 (right) |
| -X | 左側 (left) |
| +Y | 上方 (top) |
| -Y | 下方 (bottom) |
| +Z | 前方 (front) |
| -Z | 後方 (back) |
當這六張貼圖被載入後,Three.js 會自動把它們包成一個 立方體環境,供材質的 envMap 使用。這種方式比起單張全景貼圖(equirectangular)更適合即時渲染,因為 GPU 可以直接以立方體貼圖取樣。
2. 為什麼使用環境貼圖?
- 真實感:金屬、玻璃等材質會根據周圍環境產生高光與反射。
- 全局光照 (IBL):配合
PMREMGenerator,可將環境光照資訊轉為漫射光照,讓場景的間接光更自然。 - 簡化光源:只需要一組環境貼圖,即可為整個場景提供統一的光照基礎,減少大量 point/spot light 的使用。
3. 基本載入流程
Three.js 提供兩種主要方式載入 CubeTexture:
| 方法 | 說明 |
|---|---|
THREE.CubeTextureLoader |
直接載入六張圖檔,最常用。 |
THREE.TextureLoader + THREE.EquirectangularToCubeGenerator |
先載入 equirectangular 全景圖,再轉換為 CubeTexture(較少用,但可直接使用單張 HDR 圖)。 |
下面先以 CubeTextureLoader 為例說明。
程式碼範例
註:以下範例皆假設已經在 HTML 中加入 Three.js(
<script src="https://unpkg.com/three@0.165.0/build/three.min.js"></script>)以及一個基本的渲染迴圈。
1️⃣ 基本 CubeTexture 載入與設定背景
// 建立場景
const scene = new THREE.Scene();
// 使用 CubeTextureLoader 載入六張環境貼圖
const loader = new THREE.CubeTextureLoader();
const envMap = loader.load([
'textures/skybox/px.jpg', // +X
'textures/skybox/nx.jpg', // -X
'textures/skybox/py.jpg', // +Y
'textures/skybox/ny.jpg', // -Y
'textures/skybox/pz.jpg', // +Z
'textures/skybox/nz.jpg' // -Z
]);
// 設定場景的背景為 CubeTexture
scene.background = envMap;
// 後續相機、渲染器等設定...
說明:載入完成後,
scene.background會自動以立方體形式呈現,讓使用者在瀏覽器中看到完整的天空盒。
2️⃣ 為材質加入環境貼圖(金屬球體)
// 建立一個金屬球體幾何
const geometry = new THREE.SphereGeometry(1, 64, 64);
// 使用 MeshStandardMaterial,並指定 envMap
const material = new THREE.MeshStandardMaterial({
metalness: 1.0, // 完全金屬
roughness: 0.05, // 稍微粗糙,避免完全鏡面
envMap: envMap, // 套用先前載入的 CubeTexture
envMapIntensity: 1.0 // 環境光照強度,可自行調整
});
const sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
技巧:
envMapIntensity可以控制環境光對材質的影響程度,若場景光源較強,可適度降低此值避免過曝。
3️⃣ 使用 PMREMGenerator 產生預濾環境貼圖(提升 IBL 效果)
PMREMGenerator 會把 CubeTexture 轉換成 預濾的 Mipmap,讓 MeshStandardMaterial 在不同粗糙度下都能得到正確的光照。
// 建立渲染器(必須支援 WebGL2 才能完整支援 PMREM)
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 產生 PMREM(預濾環境貼圖)
const pmremGenerator = new THREE.PMREMGenerator(renderer);
pmremGenerator.compileCubemapShader();
// 使用 PMREM 轉換 envMap
const envMapPMREM = pmremGenerator.fromCubemap(envMap).texture;
// 替換材質的 envMap
material.envMap = envMapPMREM;
material.needsUpdate = true;
// 釋放資源
pmremGenerator.dispose();
重點:若不使用 PMREM,粗糙度較高的材質會出現不自然的光斑。PMREM 能讓光線在不同粗糙度下平滑過渡。
4️⃣ 動態切換環境貼圖(例如白天/夜晚切換)
// 先預先載入兩套 CubeTexture
const dayEnv = loader.load([
'textures/day/px.jpg', 'textures/day/nx.jpg',
'textures/day/py.jpg', 'textures/day/ny.jpg',
'textures/day/pz.jpg', 'textures/day/nz.jpg'
]);
const nightEnv = loader.load([
'textures/night/px.jpg', 'textures/night/nx.jpg',
'textures/night/py.jpg', 'textures/night/ny.jpg',
'textures/night/pz.jpg', 'textures/night/nz.jpg'
]);
// 產生 PMREM
const dayPMREM = pmremGenerator.fromCubemap(dayEnv).texture;
const nightPMREM = pmremGenerator.fromCubemap(nightEnv).texture;
// 切換環境的函式
function setEnvironment(isDay) {
const env = isDay ? dayPMREM : nightPMREM;
scene.background = env; // 背景切換
material.envMap = env; // 材質環境貼圖切換
material.needsUpdate = true;
}
// 範例:每 5 秒切換一次
let isDay = true;
setInterval(() => {
isDay = !isDay;
setEnvironment(isDay);
}, 5000);
實務建議:若貼圖檔案較大,建議使用 gzip / brotli 壓縮,或採用 KTX2 / Basis 格式以減少載入時間。
5️⃣ 使用 HDR equirectangular 圖檔產生 CubeTexture(進階)
// 載入 HDR 全景圖(需 three/examples/jsm/loaders/RGBELoader)
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
import { PMREMGenerator } from 'three';
const hdrLoader = new RGBELoader();
hdrLoader.setDataType(THREE.UnsignedByteType); // 若使用 .hdr,使用 FloatType 會更準確
hdrLoader.load('textures/hdr/royal_esplanade_1k.hdr', (hdrTexture) => {
// 轉換為 CubeTexture
const envMap = pmremGenerator.fromEquirectangular(hdrTexture).texture;
// 設定背景與材質
scene.background = envMap;
material.envMap = envMap;
material.needsUpdate = true;
// 釋放原始 HDR 資源
hdrTexture.dispose();
});
說明:HDR 圖檔提供更寬廣的亮度範圍,搭配
MeshStandardMaterial可得到更自然的光照與反射。此方法適合高品質的產品可視化或遊戲原型。
常見陷阱與最佳實踐
| 陷阱 | 解決方式 | 為什麼重要 |
|---|---|---|
| 貼圖方向不正確(六張圖的 X/Y/Z 方向錯位) | 使用 THREE.CubeTextureLoader 時,檔名順序必須嚴格遵守 px, nx, py, ny, pz, nz。若方向錯誤,可在載入後使用 texture.mapping = THREE.CubeReflectionMapping 強制設定。 |
方向錯誤會導致環境貼圖的反射「倒置」或「錯位」,破壞真實感。 |
| 貼圖尺寸不一致 | 確保六張圖的寬高相同,且為 2 的次方(256、512、1024…)。 | GPU Mipmap 生成需要相同尺寸,否則會自動縮放,影響效能與畫質。 |
| 未使用 PMREM | 為 MeshStandardMaterial 提供 pmremGenerator.fromCubemap 產出的貼圖。 |
粗糙材質會出現雜訊或不自然的光斑。 |
| 過大的貼圖檔案 | 採用 KTX2 / Basis 或 DDS 壓縮格式,並在服務器端啟用 gzip/brotli。 | 減少下載時間與記憶體佔用,提升手機端體驗。 |
| 環境貼圖與場景光源不協調 | 使用 THREE.HemisphereLight 或 THREE.AmbientLight 配合環境貼圖,或直接使用 scene.environment = envMap(Three.js r150+)讓渲染器自動提供環境光。 |
只靠環境貼圖不會產生間接光,場景會顯得暗淡。 |
| 忘記釋放資源 | 在不再使用時呼叫 texture.dispose()、pmremGenerator.dispose()。 |
防止記憶體泄漏,尤其在單頁應用(SPA)中切換場景時。 |
小技巧
- 預載 (preload) 環境貼圖:在首次顯示前使用
loader.load的onLoad回呼,確保使用者不會看到白屏或貼圖閃爍。 - 使用
scene.environment:自 r150 起,Three.js 支援直接將環境貼圖指定給scene.environment,渲染器會自動將其作為 IBL 使用,簡化程式碼。 - 動態模糊 (blur) 背景:若想要「景深」效果,可在渲染後將
scene.background用THREE.ShaderPass做高斯模糊,保持環境貼圖的反射仍保持清晰。
實際應用場景
| 場景 | 為何使用 CubeTexture & Environment Map |
|---|---|
| 產品可視化(金屬手機、玻璃瓶) | 讓材質呈現真實的鏡面反射與光澤,提升客戶的購買慾望。 |
| 3D 遊戲(太空船、機器人) | 使用環境貼圖提供全局光照,減少大量光源的計算,提升 FPS。 |
| 虛擬展覽(博物館、畫廊) | 透過立方體天空盒營造沉浸式環境,並讓展品表面自然反射展廳光線。 |
| AR/VR 應用 | 立方體貼圖的取樣方式在 WebXR 中效能較佳,可即時更新環境光。 |
| 教學與原型 | 快速切換白天/夜晚環境,驗證材質在不同光照下的表現。 |
案例:在一個展示「高光金屬汽車」的網站中,開發者使用
PMREMGenerator產生的 CubeTexture 作為scene.environment,再加上MeshStandardMaterial的metalness: 1、roughness: 0.1,最終得到光滑且具備環境反射的車身,且在手機端仍保持 60 FPS 以上的流暢度。
總結
- CubeTexture 是由六張方向貼圖組成的立方體貼圖,適合即時渲染環境反射與 IBL。
- Environment Map(環境貼圖)透過
MeshStandardMaterial.envMap或scene.environment為材質提供反射與間接光。 - 使用 PMREMGenerator 產生預濾環境貼圖,可讓不同粗糙度的材質都呈現自然光照。
- 常見的錯誤包括貼圖方向、尺寸不一致、未使用 PMREM、資源未釋放等,掌握最佳實踐即可避免效能與畫質問題。
- 這項技術在 產品展示、遊戲、虛擬展覽與 AR/VR 等領域都有廣泛應用,能顯著提升使用者的沉浸感與視覺品質。
下一步:試著將本教學中的範例套用到你自己的 Three.js 專案,先從簡單的金屬球體開始,逐步加入 HDR 環境貼圖與動態切換,感受環境光對畫面氛圍的巨大影響吧!祝開發順利 🚀