Q:Babylon.js是什么?🤔️
Babylon.js 是一个强大的、开源的、基于 WebGL和 WebGPU的 3D引擎,用于在网页上创建和渲染 3D图形。它提供了一套丰富的 API和功能,包括物理引擎、粒子系统、骨骼动画、碰撞检测、光照和阴影等,可以帮助开发者快速创建复杂的 3D场景和交互。
Q:我为什么要写该系列的教材? 🤔️
因为公司业务的需要因而要在项目中使用到 Babylon.js,虽然官方的文档看起来覆盖面都挺全,且 playgroud 上的案例也都比较多,但一些具体的 API 或者功能属性也都没有特别多详细的介绍,包括很多使用方式的很多坑都得自己去源码中或者论坛上找。在将其琢磨完之后, 决定写一系列关于它的教材来帮助更多 babylon.js的使用者或者是期于学习 Web 3D的开发者。同时也是自己对其的一种巩固。
Babylon.js中的雾、重力和物理引擎
在上一章节我们深入理解了天空盒,它解决的是如何让环境在视觉上更趋近于真实世界,除此之外,真实世界中还有很多其它的现象和特性,如雾、重力、弹力、物体与物体碰撞后的效果等等。这一章节就让我们来看看如何用 Babylon.js 将它们给表现出来。
clearColor 背景颜色
在介绍那些有意思的特性之前,先来看一个场景中简单的属性,clearColor。
类型:BABYLON.Color4
它是用来改变场景的背景色,也就是我们在创建一个 Scene时候经常看到默认的那个紫色背景(默认值为 new Color4(0.2, 0.2, 0.3, 1.0))。
要修改这个背景色用 clearColor即可:
var scene = new BABYLON.Scene(engine);
scene.clearColor = new BABYLON.Color4(1, 1, 0, 1); // 设置为黄色
效果如下:
在线预览地址:playground.babylonjs.com/#UU7RQ#4174
fogMode 场景雾
在实际开发中,你可能还需要雾的效果,用来模拟阴天,雨天,或者一些阴暗潮湿的环境,这时候可以试试场景雾模式。(这一块官网上说得其实还行,我再补充一些属性的用法。官网:doc.babylonjs.com/features/fe…)
fogMode
要产生雾气,首先要先设置 scene的 fogMode属性。它是一个枚举值,有以下几种:
BABYLON.Scene.FOGMODE_EXP- 雾密度遵循指数函数。BABYLON.Scene.FOGMODE_EXP2- 与上面相同但更快。BABYLON.Scene.FOGMODE_LINEAR- 雾密度遵循线性函数。BABYLON.Scene.FOGMODE_NONE- 默认值,雾被禁用。
fogDensity 密度
设置不同的值有不同的显示效果。其中如何设置为 Scene.FOGMODE_EXP和 Scene.FOGMODE_EXP2的话,还可以设置密度 fogDensity:
scene.fogDensity = 0.2;
fogDensity的默认值为 0.1,你可以根据实际需求调节。下面来看一组对比:
在线查看地址:playground.babylonjs.com/#7G0IQW#333
fogStart/fogEnd
另外如果你设置雾模式为 Scene.FOGMODE_LINEAR线性函数模式的话,还可以调整雾的开始和结束位置。
使用:
scene.fogStart = 20.0;
scene.fogEnd = 60.0;
案例对比:
在线查看地址:playground.babylonjs.com/#7G0IQW#334
fogColor 雾颜色
最后,所有的雾模式你都可以调整雾的颜色 fogColor,官方默认的颜色为:new BABYLON.Color3(0.2, 0.2, 0.3)。如果你调整为血红色(new BABYLON.Color3(0.3, 0, 0)),对比效果为:
在线案例地址:playground.babylonjs.com/#7G0IQW#335
gravity 场景重力
说到构建真实的世界,怎么能少了重力呢?在 Babylon.js 中要拥有重力的效果非常简单,只需要这么几步:
- 在场景中引入并启用物理引擎
- 给要拥有重力效果的
mesh构造一个物理体
在此,我们简单的举一个小例子以展示怎么使用重力,要实现的效果如下所示,小球掉落下来碰到地板能进行回弹:
物理引擎
在实现重力功能前,我们先来了解一下物理引擎是个什么概念,因为要有重力的效果还得依赖它。
首先,物理引擎也是一种软件库或框架,用于模拟现实世界中的物理现象,如重力、碰撞、摩擦、弹性等。它通过数学模型和算法来实现这些模拟,使得虚拟环境中的物体行为更加真实。
主要应用场景:就像是刚体动力学(模拟刚体,如盒子、球体)的运动和碰撞;软体动力学(模拟软体,如布料、果冻)的变形和运动;流体动力学(模拟液体和气体的流动);还有就是碰撞检测等功能。
所以可以看到,3D渲染引擎它更侧重的是创建、渲染和管理三维图形场景,而物理引擎主要是负责模拟现实世界中的物理现象。
那有人可能会问了,“模拟现实世界中的物理现象”本身也是属于构建三维世界中的一部分呀,那为什么3D引擎中没有包含这一部分,而是要额外引入呢?
这也是有几个原因的,首先,咱看看物理引擎做的都是啥事?模拟刚体,模拟液体/气体,做碰撞检测,这些功能哪个不是要使用复杂的数学模型和算法,所以从专业化的角度来说,专业的事就交给专业的引擎去做,保证每个引擎都能在其领域做到最好,你物理引擎就专心搞物理相关的,我3D引擎就搞好渲染和管理。
其次,从模块化的角度来说,也是无形之中提高了渲染引擎的灵活性和可维护性,物理这一块被单独抽离出来了,我可以随时更换不同的物理引擎,不同项目可能有不同需求,可以让开发者选择最适合当前需求的工具。
再还有就是性能优化和资源管理上,3D引擎和物理引擎各自管理不同类型的数据(如顶点缓冲区 vs 刚体数据),分开处理有助于更高效地管理这些资源。
物理引擎的种类
目前,有很多物理引擎可以供开发者使用,有的免费开源的,有的是商业化的。不同物理引擎之间还是有一些区别的,主要体现在:功能特性、性能优化、平台支持、易用性、集成度等上面。
例如,Bullet Physics就是一个开源的引擎库,它支持刚体动力学、软体模拟等,但是不专注于流体模拟,可以通过扩展实现基本效果。同时也支持 Windows、Linux、MacOS等多平台。
而PhysX是由 NVIDIA开发和维护的,提供高精度的刚体/软体模拟,适合复杂场景。因为它专为 GPU 加速优化,特别是在 NVIDIA显卡上表现出色。像一些高端游戏、电影特效等用的比较多。
还有就是 Havok Physics,它虽然也是商业化产品,但提供了免费的版本。功能也比较丰富,刚体、软体、流体都支持。一些 3A游戏中经常会使用到。
当然对于前端开发来说,可能还听过 cannon.js、ammo.js,这些也是功能比较全面,轻量化的引擎库。
总结来说,不同物理引擎在功能特性、性能优化、平台支持、易用性及社区文档方面均存在显著差异 ,看你实际的开发需求咯。
Babylon.js 中引入物理引擎
在 Babylon.js 中使用物理引擎,是通过插件的方式。而关于使用物理引擎这块 Babylon.js 中是有两种版本的:V1和V2。比较新的就是 V2了,官网也推荐用它。至于更换的原因有一篇文章里细说了,感兴趣的可以移步:babylonjs.medium.com/physics-v2-…。总结来说就是新版性能更好了,速度大概提升了 10 到 20 倍,也解决了很多历史遗留问题。
在 V2中,提供了一个 Havok的物理引擎插件,开发者要使用的话,需要额外引入进来。
1、cdn 远程加载
<script src="https://cdn.babylonjs.com/havok/HavokPhysics_umd.js"></script>
2、npm 包
npm i @babylonjs/havok
// or
pnpm i @babylonjs/havok
npm 包引入:
import HavokPhysics from '@babylonjs/havok';
然后在初始化场景的时候,启用物理引擎:
var scene = new BABYLON.Scene(engine);
// 定义重力向量
var gravityVector = new BABYLON.Vector3(0, -9.81, 0);
// 初始化 havok 插件
var physicsPlugin = new BABYLON.HavokPlugin();
// 启动物理引擎
scene.enablePhysics(gravityVector, physicsPlugin);
可以看到,scene.enablePhysics方法有两个参数:
gavityVector,可选,类型是Vector3,表示场景中的重力向量,通常设置为(0, -9.81, 0)以模拟地球上的重力,默认值也是这么多。plugin,第二个参数就是物理引擎插件的实例了,也是可选的。
OK,完成以上步骤之后,我们就完成了第一步:在场景中引入并启用物理引擎。
下面,我们再来定义一个地板,还有一个悬浮在上面的小球:
var sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter: 2, segments: 32}, scene);
// 让小球在地板上面,且离地板还有一些距离
sphere.position.y = 4;
var ground = BABYLON.MeshBuilder.CreateGround("ground", {width: 10, height: 10}, scene);
效果:
可以看到,这会其实是没什么重力的效果的,小球依旧在指定的位置上,并不会像真实世界中一样掉下来。
这时候,要想实现我们要的效果,还需要额外指定哪些东西具有物理特性,指定的方式就是使用 BABYLON.PhysicsAggregate(),同时还可以指定 mass(质量)、restitution(回弹系数/碰撞系数),如下代码所示,我们指定小球和地板:
var sphereAggregate = new BABYLON.PhysicsAggregate(
sphere,
BABYLON.PhysicsShapeType.SPHERE,
{ mass: 1, restitution:0.75},
scene
);
var groundAggregate = new BABYLON.PhysicsAggregate(
ground,
BABYLON.PhysicsShapeType.BOX,
{ mass: 0 },
scene
);
现在,就是我们想要的效果了:
在线预览地址:playground.babylonjs.com/#Z8HTUN#1
至于上面的代码,看不懂没关系,我们接下来来详细讲解下。
PhysicsAggregate
PhysicsAggregat从命名上看,是用来创建一个带有物理特性的聚集体,实际上,它就是 Babylon.js 中用于简化物理对象创建和管理的一个类。它将物理引擎的复杂配置封装在一个简单的接口中,使得开发者可以更方便地为场景中的对象添加物理属性。
从现实世界的角度来看,物理特性就包括:物体的质量、摩擦力、弹性系数、还有这个物体的形状等等。而这些都是要通过 PhysicsAggregat来定义的,一起来看下它的语法:
new BABYLON.PhysicsAggregate(mesh, type, options, scene, plugin);
参数:
mesh:需要应用物理属性的网格对象type:类型是一个number的枚举值,表示物体类型,枚举值有:
-
BABYLON.PhysicsShapeType.BOX: 盒子形状BABYLON.PhysicsShapeType.SPHERE: 球体形状BABYLON.PhysicsShapeType.CAPSULE: 胶囊形状BABYLON.PhysicsShapeType.CONE: 圆锥形状BABYLON.PhysicsShapeType.CYLINDER: 圆柱形状BABYLON.PhysicsShapeType.MESH: 网格形状(适用于复杂几何体)BABYLON.PhysicsShapeType.CONVEX_HULL: 凸包形状
options:包含物理属性配置的选项对象。常见选项包括:
-
mass(number): 质量,默认为0(静态物体)。friction(number): 摩擦系数。restitution(number): 弹性系数。impostorSize(Vector3): 用于定义盒子、球体等基本形状的大小。
最后两个参数 scene和 plugin就不说了,都是可选项,不填写的话就使用默认的。
稍微要注意的就是第二个参数,物体类型。因为涉及到碰撞检测等等,所以 Babylon.js肯定是得知道物体的几何形状,稍微基础一些的形状,用 BOX、SPHERE等也许就可以搞定了,但对于一些复杂的几何形状或者复杂的模型,就得用到 PhysicsShapeType.MESH和 PhysicsShapeType.CONVEX_HULL了。
PhysicsShapeType.MESH 和 PhysicsShapeType.CONVEX_HULL
这两个是两种不同的物理形状类型,不同点主要表现是在应用场景和性能上。
PhysicsShapeType.MESH,我们称之为网格形状,它会使用模型的实际几何形状进行碰撞检测。“实际几何形状”也就意味着它得很精确,每个顶点和面都要参与计算。这也就是导致了它的应用场景特别适合需要高精度碰撞检测,但不需要频繁移动或旋转的静态对象。比如地形、建筑物等。
另一个 PhysicsShapeType.CONVEX_HULL,我们称之为凸包形状,它是使用所有顶点的最小凸面体进行碰撞检测,是一个近视模型,计算效率很高,性能更好,精度虽然对比 PhysicsShapeType.MESH来说不高,但通常来说是可以满足大多数应用需求的。所以它比较适合哪些需要频繁移动或旋转,又对性能有要求的动态对象,例如角色、车辆等等。
结合它俩各自的特性上,在游戏开发中,大多数情况下会选择 CONVEX_HULL 来平衡性能和精度。在一些特殊应用中,如科学仿真,需要极高精度时才会选择 MESH。
PhysicsViewer 查看碰撞检测时的形状
在实际开发时候,我们可能需要把物体开启物理特性时的形状给显示出来,这样好方便我们做一些物理碰撞检测的调试。
有点类似于我们在第二章节插入几何体的时候,查看几何体的网格一样。我们知道,查看网格可以通过inspector把场景设置为 Wireframe 模式来查看:
而碰撞检测时的形状就并不一定是网格的形状了。
例如,有一个物体明明是 Box,但我可以给它设置成另一个球的形状:
// 创建一个球
var mesh = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter: 2, segments: 32}, scene);
// 创建一个立方体
var mesh2 = BABYLON.MeshBuilder.CreateBox("mesh2", {width: 2, height: 2, depth: 2});
// 给球设置物理特性
new BABYLON.PhysicsAggregate(
mesh,
BABYLON.PhysicsShapeType.SPHERE,
{ mass: 1 },
scene
);
// 立方体创建物理特性时,复用球的形状
new BABYLON.PhysicsAggregate(
mesh2,
mesh.physicsBody.shape,
{ mass: 1 },
scene
);
有了以上代码还看不出啥,这时候我们可以利用 BABYLON.PhysicsViewer来查看一下各自的形状是什么:
var viewer = new BABYLON.PhysicsViewer();
scene.meshes.forEach((mesh) => {
if (mesh.physicsBody) {
viewer.showBody(mesh.physicsBody);
}
});
效果如下:
在线查看地址:playground.babylonjs.com/#4Y813L#1
上图中白色的网格就是物体在进行碰撞检测时的网格了。可以看到左边的球,形状和球基本是吻合的,但是右边的立方体由于复用了球的形状,所以它最终在进行碰撞检测的时候会以球的形状去做。
OK,使用 PhysicsViewer可以让我们的开发和调试更加方便,非常棒。
后语
知识无价,支持原创!这篇文章主要向大家介绍如何在 Babylon.js 中使用雾模式,实现重力的效果,还说了物理引擎是什么,以及怎么使用。
喜欢霖呆呆的小伙伴还希望可以关注霖呆呆的公众号 LinDaiDai
我会不定时的更新一些前端方面的知识内容以及自己的原创文章🎉。
你的鼓励就是我持续创作的主要动力 😊。
其它相关文章推荐: