快轉到主要內容
  1. Labs/

用 React Three Fiber 做一個 3D 產品展示系統

Idle Engineer
作者
Idle Engineer
AI Runs. I Nap. | 404 Career Not Found
目錄

做了什麼
#

一個可以在瀏覽器裡即時操作的 3D 頭盔展示系統:換顏色、調材質、切換環境光。

GitHub: maymap/r3f-product-viewer


動機
#

看到一份 Three.js 工程師的職缺,想搞清楚這份工作實際在做什麼、作品集要做到什麼程度。從沒碰過 Three.js,但有 React 基礎,就從這個方向切入。


技術棧
#

  • React Three Fiber(R3F):Three.js 的 React 封裝,讓你用 JSX 寫 3D 場景
  • @react-three/drei:R3F 的工具庫,useGLTFOrbitControlsEnvironmentContactShadows 都來自這裡
  • Three.js:底層渲染引擎

學到的幾件事
#

PBR 材質不是「好看的貼圖」
#

一個 GLB 模型裡面通常打包了好幾張圖,每張負責不同的事:

  • Normal Map:用貼圖騙光線,讓平面的 mesh 看起來有凹凸細節
  • Roughness Map:每個像素記錄那個點的粗糙程度,所以金屬件亮、橡膠件霧,不是畫上去的顏色
  • AO Map:預先算好縫隙和角落的陰影,讓模型接縫看起來自然

三張圖加在一起,讓幾千個三角面的低模看起來像高模。

useFrame 是動畫的核心
#

useFrame((state) => {
  groupRef.current.position.y = Math.sin(state.clock.elapsedTime * 0.8) * 0.06
})

每一幀執行一次,用時間軸驅動位移,做出浮動效果。Three.js 幾乎所有動畫都走這個模式。

直接改 material 要小心累積誤差
#

第一版的 slider 實作是直接乘以倍率修改 material.roughness,結果拉來拉去之後值就歪掉了——因為每次都在改上次改過的值。

解法是在模型載入時用 useRef 快取原始材質數值,之後每次都從原始值計算:

const originals = useRef(null)

if (!originals.current) {
  const cache = new Map()
  scene.traverse(node => {
    if (node.isMesh) {
      cache.set(node.uuid, {
        roughness: node.material.roughness,
        metalness: node.material.metalness,
      })
    }
  })
  originals.current = cache
}

這份工作實際在做什麼
#

做完之後對職缺的理解:

  • 這個 demo 大概是職缺要求能力的 10%
  • 真實專案要處理的是:多零件模型的獨立換色、draw call 優化、行動裝置 60fps、GLB asset pipeline(KTX2 貼圖壓縮)
  • 「懂 PBR 材質」不是會調 slider,是知道哪張貼圖出問題要怎麼 debug

還差得遠,但至少現在知道差在哪裡。