上一篇介紹了 Three.js、render、還有 shader 的基本概念,今天我們馬上來把手弄髒吧!
在學習任何語言都會有一個 Hello, World!
,而 Three.js 框架的第一個專案,我們可以簡單建立一個場景,今天我們就手把手的建立專案、同時一邊學習一個「場景」有什麼必備的要素。
開發 JavaScript 的專案非常的簡便,只需要使用任一個熟悉的編輯器跟 npm
(node package manager) 就可以,在此使用 Visual Studio Code,vs code 相信我也不需多做介紹,已經是很多人在用的 IDE,針對不同語言與需求,他提供許多延伸模組使得開發流程更便利。
在視窗下方選擇 終端機
、或是直接使用電腦本身的終端機,首先確定 node
和npm
都有正確下載。只要叫的到版本,就代表有安裝好囉!沒有的話可以根據作業系統尋找下載方法,這邊就不多做贅述。
% node --version
v21.7.1
% npm --version
10.7.0
接著,在安裝 three.js library 之前,我想說如果在開發的時候可以有 hot module replacement,也就是在 local run server 的時候,每次改動程式碼,網頁可以更新、不需要關掉重開 server 會是一個很重要的功能,同時我們也不想要裝一堆笨重的 module,希望可以維持在輕量級的開發。因此依賴 Vite
這個前端的工具會是一個不錯的選擇!
% npm create vite@latest [project_name] --template vanilla
這邊的 template/ framework 我們選擇 vanilla javascript,先從最基本的語法開始練習吧。等安裝好就可以進入專案資料夾中,安裝 three.js。
% cd [project_name]
% npm i three
這個時候可以先跑跑看,測試我們新建好的專案能不能運行。這邊的語法對應到 package.json
設定檔的 script。(比如初始應該有 dev
、 build
、 preview
。
% npm run dev
等到終端機跳出 ready 的訊息時,在瀏覽器貼上 URL,確認有以下畫面就沒問題了!
每一個畫面,可以想成是算圖工具使用攝影機將場景裡的東西畫出來**,因此我們需要這四個元件就可以完成 Three.js 的 Hello, World!
啦!!
首先進入 main.js
把多餘的程式刪掉,只需要留下引入 library 的部分,還有像是 counter.js
和靜態資源如圖片等用不到的檔案也移除。
// main.js
import * as THREE from 'three'
場景基本上就是一個容器,把想畫的東西放入後,晚點 renderer 會把場景裡的畫面畫出來。最最基本的第一步,我們先新增一個初始設定的場景,背景顏色會是黑色。
const scene = new THREE.Scene()
如果想要增加更詳細的設定,比如說改變背景的顏色或材質,都可以在寫得很完整的官方文件找到!
在 JavaScript 裡,我們知道除了基本的數字、布林值等之外,其他萬物皆物件。這邊指的物件是 Three 有一些先定義好的,比如說幾何形狀、模型、燈光等。
一般開發最常用的模型是使用 mesh 的型態,可以想像成用許多三角形或四邊形拼成的幾何體,這其中的三角形,都有頂點跟材質(比如說顏色、反光跟粗糙程度),我們同樣可以在官方文件找到相關的資料。
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 })
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)
所以我們在場景中加入一個簡單的 Mesh
其中包含長寬高各等於 1 的 BoxGeometry
,以及一個基本的材質 MeshBasicMaterial
,僅提供顏色和一些簡單的參數可以調整。我們可以注意到 MeshBasicMaterial
傳入的參數為一個物件,在這邊的顏色是已經 Three 定義好的顏色 Class,可以使用顏色名稱如 red
來指定,或是直接參考 color class 的頁面。
相機比前兩個要素稍微複雜一些,在這邊我們先來定義相機是如何運作的:因為運算資源有限,相機則會把要運算的區域 (frustum) 定義出來。
在一般的 rendering 討論範圍中,相機可以被分成 perspective 跟 orthogonal 兩種。
這兩種主要的相機類型的差異是在投影方式和視覺效果上。
Perspective Camera(透視相機)- 透視相機模擬人眼的視覺效果,物體隨著距離的增加而變小。這種相機通常用於需要真實感的場景,例如遊戲和模擬應用。
// Perspective Camera
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
camera.position.z = 5
fov
(field of view 視場角/度):frustum 垂直方向上的視角aspect
(aspect ratio 長寬比):畫布的寬度除以高度near
(near plane):frustum 裡從相機到場景中最近可見物體的距離far
(far plane):frustum 裡從相機到場景中最遠可見物體的距離Orthogonal Camera(正交相機)- 正交相機不會隨距離改變來縮放物體,所有物體的大小在視圖中都是一致的。這種相機通常用於工程圖、建築圖和一些2D遊戲,因為它不會產生透視變形。
// Orthogonal Camera
const aspectRatio = window.innerWidth / window.innerHeight
const camera = new THREE.OrthographicCamera(-aspectRatio * 5, aspectRatio * 5, 5, -5, 0.1, 1000)
camera.position.z = 5
left
, right
, top
, bottom
:frustum 上下左右的平面座標near
:從相機到場景中最近可見物體的距離far
:從相機到場景中最遠可見物體的距離太好了,經過一番寒徹骨,總算來到最後一步,將場景畫到畫布上。
在網頁加入畫布有許多做法,舉例來說,可以直接在 HTML 文件加上 <canvas>
再用document.querySelector
把 canvas 指定給 renderer。
我們這邊嘗試簡單一點的作法,直接在 js 生成 HTML 的節點。
const renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
renderer.render(scene, camera)
此時發現 canvas 有一個白邊,因此我們加上 css 把網頁 body
的白邊剔除。別忘了在 vite 可以從 js 檔案直接導入 css
body {
margin: 0;
}
import './style.css'
import * as THREE from 'three'
// ...
最後,根據前面的介紹,希望有興趣的人可以自己練習看看這些要求,看是不是能從官方文件或線上資源找到相對的程式碼。
—
好的!那麼明天會把延伸練習的解答公布,同時我把今天完成完整的 js 程式碼放在下方,有興趣的人可以參考,覺得有稍稍幫助到、或是覺得 3D 蠻有趣的話,麻煩幫點個愛心,我們就明天再見~
import './style.css'
import * as THREE from 'three'
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
const renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
const geometry = new THREE.ConeGeometry()
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
const cube = new THREE.Mesh(geometry, material)
scene.add(cube)
camera.position.z = 5
renderer.render(scene, camera)