這裡是「Three.js學習日誌」的第3篇,本篇的主旨是透過試做一個Three.js的Hello World案例,來讓讀者對於Three.js有基本的認識,這系列的文章假設讀者看得懂javascript,並且有Canvas 2D Context的相關知識。
第一次使用three.js,尤其是那些沒有使用過3D建模軟體的人,多半會對每一個基本操作都有不小的疑問,畢竟three.js提供的API種類繁多,而且光看官方文件又有一大堆的專有名詞,讓人頭昏眼花。
建議初學者可以先去玩過隨便一款3D建模軟體(首推Blender),畢竟菜鳥如小弟我就是這麼做的。
這邊我們會先做一個簡單的Hello World,並且一步一步講解每個步驟的用意,目的是希望可以讓讀者試試味道,並且對Three.js產生基本的認識。
這個Hello World的目標是要畫出來一個轉動的立方體(如圖)。

為了求快速,在這個案例中我們先用codepen搭配https://cdn.skypack.dev提供的CDN來取得three.js的module。
import {
  ... //這邊接著會引入必要的模組
} from "https://cdn.skypack.dev/three";
Scene這個類別按字面解釋就是景,他有點像是世界的概念,當我們要去把一個three.js的程序render出畫面,基本上就是要去渲染這個景裡面所有物件構成的圖像。
import {
  Scene
} from "https://cdn.skypack.dev/three";
function main (){
   const scene = new Scene();
}
main();
three.js 其實有提供很多種類的renderer,但是最常用的基本上還是WebGLRenderer。渲染器顧名思義當然是用來渲染用的,他本身會提供一個render方法,可以用來把一個Scene中的圖像渲染出來。
這邊我們順手設置antialias(反鋸齒)和alpha(開啟透明通道)兩個option。
antialias(反鋸齒)是一種特殊的演算法,他可以用來柔化圖形邊緣因為像素顆粒太明顯而形成的鋸齒狀瑕疵。
而之所以要開啟alpha(透明通道)是因為three.js預設是不渲染透明通道的(也就是背景會全黑)。
還記得我們在前面演示過webgl清除畫布的方法嗎? 就是藉由填入特定的clearColor來達到清除的效果,清除色是可以設定為具有透明度的顏色的,所以假如我們要讓canvas背景為透明無色,那就必須要:
設置完
renderer實例之後要記得把renderer.domElement放到dom tree裡面(domElement其實就是canvas)
import {
  Scene
  WebGLRenderer
} from "https://cdn.skypack.dev/three";
function main (){
   const scene = new Scene();
   const renderer = new WebGLRenderer({
    antialias: true,
    alpha: true
  });
  document.body.append(renderer.domElement);
}
main();
這邊應該就沒甚麼特別的,Viewport其實就是我們在上一篇介紹過的gl.viewport方法,也就是畫布範圍的映射。
畫布大小與Viewport,還有清除色的設定方法都可以從renderer物件底下取得,他們分別是renderer.setSize,renderer.setViewport,還有renderer.setClearColor。
import {
  Scene,
  WebGLRenderer,
} from "https://cdn.skypack.dev/three";
function main() {
  const scene = new Scene();
  const renderer = new WebGLRenderer({
    antialias: true,
    alpha: true
  });
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setViewport(0, 0, window.innerWidth, window.innerHeight);
  renderer.setClearColor(new Color(0,0,0),0);
}
main();
沒有使用過3D建模軟體的人可能會不太明白Camera的概念。
在3D建模軟體中,Camera的用途就是把景物"照"下來。
我們前面有提到,three.js要渲染畫面的時候要透過renderer的render方法,我們除了要告訴renderer是要渲染哪一個景以外,還得告訴他是要使用那個景裡面的哪一台Camera,這樣才可以取得那台Camera照出來的圖像。

這張圖片(來源)稍微有點不正確,其實成像畫面不應該呈現在Near Clip plane上面。
Camera(相機)有很多種類型,每一種都各有各的使用場景。
在這邊我們使用PerspectiveCamera這個類所生成的相機實例,所謂的"Perspective"就是"透視法",所謂的"透視法"是一種繪畫的技法,用來把實際的三維空間的座標點,投射到二維平面上。
關於透視法,我去年有寫過一篇3維投影相關的技術文章,可以參考這裡
PerspectiveCamera這個類必須要傳入4個參數,分別是FOV,畫面長寬比,近平面,遠平面。

FOV: 也就是攝影機的水平視角(Field of View),基本上所有的光學成像儀器都會有FOV的設置,以人眼來講,人眼的FOV大約是120度,超出視角的東西就不會在成像畫面中顯示出來。畫面長寬比:就是預期成像畫面的長寬比。近平面/遠平面:其實就是一種範圍限制,攝影機不會把比近平面還要近,或是位置超出遠平面的東西照進去畫面。另外順帶一提,three.js的PerspectiveCamera模仿的是現實生活中的35mm相機,也就是預設具備35mm的焦距,再換句話說就是攝影機到成像平面(上圖的z-projection平面)的預設距離為35單位長。
初始化過
PerspectiveCamera的實例之後,記得要add進去Scene裡面。
import {
  Scene,
  WebGLRenderer,
  PerspectiveCamera
} from "https://cdn.skypack.dev/three";
function main() {
  ...
  
  const camera = new PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
  );
  
  scene.add(camera)
}
main();