我們在[Day08] 透過Babylon.js的playground 瞭解構築3D 世界的基礎元素和常用種類有提到燈光有四種:
燈光類型 | 比喻 | Babylon.js 類別 | 主要用途 |
---|---|---|---|
半球光 | 環境光/天空光 | BABYLON.HemisphericLight |
基礎環境光,提亮場景 |
平行光 | 太陽光 | BABYLON.DirectionalLight |
模擬太陽光,產生平行陰影 |
點光源 | 燈泡 | BABYLON.PointLight |
模擬燈泡、火焰等點狀光源 |
聚光燈 | 手電筒 | BABYLON.SpotLight |
模擬手電筒、舞台燈等聚焦光源 |
一般時候最常使用半球光也就是環境光,他是計算成本非常低的光源,從一個半球形的空間發出的光,很均勻的照亮場景中的物體,同時為物體頂部跟底部(從地板反射的光),提供光照亮他的顏色。
而這次Feature的單元會是介紹另外三種光源。首先是聚光燈,聚光燈是向一個特定方向發射一個圓錐形的光束,以下是先宣告一個聚光燈並指定成黃色:
const lampLight = new BABYLON.SpotLight("name", position, direction, angle_of_spread, speed_of_disipation);
lampLight.diffuse = BABYLON.Color3.Yellow();
以下這邊範例又用一個有趣的方式來畫模型了,畫出路燈的柱子,ExtrudeShape
的原理是將一個 2D 的形狀 (shape
) 沿著一個 3D 路徑 (path
) 進行擠出,形成一個立體物件,所以這邊是畫出一個圓形(形狀) 沿著路徑畫出一根路燈:
// 形狀
const lampShape = [];
for(let i = 0; i < 20; i++) {
lampShape.push(new BABYLON.Vector3(Math.cos(i * Math.PI / 10), Math.sin(i * Math.PI / 10), 0));
}
lampShape.push(lampShape[0]); //close shape
// 路徑
const lampPath = [];
lampPath.push(new BABYLON.Vector3(0, 0, 0));
lampPath.push(new BABYLON.Vector3(0, 10, 0));
for(let i = 0; i < 20; i++) {
lampPath.push(new BABYLON.Vector3(1 + Math.cos(Math.PI - i * Math.PI / 40), 10 + Math.sin(Math.PI - i * Math.PI / 40), 0));
}
lampPath.push(new BABYLON.Vector3(3, 11, 0));
const lamp = BABYLON.MeshBuilder.ExtrudeShape("lamp", {cap: BABYLON.Mesh.CAP_END, shape: lampShape, path: lampPath, scale: 0.5});
接著才開始做燈泡,並以bulb.parent = lamp;
綁在剛剛做的燈桿上。接著同時透過lampLight.parent = lamp;
把一開始我們宣告的聚光燈綁在這個燈泡上:
const bulb = BABYLON.MeshBuilder.CreateSphere("bulb", {diameterX: 1.5, diameterZ: 0.8});
bulb.material = yellowMat;
bulb.parent = lamp;
bulb.position.x = 2;
bulb.position.y = 10.5;
lampLight.parent = bulb;
在playground上可以看到路燈的效果,如果把light.intensity = 0.5
改成0
可以更明顯的看到聚光燈的效果。
若將宣告lampLight
的那行換成底下這個就會是點光源的效果:
const lampLight = new BABYLON.PointLight("lampLight", BABYLON.Vector3.Zero(), scene);
接著範例中演示的是透過使用平行光來製造影子,由於宣告平行光的方法跟上面的類似,所以這邊著重在影子的生成。
只有定向光源 (DirectionalLight)、點光源 (PointLight) 和聚光燈 (SpotLight) 可以生成陰影。
簡易的生成影子的方式是用BABYLON.ShadowGenerator
,參數是陰影圖的解析度大小和指定哪一個光要去產生陰影,解析度越高,陰影越銳利,但效能成本也越高:
const shadowGenerator = new BABYLON.ShadowGenerator(1024, light);
shadowGenerator.addShadowCaster(myMesh); // 讓 myMesh 投射來自 light 的陰影
// 要在哪裡生成影子 這邊是讓地板生成影子
ground.receiveShadows = true;
由於陰影會是由光影計算而來 N個光源生成陰影 = 場景渲染 N次 + 額外的陰影貼圖計算。可以透過Light Baking優化效能,這能在開發階段就將陰影預先算好。缺點是這只能適用於在環境中靜態的物體,不包含可以玩家或可移動的物體,並且因為事先計算所以會花比較久的時間,檔案也會大很多,這也是需要權衡的地方。在XR中用到陰影的地方可能相對較少,也許大多就是因為效能的考量。
我們完成了基礎的3D世界教學了!Feature中的最後一個單元是關於攝影機的。
然而XR中的攝影機幾乎都是根據玩家就是攝影機本身,幾乎都會使用XR的設置,所以接下們來看要怎麼進入XR 世界啦!