本文 AI 產出,尚未審核

Three.js 課程 – 材質 (Materials)

主題:光照材質 – MeshStandardMaterialMeshPhongMaterial


簡介

在 3D 網頁應用中,材質(Material)決定了模型表面的外觀與光照互動方式。光照材質是最常用的兩大類型:MeshStandardMaterial(基於 PBR 物理渲染)與 MeshPhongMaterial(傳統 Phong 演算法)。了解它們的差異與使用時機,能讓你的 Three.js 場景從「平板」躍升為「真實感十足」的作品。

本篇文章將從概念說明、實作範例、常見陷阱與最佳實踐,逐步帶領 初學者到中階開發者 掌握這兩種材質的核心要點,並提供可直接套用的程式碼片段,讓你在實務專案中快速上手。


核心概念

1. 為什麼要分「Standard」與「Phong」?

特性 MeshStandardMaterial MeshPhongMaterial
光照模型 基於 PBR(Physically Based Rendering),使用金屬度 (metalness) 與粗糙度 (roughness) 參數模擬真實光線反射 傳統 Phong 模型,以鏡面反射 (specular) 與高光 (shininess) 控制光澤
環境光遮蔽 支援 環境貼圖 (environment map)IBL(Image Based Lighting) 只能使用簡單的環境光
效能 相對較重,特別在大量物件或低階裝置上需要注意 較輕量,適合快速原型或低階裝置
適用情境 金屬、陶瓷、皮革等需要真實感的材質 卡通、低多邊形、需要快速渲染的場景

簡單來說,如果你追求真實感,首選 MeshStandardMaterial如果你偏好渲染速度或風格化外觀MeshPhongMaterial 仍然是可靠的選擇。


2. MeshStandardMaterial 的關鍵屬性

屬性 說明 常見值
color 基礎顏色 (RGB) 0xffffff
metalness 金屬度,0 (非金屬) → 1 (純金屬) 0.0 ~ 1.0
roughness 粗糙度,0 (光滑) → 1 (粗糙) 0.0 ~ 1.0
map 漫反射貼圖 (diffuse) THREE.Texture
normalMap 法線貼圖,提高細節 THREE.Texture
envMap 環境貼圖,用於反射 THREE.CubeTexture
transparent / opacity 透明度控制 true / 0.5

小技巧:金屬材質的 roughness 設為 0.1 ~ 0.3,可得到光滑金屬的感覺;非金屬材質則把 metalness 設為 0,再調整 roughness 以模擬不同表面。


3. MeshPhongMaterial 的關鍵屬性

屬性 說明 常見值
color 基礎顏色 0xffffff
specular 高光顏色,決定鏡面反射的顏色 0x111111
shininess 高光強度,值越大光澤越明亮 0 ~ 100
map 漫反射貼圖 THREE.Texture
normalMap 法線貼圖 THREE.Texture
emissive 自發光顏色 0x000000
wireframe 是否以線框模式渲染 true / false

MeshPhongMaterialshininessspecular 共同決定了光澤度與鏡面反射的顏色,對於卡通或簡易的 UI 元件非常實用。


4. 材質與光源的關係

  • PBR 材質 需要 環境光 (Ambient Light)方向光 (Directional Light)點光源 (Point Light)聚光燈 (SpotLight) 才能完整展現金屬感與粗糙感。
  • Phong 材質 只要有 至少一個點光源或方向光,即可產生明顯的高光。

實務建議:在同一場景中混用時,先把 全域光源(Ambient)放在最底層,然後再根據需求加入 方向光(模擬太陽)與 點光源(局部光源),這樣兩種材質都能得到合理的光照。


程式碼範例

以下示範 5 個常見的實作情境,從最簡單的「純色材質」到「環境貼圖 + 法線貼圖」的完整範例。所有範例均假設已有基本的 Three.js 場景(scenecamerarenderer)設定完成。

範例 1:純色 MeshStandardMaterial

// 建立一個金屬感的球體
const geometry = new THREE.SphereGeometry(1, 32, 32);
const material = new THREE.MeshStandardMaterial({
  color: 0xaaaaaa,   // 基礎灰色
  metalness: 0.9,    // 高金屬度
  roughness: 0.2    // 稍微粗糙,避免完全鏡面
});
const sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);

說明:只需要三個屬性即可得到金屬球的感覺,適合快速測試光源配置。


範例 2:帶有貼圖的 MeshPhongMaterial

// 載入貼圖
const textureLoader = new THREE.TextureLoader();
const brickMap = textureLoader.load('textures/brick_diffuse.jpg');

// 建立磚牆平面
const planeGeo = new THREE.PlaneGeometry(10, 10);
const planeMat = new THREE.MeshPhongMaterial({
  map: brickMap,
  specular: 0x222222, // 稍微暗的高光
  shininess: 30       // 中等光澤
});
const plane = new THREE.Mesh(planeGeo, planeMat);
plane.rotation.x = -Math.PI / 2; // 讓平面水平
scene.add(plane);

說明MeshPhongMaterial 能直接使用貼圖,且 shininess 控制磚牆的光澤度,適合卡通或較簡單的渲染需求。


範例 3:環境貼圖 (CubeTexture) + MeshStandardMaterial

// 建立環境貼圖(六面體)
const cubeTextureLoader = new THREE.CubeTextureLoader();
const envMap = cubeTextureLoader.load([
  'env/px.jpg', 'env/nx.jpg',
  'env/py.jpg', 'env/ny.jpg',
  'env/pz.jpg', 'env/nz.jpg'
]);
scene.environment = envMap; // 讓所有 PBR 材質自動使用

// 金屬球體
const metalGeo = new THREE.SphereGeometry(1, 64, 64);
const metalMat = new THREE.MeshStandardMaterial({
  metalness: 1.0,
  roughness: 0.05,
  envMap: envMap,
  color: 0xffffff
});
const metalSphere = new THREE.Mesh(metalGeo, metalMat);
metalSphere.position.set(2, 1, 0);
scene.add(metalSphere);

說明:設定 scene.environment 後,所有 MeshStandardMaterial 都會自動使用環境貼圖產生反射。金屬度 1、粗糙度 0.05 產生鏡面反射效果。


範例 4:結合法線貼圖提升細節(PBR)

const textureLoader = new THREE.TextureLoader();
const woodAlbedo = textureLoader.load('textures/wood_albedo.jpg');
const woodNormal = textureLoader.load('textures/wood_normal.jpg');

const boxGeo = new THREE.BoxGeometry(2, 2, 2);
const woodMat = new THREE.MeshStandardMaterial({
  map: woodAlbedo,
  normalMap: woodNormal,
  metalness: 0.0,
  roughness: 0.6
});
const woodBox = new THREE.Mesh(boxGeo, woodMat);
scene.add(woodBox);

說明:法線貼圖 (normalMap) 能在不增加多邊形數量的情況下,讓木材表面呈現凹凸感,提升真實感。


範例 5:動態切換材質(實務常見需求)

let currentMaterial = 'standard'; // 初始為 PBR

function toggleMaterial() {
  if (currentMaterial === 'standard') {
    // 換成 Phong
    mesh.material = new THREE.MeshPhongMaterial({
      map: textureLoader.load('textures/brick_diffuse.jpg'),
      shininess: 50,
      specular: 0x555555
    });
    currentMaterial = 'phong';
  } else {
    // 換回 Standard
    mesh.material = new THREE.MeshStandardMaterial({
      map: textureLoader.load('textures/brick_diffuse.jpg'),
      metalness: 0.2,
      roughness: 0.7,
      envMap: envMap
    });
    currentMaterial = 'standard';
  }
}

// 監聽鍵盤事件切換
window.addEventListener('keydown', e => {
  if (e.key === 'm') toggleMaterial();
});

說明:在開發工具或 Demo 時,常需要即時切換材質觀察差異。這段程式碼示範如何在同一個 mesh 上動態切換 MeshStandardMaterialMeshPhongMaterial


常見陷阱與最佳實踐

陷阱 說明 最佳實踐
忘記加入光源 MeshStandardMaterial 在沒有光源時會呈現全黑 必須至少配置 AmbientLight + DirectionalLight
粗糙度與金屬度同時設為 1 會產生不可預期的「鏡面」效果,且不符合物理模型 金屬度 1 時把 粗糙度 設在 0~0.3金屬度 0 時可自由調整粗糙度
環境貼圖尺寸過大 會造成記憶體占用過高、載入緩慢 建議使用 256×256512×512 的 cube map,並使用 .setEncoding(THREE.sRGBEncoding)
法線貼圖未正規化 產生奇怪的光照斑點 確保貼圖在圖像編輯軟體中已 標準化 (normalize),或在載入後使用 texture.encoding = THREE.sRGBEncoding
材質切換忘記釋放舊貼圖 造成 GPU 記憶體泄漏 在切換材質前,使用 oldMaterial.dispose() 釋放貼圖與緩衝區
過度使用高多邊形模型 PBR 計算已較耗資源,若再加上大量頂點會卡頓 盡量使用 LOD(Level of Detail)InstancedMesh,把細節交給貼圖與法線貼圖補償

實際應用場景

  1. 產品展示(電商)

    • 使用 MeshStandardMaterial 搭配 HDR 環境貼圖,呈現金屬、玻璃、皮革等材質的真實光澤,提升客戶購買慾望。
  2. 互動式教學或資料視覺化

    • MeshPhongMaterial 速度快,適合在大量圖表、快速切換顏色與光澤的情況下使用。
  3. 遊戲與虛擬實境 (VR)

    • 在需要高真實感的角色或道具上使用 PBR;UI 按鈕、指示牌則可採用 Phong 以降低效能負擔。
  4. AR 應用

    • 由於行動裝置效能有限,常見做法是 混合:主體使用 MeshStandardMaterial(金屬/玻璃),背景與輔助物件使用 MeshPhongMaterial
  5. 建築可視化

    • 建築外牆、玻璃幕牆使用 PBR,地面或草坪則可用 Phong 以加速渲染。

總結

  • MeshStandardMaterial:基於 PBR,提供金屬感、粗糙度、環境反射等真實渲染效果;適合需要高寫實度的場景。
  • MeshPhongMaterial:採用 Phong 模型,計算簡單、渲染快速,適合卡通、低多邊形或效能受限的情況。
  • 正確的 光源配置貼圖管理材質切換 是避免常見陷阱的關鍵。
  • 在實務開發中,混合使用 兩種材質、根據裝置效能調整 金屬度、粗糙度,能兼顧畫質與效能。

掌握了這兩種光照材質的特性與最佳實踐後,你就能在 Three.js 中自由打造 逼真風格化 的 3D 作品,無論是電商產品頁、互動教學還是遊戲開發,都能得心應手。祝你玩得開心、寫得順利!