Web3D开源库babylonjs入门教程(持续更新中)

2,217 阅读6分钟

这是我的第一篇教程,欢迎大家批评指正o( ̄︶ ̄)o

本文的所有代码可在 github.com/GGsimidadad… 上查看。

环境搭建

本教程使用 vue3 + typescript + babylonjs 开发。

使用Vite构建Vue

使用Vite构建Vue开发环境,步骤请参考官网文档vitejs.cn/guide/

安装Babylonjs

按照官网教程创建完毕,进入项目根目录,执行yarn add babylonjs -s安装babylonjs。

改造页面

删除脚手架生成的HelloWorld.vue文件,将App.vue文件改造成如下代码:

<script setup lang="ts">

</script>

<template>
  <canvas id="container" ></canvas>
</template>

<style>
html,
body,
#app,
#container {
  margin: 0;
  width: 100%;
  height: 100%;
}
</style>

至此,页面中只有一个充满整个页面的canvas元素,而它将作为3D场景的画板,我们可以在上面构建3D世界了。

版本

我的package.json内容如下:

{
  "name": "babylonjs-build",
  "version": "0.0.0",
  "scripts": {
    "dev": "vite",
    "build": "vue-tsc --noEmit && vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "babylonjs": "^4.2.0",
    "vue": "^3.2.25"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^2.0.0",
    "typescript": "^4.4.4",
    "vite": "^2.7.2",
    "vue-tsc": "^0.29.8"
  }
}

开发第一个3D场景

组成

我们先简单的了解一下一个3D场景是由哪些元素组成的。

  1. 引擎Engine:(必需)负责了与底层WebGL和Audio连接
  2. 场景Scene:(必需)为所有灯光、相机、模型等提供显示场景
  3. 相机Camera:(必需)是用户从不同角度观察场景的工具
  4. 灯光Light:模拟各种真实世界的光线
  5. 模型Mesh:在场景中呈现的各种模型,包括立体模型、平面模型和线
  6. 材质Material:用于改变模型表面呈现的颜色、图片、纹理等效果
  7. 动画Animation:使场景中的元素可以动态改变状态,元素包括相机、灯光、模型、材质等

导入引擎

首先,我们要从babylonjs中导入Engine,并且将指定的canvas元素作为3D场景的容器。

<script setup lang="ts">
import { onMounted } from '@vue/runtime-core';
import { Engine } from 'babylonjs';

onMounted(() => {
  const canvas = document.querySelector<HTMLCanvasElement>('#container');
  const engine = new Engine(canvas);
});
</script>

创建并渲染场景

babylonjs中导入Scene,创建一个新的场景,并且执行engine.runRenderLoop,让引擎在每一帧中去渲染scene。同时还需要创建一个相机,有了相机我们才能看到场景里的东西,如果没有相机控制台会报Uncaught Error: No camera defined的错。

<script setup lang="ts">
import { onMounted } from '@vue/runtime-core';
import { ArcRotateCamera, Engine, Scene, Vector3 } from 'babylonjs';

onMounted(() => {
  const canvas = document.querySelector<HTMLCanvasElement>('#container');
  const engine = new Engine(canvas);
  const scene = createScene(engine);
  engine.runRenderLoop(() => {
    scene.render();    
  });
});

const createScene = (engine: Engine) => {
  /** 创建场景 */
  const scene = new Scene(engine);
  /** end */

  /** 创建相机 */
  const camera = new ArcRotateCamera('camera', 0, 0, 10, Vector3.Zero(), scene);
  camera.attachControl(true);
  /** end */

  return scene;
};

</script>

其中,ArcRotateCamera构造函数的参数依次代表:

  1. name: 相当于我们平时理解的id或者key,全局唯一
  2. alpha: 初始水平方向旋转弧度
  3. beta: 初始垂直方向旋转弧度
  4. radius: 距离旋转中心点的半径
  5. target: 旋转中心坐标
  6. scene: 所在场景

创建完相机后,需要调用attachControl方法在画布上附加相机的控制事件。老版本中这个方法需要传入canvas参数指定画布,新版本移除了这个参数,只需要传入一个布尔值指定是否阻止事件冒泡即可。

现在,我们的画布变成了深蓝色,说明场景已经被正确的创建并渲染了。如果想把场景背景色设置成灰色,只需要设置sceneclearColor属性即可:

scene.clearColor = Color4.FromColor3(Color3.Gray());

下面我们介绍如何创建模型和灯光。

模型和灯光

创建第一个模型

我们在场景中添加一个立方体。

创建立方体需要用到babylonjsMeshBuilder.CreateBox方法。注意,老版教程中会使用Mesh.CreateBox方法,这个方法官方已经不推荐使用了。

import { ArcRotateCamera, Color3, Engine, MeshBuilder, Scene, Vector3, Color4 } from 'babylonjs';

/** 中间代码省略 */

const createScene = (engine: Engine) => {
  /** 创建场景 */
  const scene = new Scene(engine);
  /** end */

  /** 创建相机 */
  const camera = new ArcRotateCamera('camera', 0, 0, 10, Vector3.Zero(), scene);
  camera.attachControl(true);
  /** end */

  /** 创建立方体 */
  const box = MeshBuilder.CreateBox('box', { width: 2, height: 3, depth: 4 }, scene);
  /** end */
  
  return scene;
};

MeshBuild.CreateBox方法需要传入三个参数:

  1. name:和cameraname含义一样,就是我们平时理解的id,需要全局唯一
  2. option:构建立方体的初始化配置,配置表如下:
属性默认值
size(number) box每条边的长度1
height(number) 在Y轴上的边的长度,会覆盖size的值size
width(number) 在X轴上的边的长度,会覆盖size的值size
depth(number) 在Z轴上的边的长度,会覆盖size的值size
faceColors(Color4[]) 由Color4组成的长度为6的数组,代表每个面的颜色Color4(1, 1, 1, 1) for each side
faceUV(Vector4[]) 由Vector4组成的长度为6的数组,代表每个面的纹理贴图的应用范围UVs(0, 0, 1, 1) for each side
updatable(boolean) 指定Mesh的顶点数据是否可以被更新false
sideOrientation(number) 定义模型的每个面的可见方式。例如正方体有六个面,每个面都有正面和反面。默认相机只能看到一个面的正面,这样比较节省计算资源。- Mesh.FRONTSIDE,表示正面可见
  • Mesh.BACKSIDE,表示反面可见- Mesh.DOUBLESIDE,表示正反面都可见
  • Mesh.DEFAULT,目前这个值和FRONTSIDE相等 | Mesh.DEFAULTSIDE |
  1. scene:代表物体所处场景

现在场景中就多了一个234的立方体,但是我们发现,这个立方体浑身漆黑,这是为什么呢?

原因很简单,因为我们还没有设置灯光。和现实世界一样,如果没有光,所有的物体都会是黑的。那么接下来我们就给这个场景增加一个平行光DirectionalLight——也就是我们现实中的太阳。

创建灯光

import { ArcRotateCamera, Color3, Engine, MeshBuilder, Scene, Vector3, Color4, DirectionalLight } from 'babylonjs';

/** 中间代码省略 */

const createScene = (engine: Engine) => {
  /** 创建场景 */
  const scene = new Scene(engine);
  scene.clearColor = Color4.FromColor3(Color3.Gray());
  /** end */

  /** 创建相机 */
  const camera = new ArcRotateCamera('camera', 0, 0, 10, Vector3.Zero(), scene);
  camera.attachControl(true);
  /** end */

  /** 创建立方体 */
  const box = MeshBuilder.CreateBox('box', { width: 2, height: 3, depth: 4 }, scene);
  /** end */

  /** 创建灯光 */
  const light = new DirectionalLight('light', Vector3.Down(), scene);
  light.diffuse = Color3.Red();
  light.specular = Color3.Green();
  /** end */

  return scene;
};

DirectionalLight构造函数需要传入三个参数:

  1. name:名称,全局唯一
  2. direction:光线照射的方向。Vector3.Down(){ x: 0, y: -1, z: 0 },表示光线垂直向下
  3. scene:光线所处场景

diffuse这个属性代表光线的漫反射光色,所谓漫反射色表示光线照射到物体上时物体所反射出的颜色,我们这里设置为了红色。现在我们先注释掉light.specular这段代码,可以看到光线从上而下照射到物体上时,物体的顶面呈现出了红色,而其他五个面由于没有被光照到,所以仍然是黑色:

specular这个属性代表镜面光色(也叫高光色),即光源在物体上反射出的颜色,就好像从镜子中看灯泡一样。我们这里设置成了绿色,理论上物体表面应该会有一个绿色的高光,但是实际效果却呈现出了黄色!

这是为什么呢?

原因很简单,物体表面现在有一个红色的漫反射色和绿色的高光色,着色器会把两种颜色混合,所以呈现出了黄色。当我们把相机旋转到一定角度,可以看到光源在物体上确实还是显示绿色:

由此可见,物体最终呈现出的颜色并不一定是我们设置的某种颜色,而是由多种情况下的颜色的混合结果。这个在开发中要特别注意。

更多的模型和灯光

当然,babylonjs还内置了很多其他的创建模型的方法,包括不规则几何图形和线。灯光除了平行光DirectionalLight,还有点光PointLight、聚光灯SpotLight、半球光HemisphericLight。您可以在babylonjs官方网站找到更多的信息,或者查看镜像中文网站doc.cnbabylon.com/babylon101/。我们这里就不再赘述了,后面有机会再展开讲解。

现在有一个问题了,我们能不能在没有灯光照射的情况下,让物体产生颜色呢?答案当然是可以,这就需要引入一个新的概念——材质。下面我们就聊聊如何创建材质,以及设置它的相关属性。