3D網頁新手上路 — Babylon.js

Chingya
✨ 黑洞創造 BlackHole Creative ✨
13 min readJun 27, 2022

👁‍🗨 初步了解 3D 繪圖套件的使用,創造 Web 3D 空間

Babylon.js 簡述

Babylon.js 是一款基於 WebGL、HTML5 和 JavaScript 即時渲染 3D 引擎,可以通過 HTML5 在 Web 瀏覽器中呈現 3D 模型。由微軟提供完善的開發團隊,文檔完善且更新及時,支持 JavaScript 和 TypeScript 兩種編程語言,相對穩定,因此適合做大型項目。主要多集中用來製作 3D 網頁遊戲,具有碰撞檢測、抗鋸齒等遊戲特性。

目前 Babylon.js 可接受 3D 檔案類型有 gltf、glb、obj、babylon,依據檔案類型差異,取得mesh 變數也會有差異。

使用的 3D 模型可參考這篇文章:如何做出良好的 Babylon.js 模型

使用 Babylon.js 流程

Step 1 :安裝

首先,需要先使用 CDN 引入:

//主程序腳本
<script src="<https://cdn.babylonjs.com/babylon.js>"></script>

//輔助用:加載模型的檔案
<script src="<https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js>"></script>

//輔助用:統一不同瀏覽器上point操作
<script src="<https://code.jquery.com/pep/0.4.3/pep.js>"></script>

或是可以使用 NPM 的方式引入:

npm install babylonjs --saveimport * as BABYLON from 'babylonjs';

Step 2 :創建 BABYLON 引擎

const canvas = document.getElementById("renderCanvas");
const engine = new BABYLON.Engine(canvas, true);
// 當頁面被調整時,引擎也會跟著調整
window.addEventListener("resize", function () {
engine.resize();
});

Step 3:建立場景

當完成引入以及創建引擎後,我們就可以來建立 3D 場景,使用 createScene() 函數生成場景,場景就像是搭建一個舞台(如果有用過 pixi.js ,就像是 pixi.js 中的 stage 一樣)。

const createScene = function() {
var scene = new BABYLON.Scene(engine);
return scene;
}
const sceneToRender = createScene();// 預設不會轉譯場景,若要在每個畫面上重複轉譯場景,則需要執行以下程式碼:
engine.runRenderLoop(function () {
sceneToRender.render();
});

Step 4:新增攝影機

3D 的空間中,我們需要建立攝影機來顯示出畫面,Babylon 有提供不同種的攝影機,可依照所想要的功能來選擇,這邊列舉兩種最常使用的攝影機(想看更多可以參考官方文件):

📷 Universal Camera

  • 通常用來作為第一人稱視角的攝影機,操作適用於所有鍵盤、鼠標、觸控和遊戲手柄。
  • 2.3版本後新增,可代替自由相機(Free Camera)、觸控相機(Touch Camera)和遊戲手柄相機(Gamepad Camera)。
  • 參數:name(攝影機名稱),position(攝影機初始位置),scene(攝影機位於的場景)
//參數: name, position, scene
const camera = new BABYLON.UniversalCamera("UniversalCamera", new BABYLON.Vector3(0, 0, -10), scene);

//設定相機朝向的目標
camera.setTarget(BABYLON.Vector3.Zero());

//設定控制畫布
camera.attachControl(canvas, true);

📷 Arc Rotate Camera

  • 攝影機固定指向一個目標位置,旋轉圍繞依照目標來移動。
  • 參數:name(攝影機名稱),Alpha(縱軸旋轉角度),Beta(橫軸旋轉角度),radius(半徑),target position(目標位置),scene(攝影機位於的場景)
  • 參數中的 Alpha、Beta、radius 的概念,可以參照下圖說明,可以比較理解數值的意思 👇👇👇
From Babylon Camera Introduction
//參數: name, alpha, beta, radius, target position, scene
const camera = new BABYLON.ArcRotateCamera("ArcRotateCamera", 0, 0, 10, new BABYLON.Vector3(0, 0, 0), scene);

//設定攝影機的位置
camera.setPosition(new BABYLON.Vector3(0, 0, 20));

//設定控制畫布
camera.attachControl(canvas, true);

Step 5:新增光源

架設好攝影機之後,需要再添加光源,否則看到的任何物體都會是黑色的,如同人在現實環境中,也是需要有光才有辦法看到物體的顏色,而在 Babylon 中總共有四種光源可以使用。

💡 點光源(BABYLON.PointLight)

  • 參數:name,position(場景中的光源位置),scene
  • 類似燈泡光源,照在平面上會有點的效果。
const light = new BABYLON.PointLight("pointLight", new BABYLON.Vector3(1, 10, 1), scene)

💡 方向光(BABYLON.DirectionalLight)

  • 參數:name,direction(光源照射方向),scene
  • 模擬從遠方照過來的光,例如太陽光。
const light = new BABYLON.DirectionalLight("DirectionalLight", new BABYLON.Vector3(0, -1, 0), scene);

💡 聚光燈(BABYLON.SpotLight)

  • 參數:name,position(場景中的光源位置),direction(光源照射方向),angle(照射的角度),exponent(光的衰減指數),scene
  • 模擬手電筒、探照燈的光源,將光匯聚到一個地方。
  • 角度大小影響聚光程度,角度越大、聚光效果越差、照射區域越大。
  • 光的衰減指數越大,衰減越快。
const light = new BABYLON.SpotLight("spotLight", new BABYLON.Vector3(0, 30, -10), new BABYLON.Vector3(0, -1, 0), Math.PI / 3, 2, scene);

💡 環境光(BABYLON.HemisphericLight)

  • 參數:name,direction(光源的朝向),scene
  • 通常設定都是朝向天空、向上,也就是 Vector3(0, 1, 0)
const light = new BABYLON.HemisphericLight("HemiLight", new BABYLON.Vector3(0, 1, 0), scene);

光源可以不只一個和一種,可依照需求自行增加。如果需要變更光的顏色,可以使用三個參數進行調整,分別是 diffuse、specular 和 groundColor,顏色設定使用 BABYLON.Color3 調整 RGB,數值位於 0 ~ 1 之間。

  • diffuse 代表光的主體顏色(所有光源都有)
  • specular 代表照在物體上高亮區域的顏色(所有光源都有)
  • groundColor 代表地上反射光的顏色(只有環境光有)
light.diffuse = new BABYLON.Color3(0, 1, 1);
light.specular = new BABYLON.Color3(1, 1, 0);
light.groundColor = new BABYLON.Color3(0, 1, 0);

Step 6:新增 3D 物件

現在我們已經準備好舞台、攝影機和光線,接下來就輪到主角登場了!所謂的「主角」指的是我們要在場景中放置的物件,BABYLON 中可以建立一些基本圖形,它提供兩種寫法,較新的寫法是使用 BABYLON.MeshBuilder,options 中的參數使用物件形式帶入:

const mesh = BABYLON.MeshBuilder.Create<MeshType>(name, options, scene);

另一種較舊的寫法則是使用 BABYLON.Mesh ,後面的參數則依照建立的圖形不同,按照不同的順序帶入:

const mesh = BABYLON.Mesh.Create<MeshType>(name, required_param1, required_param2, ..., scene, optional_parameter1, ........);

舉例來說,我們要建立一個簡單的球形和地板:

//BABYLON.MeshBuilder
const sphere = BABYLON.MeshBuilder.CreateSphere('sphere1', { segments: 16,diameter: 1.5, }, scene);
const ground = BABYLON.MeshBuilder.CreateGround('ground1', { width: 6, height: 6 , subdivisions: 2}, scene);
ground.position.y = -1;
//BABYLON.Mesh
const sphere = BABYLON.Mesh.CreateSphere('sphere1', 16, 1.5, scene);
const ground = BABYLON.Mesh.CreateGround('ground1', 6, 6, 2, scene);
ground.position.y = -1;

目前兩種方式都可以使用,就個人經驗來說,舊的寫法雖然簡潔,但在調整參數時會需要較熟悉參數順序。

若是需要調整材質顏色的話,則使用以下程式碼:

const Material = new BABYLON.StandardMaterial("material", scene);
Material.diffuseColor = BABYLON.Color3.Random(); //球會換成隨機顏色
sphere.material = Material;

除了內建幾何圖形外,我們也可以引入自己的 3D 模型使用,引入的方式是使用 SceneLoader(❗記得前面要先引用 CDN),而在 SceneLoader 中又可細分出幾個引入的函式,這次先針對 ImportMeshAsync 說明,其他的詳細內容可參考官方說明

先載入一個 Totoro 的模型,並試著將載入後的資訊 log 出來:

//參數:model name, folder path, file name, scene
BABYLON.SceneLoader.ImportMeshAsync('', "./", "Totoro.glb", scene)
.then((_scene) => {
console.log(_scene);
});

一般來說,建議使用的模型檔案格式為 glb,若是使用 obj 或是其他格式等等,可能就比較不好抓取整個模型,因為 glb 的第一層 Mesh 都會是根目錄 root,因此我們可以藉由取得根目錄調整模型的大小或位置。

From Working with Models
//參數:model name, folder path, file name, scene
BABYLON.SceneLoader.ImportMeshAsync('', "./", "Totoro.glb", scene)
.then((_scene) => {
console.log(_scene);
const root = _scene.meshes[0]; //取得整個模型的mesh
root.normalizeToUnitCube(); //初始化大小和位置
root.position.set(0, 0, 0); //設定位置
root.scaling.scaleInPlace(1.5); //設定大小為1.5倍
});

登登!以下為示範畫面:

從 Step1 ~ Step6 一步一步寫下來,舞台、燈光、攝影機以及角色,把握這幾個大原則,就能完成簡單的 3D 場景。不過,本篇是以靜態模型為主,若是需要讓使用者與畫面互動或是撥放 3D 動畫,則要再參考官方文件中的函式庫,並且調整其中的細節參數,以及需要使用動畫模型或是骨架等等,細節過多就不寫在本篇中🙇‍♀️。

--

--