iT邦幫忙

2025 iThome 鐵人賽

DAY 11
0
Modern Web

WebXR未來新視界:Babylon.js打造Web的VR/AR/XR體驗系列 第 11

[Day11] 用Babylon.js基礎模型組裝模型時常用的操作(Feature - 單元二)

  • 分享至 

  • xImage
  •  

上篇有提到模型來源有兩種:引入外部模型用基本模型去組裝。主要會用引入外部模型的方式。那基礎模型的通常什麼時候用呢?在進行開發測試階段的時候,會先專注於核心功能和確認場景,這時候就可以透過最基礎模型的簡單幾何體,來確認程式跑的對不對;或是確認這樣的互動是符合需求的。以及debug的時候,放置小球體去觀察物體或光源。在以上這些情境下,會用基礎模型來提高開發效率,像是會用圓柱體去代表人物,方形代表建築等。接下來我們透果簡單的練習來熟悉組裝基礎模型,和快速帶過常見操作基礎模型的功能。

Feature單元二 Build A Village:利用多個基本形狀組合出一個場景

單元二的範例一的程式碼如下,這個範例可以回到playground來看:

https://playground.babylonjs.com/#KBS9I5#66

在這個範例中,主要是要示範了一般在一個剛建立的3D世界是一個無垠的宇宙,基於我們還是習慣腳踏實地,很多時候會設定地平線的需要,這時候通常會這樣宣告:

const ground = BABYLON.MeshBuilder.CreateGround("ground", {width:10, height:10});

接著放置Mesh(網格)會有多種的方式達成,而在以下範例中來介紹:

https://playground.babylonjs.com/#KBS9I5#68

在這個範例中,畫面上看到三個長得一樣的長方體,但細看程式碼會發現他們是分別用三種不同的實作方式:

box1是最常見的方法,在呼叫的時候直接給長寬高。一個很明確大小的長方體。

const box1 = BABYLON.MeshBuilder.CreateBox("box1", {width: 2, height: 1.5, depth: 3});

box2是建立一個預設1x1x1的長方體,之後再給長寬高。因為長寬高是後給的,所以可以比較好做動態變化。

const box2 = BABYLON.MeshBuilder.CreateBox("box2", {});
box2.scaling.x = 2;
box2.scaling.y = 1.5;
box2.scaling.z = 3;

box3是建立一個預設1x1x1的長方體,給他一組向量並一次給三個象限的拉伸長度。

const box3 = BABYLON.MeshBuilder.CreateBox("box3", {});
box3.scaling = new BABYLON.Vector3(2, 1.5, 3);

位置也有多種寫法:

// 方法1
box.position.x = -2;
box.position.y = 4.2;
box.position.z = 0.1;
// 方法2
box.position = new BABYLON.Vector3(-2, 4.2, 0.1);

旋轉也有多種寫法:

// 方法1 用Math.PI (180度)的百分比來看轉多少, 
box.rotation.y = Math.PI / 4;
// 方法2 直接設定角度
box.rotation.y = BABYLON.Tools.ToRadians(45);

在建立並放置好Mesh之後,通常會加上模型的材質(Material)。

所謂材質包含色彩、紋理、光澤度等,添加顏色(Color)會用Color3表示rgb,或用Color4表示rgba:

const groundMat = new BABYLON.StandardMaterial("groundMat");
groundMat.diffuseColor = new BABYLON.Color3(0, 1, 0);
ground.material = groundMat; // 由於要把顏色指定給ground, 所以這句要寫在宣告ground的後面

添加紋理(Texture),指的是加上圖樣:

const roofMat = new BABYLON.StandardMaterial("roofMat");
roofMat.diffuseTexture = new BABYLON.Texture("https://assets.babylonjs.com/environments/roof.jpg", scene);
const boxMat = new BABYLON.StandardMaterial("boxMat");
boxMat.diffuseTexture = new BABYLON.Texture("https://www.babylonjs-playground.com/textures/floor.png");

// 然後一樣, 要指定給宣告過的roof和box
roof.material = roofMat;
box.material = boxMat;

從playground範例可看這段加上材質跟顏色後的結果:

https://playground.babylonjs.com/#KBS9I5#71

除了每面都一樣的texture外,也可以透過指定位置來將一個立體面擁有不同的位置,以下方有窗戶和門的圖片為例:

// 將上個步驟的 boxMat.diffuseTexture = new BABYLON.Texture("https://www.babylonjs-playground.com/textures/floor.png");
// 改成以下, 改變傳進去的圖檔
boxMat.diffuseTexture = new BABYLON.Texture("https://assets.babylonjs.com/environments/cubehouse.png")

他是這樣的一個圖片

https://ithelp.ithome.com.tw/upload/images/20250921/20103990o6u2m9LTHf.png

想要讓他套用在房子的正方體上的四個面上:

    //options parameter to set different images on each side
    const faceUV = [];
    faceUV[0] = new BABYLON.Vector4(0.5, 0.0, 0.75, 1.0); //rear face
    faceUV[1] = new BABYLON.Vector4(0.0, 0.0, 0.25, 1.0); //front face
    faceUV[2] = new BABYLON.Vector4(0.25, 0, 0.5, 1.0); //right side
    faceUV[3] = new BABYLON.Vector4(0.75, 0, 1.0, 1.0); //left side

FaceColors和FaceUV用在多面體,所以球體就沒有這兩個選項。FaceColors可以設定每個面的顏色,FaceUV可以設定用同一張圖的不同部分去呈現在不同面上。

BABYLON.Vector4會用四個參數(x, y, z, w),x, y 會組成左下角的座標;z, w 會組成右上角的座標。 而用來標示座標的數字會是整張圖的比例位置,以0到1之間的數字表示。
所以以上面的第0, 第1, 第2, 第3的x,y座標以紅色標記;z, w座標以黃色標記如下位置:

https://ithelp.ithome.com.tw/upload/images/20250921/201039905jYpdCO9O7.png

所以用上圖的房子最後在playground會是這樣:
https://playground.babylonjs.com/#KBS9I5#72

關於模型的坑很深,在這邊的教學只有簡單帶過,關於不同材質會有不同的光線互動等,有很多地方可以探究和調適,詳細的文件我先列在下方。

詳細的**Face Materials文件:**https://doc.babylonjs.com/features/introductionToFeatures/chap2/face_material

https://doc.babylonjs.com/features/featuresDeepDive/materials/advanced/facet_uvs/

詳細的Materials文件:

https://doc.babylonjs.com/features/featuresDeepDive/materials/using/materials_introduction

組合模型

要組合兩個模型的語法很簡單,方法1 一句就把兩個組合在一起了,但卻發現剛剛貼的窗戶和屋頂變成只有窗戶蓋滿整個模型。這時候來看看用方法2 帶有參數的方法來做,不僅修正了應該要不同材質的部分,也能發現第二種寫法更嚴謹、效能更好,會是較佳的實踐方式:

// 方法1: 結合[]裡面的所有模型
const house = BABYLON.Mesh.MergeMeshes([box, roof])

/**
 * 方法2: 結合[]裡面的所有模型並帶以下參數
 * true (disposeSource 預設false)-- 合併完成後是否銷毀原始網格物件。建議true可優化效能。
 * false (allow32BitsIndices 預設false)-- 不用32位元時會用16位元,最多網格頂點數量不能超過2^16 (65,536),大多情況已經夠用。
 * null (meshSubclass 預設null)- 進階功能,大多情況用null。
 * false (subdivideWithSubMeshes 預設false)-- 合併後依據不同材質在內部創建子網格
 * true (multiMultiMaterials 預設false) -- 要多材質模型和多材質模型的合併使用
 **/
const house = BABYLON.Mesh.MergeMeshes([box, roof], true, false, null, false, true);

詳細的Merging Meshes文件:
https://doc.babylonjs.com/features/featuresDeepDive/mesh/mergeMeshes

複製模型

在這個單元中介紹了兩種常見複製模型的方式:

// clone 是產生一個獨立的Mesh所以幾何數據是獨立可以個別被修改,但材質不能改變仍然與原本的網格關聯。
clonedHouse = house.clone("clonedHouse")

// createInstance 是建一個輕量級的實例,與原始物件共享幾何數據。速度極快且非常省記憶體,適合大量相同物件時用。
instanceHouse = house.createInstance("instanceHouse")

https://playground.babylonjs.com/#KBS9I5#78

關注在以下關於複製這段,由於前20行已經宣告好兩個房子的樣子(detached_house和semi_house),這邊在複製房子之前設定好位置和角度,並在跑迴圈時帶上去,就能得到一個村莊。

https://ithelp.ithome.com.tw/upload/images/20250921/20103990rfxyRzi1tf.png


上一篇
[Day10] 認識模型 — 常用的模型格式類型和如何引入外部模型
下一篇
[Day12] 讓世界動起來:Babylon.js 的基本動畫,讓車子動起來(Feature - 單元三)
系列文
WebXR未來新視界:Babylon.js打造Web的VR/AR/XR體驗12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言