Q:Babylon.js是什么?🤔️
Babylon.js 是一个强大的、开源的、基于 WebGL
和 WebGPU
的 3D
引擎,用于在网页上创建和渲染 3D
图形。它提供了一套丰富的 API
和功能,包括物理引擎、粒子系统、骨骼动画、碰撞检测、光照和阴影等,可以帮助开发者快速创建复杂的 3D
场景和交互。
Q:我为什么要写该系列的教材? 🤔️
因为公司业务的需要因而要在项目中使用到 Babylon.js
,虽然官方的文档看起来覆盖面都挺全,且 playgroud 上的案例也都比较多,但一些具体的 API 或者功能属性也都没有特别多详细的介绍,包括很多使用方式的很多坑都得自己去源码中或者论坛上找。在将其琢磨完之后, 决定写一系列关于它的教材来帮助更多 babylon.js
的使用者或者是期于学习 Web 3D
的开发者。同时也是自己对其的一种巩固。
材质与光的交响曲
在 3D 领域中,材质是一个非常重要的概念,它定义了物体表面的外观,决定了物体如何反射或吸收光线,从而影响物体的颜色、光泽度、透明度等视觉效果。
以下这些效果都是通过 Babylon.js 的材质功能实现的:
面片上播放视频:
网格地板:
地形、山谷:
水波纹:
对于材质,能说的部分还是挺多的:贴图、HDR、KTX2等等等等,但在前期,有一个必须要掌握的知识点就是 BABYLON.StandardMaterial
(标准材质),可以说,它是我们后面做开发时用的最多的一个材质类。
而另一个与材质打交道较多的就属【光】了。
光,当然不是迪迦的这个光,而是指太阳光、灯光、聚光灯等(Babylon.js 中具体有哪些类型的光源会在下一章节来详细讲解)。
在 Babylon.js 中,标准材质对光的反应会有四种,也是 3D 领域中经常听到的:
漫反射
高光反射
自发光
环境光
这一章节,我们将会详细了解标准材质(StandardMaterial)与光的“爱恨情仇”,看看它们碰撞在一起时能奏起一篇怎样的交响曲。通过这一章节的学习,你将了解到如何给 Mesh 添加类似于下面这种的效果:
(有颜色、有高光、有阴影效果)
可以说,有了材质,我们的 Mesh
才算是穿上了衣服,起码能够“出门见人”~
BABYLON.StandardMaterial
StandardMaterial
本身的创建和赋值并不复杂, 直接初始化一个实例,然后赋值给需要的 Mesh
即可:
// 第一个参数为材质名称, 第二个参数为场景
var redMat = new BABYLON.StandardMaterial("redMat", scene);
// 赋值到 Mesh 的 material 属性上, 表示这个 Mesh 所用的材质是 redMat
sphere.material = redMat;
注:材质也可以通过 inspector
查看到,创建时候的 name
属性就可以方便你在 inpector
中查看到:
在 Babylon.js 7.0 版本中,还新增了一个可选参数 forceGLSL, 默认值为 false,表示使用 GLSL 代码生成着色器(即使在WebGPU上),咱暂时还用不上这个,仅作为了解。
但这玩意复杂就复杂在它的使用上,有非常多的属性,这也是它能实现这么多效果的原因。
例如,在开篇我们谈到了它与光的四种反应:
漫反射
高光反射
自发光
环境光
在StandardMaterial
中就有四个字段,用于定义处理这四种反应该显示成什么颜色,它们分别是:
const myMaterial = new BABYLON.StandardMaterial("myMaterial", scene);
myMaterial.diffuseColor = new BABYLON.Color3(1, 0, 1); // 漫反射颜色
myMaterial.specularColor = new BABYLON.Color3(0.5, 0.6, 0.87); // 高光颜色
myMaterial.emissiveColor = new BABYLON.Color3(1, 1, 1); // 自发光颜色
myMaterial.ambientColor = new BABYLON.Color3(0.23, 0.98, 0.53); // 环境光颜色
除此之外,还有透明度、贴图等属性,在后面的章节中我们会一一介绍。
BABYLON.Color3
在正式开讲前,我认为还有必要简单的介绍一下 BABYLON.Color3
这个类,因为也被提到过很多次了,怕大家不明白它的作用和值的含义。
在 Babylon.js 中,其中一种表示颜色的方式是使用Color3
。它使用三个浮点数参数来表示颜色的红色、绿色和蓝色分量。每个参数的范围是从 0 到 1。
例如:new BABYLON.Color3(1, 0, 0)
,由于只有第一个通道(红色)为 1
,剩下的都为 0
,所以这个值表示的是红色。
类似的还有 BABYLON.Color4
,比 Color3
多了一个透明通道,取值也是 0 到 1。
(作为一名 CSS
的高端玩家,你一定很奇怪,平常我们用的比较多的是 0~255 的范围,为啥到这里就是 0~ 1 了呢?这肯定也是有它的原因的,咱后面再来说)
自发光 emissive
先来看一个我认为最简单的自发光属性(emissive
),它的主要作用是:使材质看起来像是自己发光。
与自发光(emissive
)相关的主要属性是:
emissiveColor
:设置自发光颜色emissiveTexture
:设置自发光纹理
emissiveColor 自发光颜色
类型: BABYLON.Color3
描述: 设置材质的自发光颜色,使物体看起来像是自己发光。
material.emissiveColor = new BABYLON.Color3(1, 0, 0); // 红色自发光
效果展示:
如上所示,为了与其他光做对比,左边的球是受光照影响的,右边禁用了光照的影响,仅自发光。
我们来看一下完整的代码:
var scene = new BABYLON.Scene(engine);
var camera = new BABYLON.ArcRotateCamera("Camera", -Math.PI / 2, Math.PI / 4, 5, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvas, true);
var light = new BABYLON.HemisphericLight("hemiLight", new BABYLON.Vector3(-1, 1, 0), scene);
light.diffuse = new BABYLON.Color3(1, 0, 0);
// 创建一个材质, 并让这个材质自发光为红色
var redMat = new BABYLON.StandardMaterial("redMat", scene);
redMat.emissiveColor = new BABYLON.Color3(1, 0, 0);
// 让材质禁用光照, 这样它就可以不受 light 的影响了
redMat.disableLighting = true;
var sphere0 = BABYLON.MeshBuilder.CreateSphere("sphere0", {}, scene);
sphere0.position.x = -1.5;
var sphere1 = BABYLON.MeshBuilder.CreateSphere("sphere1", {}, scene);
sphere1.material = redMat;
或者在线预览:https://playground.babylonjs.com/#20OAV9#14589
emissiveColor 的特点
通过自发光颜色的那个案例对比,我们可以看到,自发光有一个很明显的缺点,那就是“不真实的光照”。
自发光材质不会实际照亮周围的物体,只是看起来像在发光,且它的效果是静态的,不能像实际光源那样动态变化,这也意味着光照不会对它造成影响。
当然光照对它不造成影响是缺点也是优点:
1、视觉效果突出:自发光材质可以使物体在黑暗环境中仍然可见,增加视觉效果。
2、性能较好:相比于使用实际的光源,自发光材质对性能的影响较小,因为它不需要计算光照。
主要的使用场景为:
科幻效果:在科幻场景中,常常需要一些发光的物体,比如发光的按钮、屏幕或能量源。
灯光效果:模拟灯光或霓虹灯效果。
装饰性元素:用于装饰性元素,使其在黑暗环境中仍然可见。
漫反射 diffuse
在现实生活中,【漫反射】是光线在物体表面均匀散射的效果,决定了物体在光照下的主要颜色和外观。
在Babylon.js中,标准材质(StandardMaterial)的漫反射(Diffuse)属性用于控制物体表面在光照下的基本颜色和纹理。与其相关的主要属性是:
diffuseColor
:漫反射颜色,决定物体在光照下的基本颜色diffuseTexture
:设置材质的漫反射纹理,决定物体表面的基本纹理
diffuseColor 漫反射颜色
类型: BABYLON.Color3
描述: 设置材质的漫反射颜色,决定物体在光照下的基本颜色。
使用场景: 用于简单的颜色填充,不需要复杂的纹理。
使用及赋值案例:
const material = new BABYLON.StandardMaterial("myMaterial", scene);
material.diffuseColor = new BABYLON.Color3(1, 0, 0); // 红色漫反射
案例一: 灯光设置diffuse为白色,材质设置diffuseColor为黄色
通过上面的案例我们很难看出漫反射有什么实际的效果,让我们来看看下面这个更为代表性的案例。
首先,让我们来创建一个地板(在Babylon.js中使用 MeshBuilder.CreateGround
来创建一个地板),并且创建一个标准材质,漫反射设置为黄色,并把材质设置给地板:
const ground = BABYLON.MeshBuilder.CreateGround("ground", {width: 4, height: 6}, scene);
const groundMat = new BABYLON.StandardMaterial("groundMat", scene);
groundMat.diffuseColor = new BABYLON.Color3(1, 1, 0); // 黄色
ground.material = groundMat;
有了以上代码之后,如果场景中没有任何光源的话,我们看到的应该是下面这样的(一个黑色的地板):
设置了漫反射颜色为黄色,但是看到的地板却是黑色的?这其实也很好理解,在场景中没有任何光源的情况下,我们又没有设置地板材质的自发光属性,那么看到的物体肯定就是黑色的了。就像是在现实生活中,你长得再好看,关上灯那还不是啥也看不见?(咳咳,涉H了哈,收~)
接着让我们来增加一个白色的平行光(在Babylon.js中使用 BABYLON.DirectionalLight
来创建一个平行光):
const lightWhite = new BABYLON.DirectionalLight("DirectionalLight", new BABYLON.Vector3(0, -1, 0), scene);
lightWhite.diffuse = new BABYLON.Color3(1, 1, 1);
平行光的属性也许你还没有了解过,这边简单的做一下解释:
使用
DirectionalLight
创建了一个平行光设置平行光的方向为
new Vector3(0, -1, 0)
,即垂直地板向下设置平行光的
diffuse
属性为白色,决定光源发出的光的颜色
此时平行光与地板的关系类似于下面这张图:
实际的效果为:
可以看到,如果地板材质的漫反射设置为黄色,平行光漫反射颜色设置为白色,地板呈现的就是黄色。
完整代码为:
var scene = new BABYLON.Scene(engine);
var camera = new BABYLON.ArcRotateCamera("Camera", -Math.PI, 0.8799, 10, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvas, true);
//Light direction is directly down
const lightWhite = new BABYLON.DirectionalLight("DirectionalLight", new BABYLON.Vector3(0, -1, 0), scene);
lightWhite.diffuse = new BABYLON.Color3(1, 1, 1);
const material = new BABYLON.StandardMaterial("myMaterial", scene);
material.diffuseColor = new BABYLON.Color3(1, 0, 0); // 红色漫反射
const ground = BABYLON.MeshBuilder.CreateGround("ground", {width: 4, height: 6}, scene);
const groundMat = new BABYLON.StandardMaterial("groundMat", scene);
groundMat.diffuseColor = new BABYLON.Color3(1, 1, 0); // 黄色
ground.material = groundMat;
或者在线预览:https://playground.babylonjs.com/#20OAV9#14590
案例二: 灯光设置diffuse为蓝色,材质设置diffuseColor为黄色
那么如果我们将平行光漫反射设置为蓝色,最终会是什么颜色呢?
黄色 + 蓝色 = 绿色?
No, No, No, 它竟然会是黑色:
这个现象好像和美术老师从小教我们的不太一样啊,是吧?
在现实生活中,光照的颜色和材质的颜色在物理上是通过光的反射和吸收来相互作用的。具体来说,光源的颜色表示光的能量,而材质的颜色表示材质反射光的能力。而在计算机图形学中,光照模型通常使用乘法运算来计算光源和材质的颜色。
当光源的颜色和材质的颜色进行乘法运算时,实际上是在计算每个颜色通道(红、绿、蓝)上的光能量和反射能力的乘积。如果光源的颜色和材质的颜色在某个通道上没有重叠的部分(即一个通道的值为0),那么在该通道上的结果就是0。
例如:
光源颜色为蓝色 (0, 0, 1),材质颜色为黄色 (1, 1, 0)。
计算每个通道的乘积:
红色通道:0 * 1 = 0
绿色通道:0 * 1 = 0
蓝色通道:1 * 0 = 0
最终的结果是 (0, 0, 0),即黑色。
其实这种乘法运算也比较好理解啦,它无非就是反映了物理现实中的光照和反射过程:如果光源在某个颜色通道上没有能量(值为0),或者材质在某个颜色通道上不反射光(值为0),那么在该通道上就不会有反射光的贡献,结果就是黑色。
整个过程可以表示为:
同理,那么光源漫反射颜色为白色(1,1,1)时为什么地板最终显示的是黄色,也就不难理解了吧。
案例三: 灯光设置diffuse为蓝色,材质不设置diffuseColor
那有同学是否就会有疑问?上面的案例展示的是物体材质设置了 diffuseColor 属性(黄色),某个照射材质的灯光也设置了 diffuse 属性(蓝色),那如果不给这个物体设置材质,或者这个材质不指定 diffuseColor 属性的话,会怎样呢?
代码为:
// 创建了一个地板,但是不设置材质
const ground = BABYLON.MeshBuilder.CreateGround("ground", {width: 4, height: 6}, scene);
// 创建了一个平行光,并设置平行光的漫反射为蓝色
const lightWhite = new BABYLON.DirectionalLight("DirectionalLight", new BABYLON.Vector3(0, -1, 0), scene);
lightWhite.diffuse = new BABYLON.Color3(0, 0, 1);
我直接揭晓答案,最终我们看到的会是蓝色:
最终看到的是蓝色,也就是说看到的是灯光的颜色。
那是因为如果没有设置 diffuseColor
,标准材质的默认 diffuseColor
是白色(BABYLON.Color3(1, 1, 1)
),白色会完全反射光源的颜色,因此,物体会反射平行光的蓝色。
diffuseColor的特点
材质上的漫反射颜色使用起来相对来说还是比较简单的,所以当你只需要为物体设置一个简单的颜色时,可以使用diffuseColor
,但别忘了要结合灯光的 diffuse
属性才能发挥作用。
优点:
易于使用: 设置颜色和纹理非常简单,适合快速原型设计。
高效渲染: 漫反射计算相对简单,对性能影响较小。
广泛应用: 适用于大多数常见的材质效果,如木材、金属、布料等。
缺点:
缺乏细节: 仅使用漫反射可能无法表现出复杂的光照效果,如高光、反射等。
依赖光源: 漫反射效果依赖于场景中的光源设置,光源不足时效果不明显。
环境光 ambient
理解了漫反射的过程再来看环境光也不难。
刚刚漫反射的过程是:给灯光设置漫反射属性(light.diffuse
),给材质设置漫反射颜色属性(material.diffuseColor
),两者相遇时,产生漫反射,显示最终看到的效果。
而环境光对应的是场景的 ambientColor
属性,与材质的 ambientColor
属性(或者 ambientTexture
属性)相遇时产生的效果。
ambientColor 环境光颜色
类型都是 BABYLON.Color3
就没什么好说的了,但和 diffuseColor
不同的是,默认值为黑色(new Color3(0, 0, 0)
)。
案例一:场景设置ambientColor,材质设置ambientColor
我们直接来看案例:
定义三个球体,并给第2个和第3个球添加上材质,分别设置红色和绿色的环境光颜色:
// 不设置环境光颜色
var sphere0 = BABYLON.MeshBuilder.CreateSphere("sphere0", {}, scene);
sphere0.position.x = -1.5;
// 设置红色环境光颜色
var sphere1 = BABYLON.MeshBuilder.CreateSphere("sphere1", {}, scene);
var redMat = new BABYLON.StandardMaterial("redMat", scene);
redMat.ambientColor = new BABYLON.Color3(1, 0, 0);
sphere1.material = redMat;
// 设置绿色环境光颜色
var sphere2 = BABYLON.MeshBuilder.CreateSphere("sphere2", {}, scene);
sphere2.position.x = 1.5;
var greenMat = new BABYLON.StandardMaterial("redMat", scene);
greenMat.ambientColor = new BABYLON.Color3(0, 1, 0);
sphere2.material = greenMat;
完成以上步骤后,不出意外,三个球都是黑的,没啥问题。
此时,给场景的 ambientColor
设置为白色(1,1,1)
:
scene.ambientColor = new BABYLON.Color3(1, 1, 1);
也就是给当前场景添加一个白色的环境光,那么这时候,球2和球3材质的 ambientColor 就发挥作用了,使得它们变成了红色和绿色。由于第一个球没有任何材质,因此它依旧还是黑色:
以上就是环境光最简单的用法了。
在线预览地址:https://playground.babylonjs.com/#20OAV9#14591
高光 specular
先来看看高光是什么?
高光是一种美术用语,指光源照射到物体然后反射到人的眼睛里时,物体上最亮的那个点就是高光。高光不是光,而是物体上最亮的部分。
而在Babylon.js中,标准材质(StandardMaterial
)的高光(Specular
)属性用于控制物体表面在光照下的高光反射效果,通常用于模拟光滑或有光泽的表面。
StandardMaterial
上与高光有关的属性是以下两个:
specularColor
:设置材质的高光颜色,决定物体表面高光反射的颜色specularPower
:设置高光的强度,数值越大,高光越集中,数值越小,高光越分散
specularColor 高光颜色
specularColor 的类型也是 BABYLON.Color3
,它用来设置材质的高光颜色,决定物体表面高光反射的颜色。
specularColor 高光颜色案例一
直接上案例,看效果:
定义了一个球,不设置任何材质,在场景中创建一个平行光(光的 diffuse
设置为红色),并照射在球上。
const sphere0 = BABYLON.MeshBuilder.CreateSphere("sphere0", {}, scene);
const light = new BABYLON.DirectionalLight("DirectionalLight", new BABYLON.Vector3(0, -1, 0), scene);
light.diffuse = new BABYLON.Color3(1, 0, 0);
通过学习上面材质的 diffuseColor
我们知道,如果物体没有设置材质,会反射平行光的颜色,也就是说此时我们看到的小球应该是红色的:
诶~,红色确实是红色没错,但中间部分还有一个白色的光晕,看起来就像是手电筒照射在一个红色的台球上,比前面自发光还有环境光的视角效果真实多了!
这个球上最亮的那部分(白色)就是高光了,接下来我们通过给物体添加 StandardMaterial
并设置 specularColor
来控制高光的颜色:
const sphere0 = BABYLON.MeshBuilder.CreateSphere("sphere0", {}, scene);
// 创建标准材质,并设置高光颜色为绿色
const standardMat = new BABYLON.StandardMaterial("standardMat", scene);
standardMat.specularColor = new BABYLON.Color3(0, 1, 0);
sphere0.material = standardMat;
const light = new BABYLON.DirectionalLight("DirectionalLight", new BABYLON.Vector3(0, -1, 0), scene);
light.diffuse = new BABYLON.Color3(1, 0, 0);
通过最终的效果来看,我们确实是成功了,高光部分变为了绿色。
在线预览地址:https://playground.babylonjs.com/#20OAV9#14592
specularColor 高光颜色案例二
可以看到,默认情况下高光还是会被设置为白色,但在有些时候,我们可能不想要这种高光的效果,这时候也很简单,把 specularColor 设置为 黑色 (new BABYLON.Color3(0, 0, 0)
)即可。
来看一下这些案例的对比效果:
不设置
specularColor
设置
specularColor
为白色设置
specularColor
为黑色
在线预览地址:https://playground.babylonjs.com/#20OAV9#14613
specularPower 高光强度
另一个和材质的高光相关联的就是高光强度了,默认值为 64,数值越大,高光越集中,适用于光滑表面(如金属、玻璃)。数值越小,高光越分散,适用于粗糙表面(如木材、布料)。
用法也比较简单,这里就不做过多介绍了。
几种材质光颜色的默认值
为了方便大家了解到各个光颜色的默认值,我们在这里做一下小结:
StandardMaterial
:
默认值为白色:
diffuseColor
漫反射颜色、specularColor
高光颜色默认值为黑色:
ambientColor
环境光颜色、emissiveColor
自发光颜色
Scene
:
默认值为黑色:
ambientColor
环境光颜色
Light
:
默认值为白色:
diffuse
漫反射
材质对光的限制 maxSimultaneousLights
Babylon.js 允许我们创建和注册任意数量的灯光,但是材质在渲染时可以同时受到多少个光源的影响是有被限制的。在 StandardMaterial
上有一个 maxSimultaneousLights
属性就是用来做这个的,它的默认值是4,意味着一个材质最多可以同时受到 4 个光源的影响。
有这个限制的主要原因是:在复杂的场景中,可能会有很多光源,但为了优化性能,不需要每个材质都受到所有光源的影响。通过设置 maxSimultaneousLights
,可以限制每个材质在渲染时考虑的光源数量,从而提高渲染性能。
来看一个案例:
在这个示例中,虽然场景中有 5 个光源,但由于 maxSimultaneousLights
被设置为 3,因此球体材质在渲染时只会考虑 3 个光源的影响:
// 创建场景
var scene = new BABYLON.Scene(engine);
// 创建相机
var camera = new BABYLON.ArcRotateCamera("camera", Math.PI / 2, Math.PI / 2, 10, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvas, true);
// 创建光源
var light1 = new BABYLON.PointLight("light1", new BABYLON.Vector3(10, 10, 0), scene);
var light2 = new BABYLON.PointLight("light2", new BABYLON.Vector3(-10, 10, 0), scene);
var light3 = new BABYLON.PointLight("light3", new BABYLON.Vector3(0, 10, 10), scene);
var light4 = new BABYLON.PointLight("light4", new BABYLON.Vector3(0, 10, -10), scene);
var light5 = new BABYLON.PointLight("light5", new BABYLON.Vector3(0, 20, 0), scene);
// 创建材质
var material = new BABYLON.StandardMaterial("material", scene);
material.diffuseColor = new BABYLON.Color3(1, 0, 0); // 红色
material.maxSimultaneousLights = 3; // 设置同时影响的光源数量为 3
// 创建球体并应用材质
var sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter: 2}, scene);
sphere.material = material;
效果如下:
可以看到,球上只有三个高光的白点,证明了只有三个光对其产生了影响。
在线预览地址为:https://playground.babylonjs.com/#20OAV9#14612
透明度属性 alpha
现实生活中的物体也不一定全是像台球、木头这种不透明的东西,也有玻璃球,水晶这种透明,半透明的情况存在。
而 Babylon.js 中设置材质的透明度属性,使用起来非常的简单,设置 alpha
属性即可。
基于上面高光颜色案例二,来做下修改,给第三个球的材质设置半透明:
standardMat2.alpha = 0.5;
效果显示为:
在线预览地址:https://playground.babylonjs.com/#20OAV9#14614
后语
知识无价,支持原创!这篇文章对比前两篇的篇幅明显长了,没办法,材质确实就是有很多东西可以说的,而且和光的反应也还只是其中的一部分。通过这一章节的学习,你不妨想想开篇时提到的那种一半红,一半绿的球可以怎么去实现呢?(嘻嘻,可以先作为课后作业,实在不会也没事,咱后面的光源部分会教你实现)
在下一章节,我们会继续介绍材质中比较重要的部分——纹理,敬请期待吧~
喜欢霖呆呆的小伙伴还希望可以关注霖呆呆的公众号 LinDaiDai
我会不定时的更新一些前端方面的知识内容以及自己的原创文章🎉。
你的鼓励就是我持续创作的主要动力 😊。
其它相关文章推荐: