本文 AI 產出,尚未審核

Three.js 教學 – 貼圖 (Textures):載入 TextureLoader

簡介

在 3D 網頁開發中,貼圖是讓模型表面呈現真實感的關鍵。沒有貼圖的幾何體只是一個單色的平面或立方體,難以表達材質、顏色與細節。Three.js 提供了功能完整且易於使用的 TextureLoader,讓開發者只需幾行程式碼,就能把 JPEG、PNG、甚至 HDR 圖檔載入並套用到材質上。

本篇文章將從 概念說明實作範例常見陷阱與最佳實踐,一步步帶你掌握 TextureLoader 的使用方式,並展示在實務專案中常見的應用情境。即使你是剛接觸 Three.js 的新手,也能在閱讀完本篇後,快速在自己的作品裡加入高品質的貼圖。


核心概念

1. 為什麼使用 TextureLoader

  • 非同步載入:瀏覽器會在背景下載影像檔,避免阻塞主執行緒。
  • 自動產生 MIPMAP:對遠距離或縮小的貼圖自動產生多層級影像,提高渲染效能與抗鋸齒。
  • 支援跨域:可設定 crossOrigin 屬性,載入 CDN 或其他網域的資源。

:在 Three.js 0.150 之後,TextureLoader 預設使用 WebGL2Sampler,若要手動調整過濾方式,需要透過 texture.minFiltertexture.magFilter 等屬性。

2. 基本使用流程

  1. 建立 TextureLoader 實例
  2. 呼叫 .load( url, onLoad, onProgress, onError )
  3. onLoad 回呼中取得 THREE.Texture 物件
  4. 把貼圖指派給材質的 map(或 normalMaproughnessMap 等)
const loader = new THREE.TextureLoader();
loader.load(
  'textures/brick_diffuse.jpg',
  ( texture ) => {
    // 貼圖載入完成
    const material = new THREE.MeshStandardMaterial({ map: texture });
    const mesh = new THREE.Mesh( geometry, material );
    scene.add( mesh );
  },
  undefined,
  ( err ) => console.error('貼圖載入失敗', err)
);

3. 進階設定:crossOriginencodingflipY

  • crossOrigin:若貼圖位於不同網域,需要設定 loader.setCrossOrigin( 'anonymous' ),否則會因 CORS 政策被阻擋。
  • encoding:對於 sRGB 影像(如大多數 JPEG/PNG),應設定 texture.encoding = THREE.sRGBEncoding,讓顏色在 gamma 空間正確顯示。
  • flipY:WebGL 預設把貼圖 Y 軸翻轉,若貼圖已經做好翻轉(例如使用 Photoshop),可以 texture.flipY = false

程式碼範例

以下提供 五個 常見且實用的範例,從最簡單的載入到使用 LoadingManager、重複貼圖與立方體貼圖(CubeTexture)等進階技巧,皆附上詳細註解說明。

範例 1:最簡單的貼圖載入

// 建立場景、相機、渲染器(略)
const geometry = new THREE.BoxGeometry(2, 2, 2);
const loader = new THREE.TextureLoader();

loader.load('assets/textures/wood.jpg', (tex) => {
  // 設定貼圖編碼為 sRGB,避免顏色偏暗
  tex.encoding = THREE.sRGBEncoding;

  const material = new THREE.MeshStandardMaterial({ map: tex });
  const cube = new THREE.Mesh(geometry, material);
  scene.add(cube);
});

重點:只要把 Texture 指派給 material.map,Three.js 會自動建立 UV 坐標(若模型已有 UV)。

範例 2:使用 LoadingManager 同步管理多張貼圖

// 建立一個 LoadingManager,所有貼圖的載入進度都會集中在這裡
const manager = new THREE.LoadingManager();
manager.onStart = () => console.log('開始載入貼圖...');
manager.onProgress = (url, itemsLoaded, itemsTotal) =>
  console.log(`已載入 ${itemsLoaded}/${itemsTotal}: ${url}`);
manager.onLoad = () => console.log('全部貼圖載入完成!');
manager.onError = (url) => console.error('載入失敗:', url);

const loader = new THREE.TextureLoader(manager);

// 同時載入三張貼圖
const diffuse = loader.load('textures/metal_diffuse.jpg');
const normal  = loader.load('textures/metal_normal.jpg');
const rough   = loader.load('textures/metal_roughness.jpg');

// 設定編碼與貼圖參數
diffuse.encoding = THREE.sRGBEncoding;
normal.encoding  = THREE.LinearEncoding;

// 建立材質
const material = new THREE.MeshStandardMaterial({
  map: diffuse,
  normalMap: normal,
  roughnessMap: rough,
});

技巧LoadingManager 可與 GLTFLoaderOBJLoader 等其他載入器共用,便於顯示全局載入進度條。

範例 3:重複貼圖(Repeating Texture)

const loader = new THREE.TextureLoader();
loader.load('textures/tiles.png', (tex) => {
  // 讓貼圖在 U、V 方向各重複 4 次
  tex.wrapS = THREE.RepeatWrapping;   // 水平 (U) 包裝模式
  tex.wrapT = THREE.RepeatWrapping;   // 垂直 (V) 包裝模式
  tex.repeat.set(4, 4);               // 重複次數

  const material = new THREE.MeshStandardMaterial({ map: tex });
  const plane = new THREE.Mesh(
    new THREE.PlaneGeometry(10, 10),
    material
  );
  scene.add(plane);
});

說明:若不設定 wrapS/TTHREE.RepeatWrappingrepeat 屬性不會生效,貼圖仍會只顯示一次。

範例 4:立方體貼圖(CubeTexture)製作環境貼圖

// 立方體貼圖需要六張影像:px, nx, py, ny, pz, nz
const path = 'textures/cube/skybox/';
const format = '.jpg';
const urls = [
  path + 'px' + format, path + 'nx' + format,
  path + 'py' + format, path + 'ny' + format,
  path + 'pz' + format, path + 'nz' + format,
];

const cubeLoader = new THREE.CubeTextureLoader();
const envMap = cubeLoader.load(urls, () => {
  console.log('環境貼圖載入完成');
});

// 設定環境貼圖的編碼
envMap.encoding = THREE.sRGBEncoding;

// 把環境貼圖套用到場景與材質
scene.background = envMap; // 背景顯示
const material = new THREE.MeshStandardMaterial({
  envMap: envMap,
  metalness: 1,
  roughness: 0,
});

應用:環境貼圖不只能作為背景,還能提供 鏡射反射envMap)與 光照PMREMGenerator)的資訊。

範例 5:自訂貼圖過濾與 MIPMAP 設定(適合高效能需求)

const loader = new THREE.TextureLoader();
loader.load('textures/grass.jpg', (tex) => {
  // 關閉自動產生 MIPMAP(若貼圖已是 MIPMAP)
  tex.generateMipmaps = false;

  // 設定過濾方式:放大使用線性過濾,縮小使用最近點過濾
  tex.magFilter = THREE.LinearFilter;
  tex.minFilter = THREE.NearestFilter; // 只在遠距離保留原始像素

  // 若貼圖尺寸不是 2 的次方,必須手動關閉 MIPMAP
  if (!THREE.MathUtils.isPowerOfTwo(tex.image.width) ||
      !THREE.MathUtils.isPowerOfTwo(tex.image.height)) {
    tex.generateMipmaps = false;
    tex.minFilter = THREE.LinearFilter;
  }

  const material = new THREE.MeshStandardMaterial({ map: tex });
  const plane = new THREE.Mesh(
    new THREE.PlaneGeometry(20, 20),
    material
  );
  scene.add(plane);
});

說明:非 2 的次方貼圖(如 1024×768)在 WebGL 中無法自動產生 MIPMAP,若不關閉會導致渲染錯誤或警告。


常見陷阱與最佳實踐

陷阱 可能的症狀 解決方案
未設定 encoding 顏色偏暗、對比度不正確 texture.encoding = THREE.sRGBEncoding(對於大部分 JPEG/PNG)
跨域 CORS 錯誤 Failed to load resource: net::ERR_FAILED 在伺服器端加上 Access-Control-Allow-Origin:*,或使用 loader.setCrossOrigin('anonymous')
貼圖尺寸非 2 的次方 MIPMAP 警告、模糊或條紋 使用圖形編輯工具調整為 256、512、1024…,或在程式中關閉 generateMipmaps
忘記更新 needsUpdate 改變貼圖屬性後無效 texture.needsUpdate = true;
同時載入過多貼圖 網路壅塞、載入時間過長 使用 LoadingManager 監控,或先載入低解析度貼圖作為佔位 (LOD)

最佳實踐

  1. 統一貼圖編碼:在專案的貼圖匯入流程中,統一使用 sRGBEncoding,避免顏色不一致。
  2. 使用 PMREMGenerator 產生環境貼圖的預濾波貼圖:對於 PBR 材質,envMap 必須經過預濾波才能正確呈現光照。
  3. 將常用貼圖放入快取(Cache):Three.js 內部會自動快取同一 URL 的貼圖,但在大型專案中可自行維護貼圖字典,避免重複載入。
  4. 結合 requestAnimationFrame 控制載入進度條:在 LoadingManageronProgress 中更新 UI,提升使用者體驗。
  5. 適度壓縮貼圖:使用 WebP、Basis 或 KTX2(支援 GPU 壓縮)可大幅減少下載大小,尤其在行動裝置上效果顯著。

實際應用場景

場景 需求 使用 TextureLoader 的方式
建築可視化 大量磚牆、木材、金屬貼圖 透過 LoadingManager 同時載入多套材質,並使用 repeat 讓同一貼圖重複鋪陳於牆面
遊戲角色 皮膚、衣服的漫反射與法線貼圖 diffuseMapnormalMaproughnessMap 結合在同一 MeshStandardMaterial,並設定 encoding
產品展示 金屬或玻璃的高光反射 使用 CubeTextureLoader 製作環境貼圖,再把 envMap 套用到金屬材質,配合 PMREMGenerator 取得平滑的光照
AR / VR 需要低延遲的貼圖載入 事先在 onBeforeRender 前先行載入貼圖,或在進入場景前完成全部貼圖的預載 (preload)
資料視覺化 以貼圖表示不同資料層級 依據資料值動態產生 CanvasTexture,然後使用 TextureLoader 載入作為材質的 map

總結

  • TextureLoader 是 Three.js 中最常用、最直觀的貼圖載入工具,支援非同步、跨域、編碼與翻轉等多項設定。
  • 透過 LoadingManagerrepeatCubeTextureLoader 等配套功能,我們可以在保持效能的同時,快速完成 PBR環境映射重複貼圖 等需求。
  • 常見的錯誤多與 CORS貼圖編碼非 2 的次方尺寸 相關,只要在開發流程中加入檢查與統一設定,就能避免大多數問題。
  • 最後,別忘了在專案中加入 貼圖快取進度條 以及 GPU 壓縮貼圖(如 Basis/KTX2),讓你的 Three.js 應用在 桌面行動 兩端都能保持流暢與美觀。

祝你在 Three.js 的世界裡玩得開心,創造出令人驚艷的 3D 體驗! 🎉