簡介 React-Three-Fiber,淺談 WebGL 及 Three.js

WebGL (Web Graphics Library) 是一個基於 OpenGL ES 2.0 的 JavaScript API,允許瀏覽器在無需任何插件的情況下,透過 <canvas> 元素進行 2D 或 3D 的繪圖。

其中,OpenGL ES (OpenGL for Embedded System) 是 OpenGL 的精簡版本,專門為了嵌入式系統、行動裝置和瀏覽器設計。其渲染(Rendering)的基礎原理是將給定的數據計算後,產生出最終畫面。

舉例來說,輸入的數據包含物體的頂點座標、紋理、相機位置及光源資訊,而輸出的畫面則是從相機視角所看到的影像,OpenGL 的核心功能就是計算並渲染:算出 3D 物體在畫面上的位置、將 3D 物體轉換成 2D 像素,最後再加入光照來著色像素。

而 WebGL 在實務上,就是在瀏覽器中執行 OpenGL 的渲染功能。

Three.js 基礎原理

Three.js 則是將 WebGL 封裝成更高階的 API,使得開發者不需要關注 WebGL 較底層的細節。像是在繪製一個物體上,使用 WebGL 需要自己撰寫 Shaders(著色器程式)來完成 3D 轉 2D 像素的細節,而透過 Three.js 中則只要專注在畫什麼(物體的形狀、材質、光源)、如何觀看(拍攝物體的位置和角度)即可,不需考慮「怎麼畫出來」(底層的運算和渲染細節)。

基於這樣的簡化,Three.js 渲染的流程大致能歸納成 Scene(場景)、Camera(相機)、Renderer(渲染器)。Scene 就是 3D 世界的容器,可以放入各種物體、加入燈光;Camera 則決定觀看的視角,有 Perspective(透視)和 Orthographic(正交)兩種 Camera 可供選擇;而 Renderer 負責將 Scene 和 Camera 計算出的結果渲染至 HTML 的 <canvas> 元素上。

以下是一個繪製方塊的簡單例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import * as THREE from 'three';

// Scene, Camera, Renderer
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10);
const renderer = new THREE.WebGLRenderer();

// Cube
const cube = new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshNormalMaterial());
scene.add(cube);

renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

camera.position.set(5, 5, 5);
camera.lookAt(0, 0, 0);
renderer.render(scene, camera);

這段程式碼使用了 Perspective Camera 模擬人眼的視覺效果,並設定視野(FoV, Field of View)為 70 度、畫面比例依照視窗的寬高比、最近和最遠可見距離分別為 0.01 和 10 個單位。

接著,我們在 Scene 中加入一個由 Geometry(幾何體)和 Material(材質)組成的 Mesh(網格物體),其中又指定 BoxGeometry(預設為寬高深皆為 1 的立方體)、MeshNormalMaterial(一種根據面的方向顯示不同顏色的材質),最後產生出來的方塊長成這樣。

Three.js 渲染立方體

為了同時看到立方體的三個面,我們設定了相機的位置 (5, 5, 5),也就是在方塊的右上後方,如此一來便能觀察到完整的一顆方塊了。

簡介 React-Three-Fiber

最後談到 React-Three-Fiber,這是一個基於 React 的 Three.js Renderer,可以將 Three.js 中的物體、材質、相機及光源等元素封裝成 React Components,讓開發者直覺地設計 3D 場景。

以下便是一個與前章節中能和 Three.js 範例裡呈現相同效果的程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { Canvas } from '@react-three/fiber';

export default () => {
return (
<div style={{ width: '100vw', height: '100vh', margin: 0, padding: 0, overflow: 'hidden', position: 'fixed', top: 0, left: 0 }}>
<Canvas gl={{ pixelRatio: window.devicePixelRatio }} camera={{ fov: 70, near: 0.01, far: 10, position: [5, 5, 5] }}>
<color attach="background" args={['black']} />
<mesh>
<boxGeometry />
<meshNormalMaterial />
</mesh>
</Canvas>
</div>
);
};

此處同樣設定了相機,包含 FoV、位置等等,也加入了包含 <boxGeometry /><meshNormalMaterial /><mesh /> Component。

除了將 Three.js 封裝成 React 直覺式的 JSX 語法,也融入了 React Component 的生命週期。例如在 Three.js 中需要呼叫 scene.add()dispose() 來管理 Scene 的資源,但是被封裝在 React Component 之後,這些資源都會自動在 Mount 及 Unmount 處理。

參考資料

  1. MDN - WebGL: 2D and 3D graphics for the web
  2. Wiki - OpenGL ES
  3. Three.js - Create a scene
  4. Wiki - 視野
  5. React Three Fiber