一、创建网格
利用网格构建器MeshBuilder可以创建各种形状,平面,线以及自定义网格。
例如:
CreateBox创建立方体,CreateSphere创建球体,CreateCylinder创建柱体,CreateCapsule创建胶囊,
CreatePlane创建平面,CreateGround创建地面
CreateDisc创建圆盘或正多边形,CreateTorus创建环面,CreateTorusKnot创建环结,CreateText创建文本
CreateLines创建线,CreateDashedLines创建虚线
...
1.创建立方体与地面(平行于xz平面的水平平面)
import {
Engine,
Scene,
ArcRotateCamera,
Vector3,
Color4,
Color3,
MeshBuilder,
SpotLight,
Mesh,
Space,
Tools,
} from '@babylonjs/core'
//创建一个名为 BabylonScene 的类
export class BabylonScene {
engine: Engine
scene: Scene
//private将属性或方法标记为私有,表示它们只能在类的内部被访问,外部无法直接访问。
constructor(private canvas: HTMLCanvasElement) {
this.engine = new Engine(this.canvas, true) //渲染引擎
this.scene = this.CreateScene() //场景
// 渲染场景
this.engine.runRenderLoop(() => {
this.scene.render()
})
}
CreateScene(): Scene {
const scene = new Scene(this.engine) // 创建场景
/**
* 弧形旋转摄像机ArcRotateCamera
* 配置项:相机名称,初始经度,初始纬度,与焦点距离,位置,场景
*/
const camera = new ArcRotateCamera(
'ArcRotateCamera',
Math.PI / 8,
Math.PI / 8,
10,
new Vector3(0, 0, 0),
scene
)
// 相机定位
camera.setPosition(new Vector3(0, 10, 10))
camera.attachControl(this.canvas, true) //让相机控制画布
/**
* 聚光灯SpotLight
* 配置项:名称,位置,方向,角度,指数,场景
*/
const light = new SpotLight(
'spotLight',
new Vector3(0, 10, 0),
new Vector3(0.5, -1, 0),
Math.PI / 2,
3,
scene
)
/**
* 创建一个立方体
* 配置项:名称,选项,场景
*/
const box = MeshBuilder.CreateBox('box', { size: 1 }, scene)
// 创建地面
const ground = MeshBuilder.CreateGround(
'ground',
{ width: 6, height: 6 },
scene
)
ground.position.y = -1
return scene
}
}
效果如图:
| 立方体选项属性 | value | 默认值 |
|---|---|---|
| size | (number) 尺寸 | 1 |
| height | (number) 高度大小,覆盖size选项 | size |
| width | (number) 宽度大小,覆盖size选项 | size |
| depth | (number) 深度大小,覆盖size选项 | size |
| faceColors | (Color4[]) | Color4(1, 1, 1, 1) |
| faceUV | (Vector4[]) | UVs(0, 0, 1, 1) |
| wrap | (boolean) 当为真时,所有垂直边( 0 , 1 , 2 , 3 )将垂直应用图像纹理 | false |
| topBaseAt | (number) 给定 0 , 1 , 2 , 3 的顶触边底 | 1 |
| bottomBaseAt | (number) 给定 0 , 1 , 2 , 3 的下触面底脚 | 0 |
| updatable | (boolean) 如果网格是可更新的则为 true | false |
| sideOrientation | (number) 侧方位 | DEFAULTSIDE |
| frontUVs | (Vector4) ONLY WHEN sideOrientation:BABYLON.Mesh.DOUBLESIDE set | Vector4(0,0, 1,1) |
| backUVs | (Vector4) ONLY WHEN sideOrientation:BABYLON.Mesh.DOUBLESIDE set | Vector4(0,0, 1,1) |
| 地面选项属性 | value | 默认值 |
|---|---|---|
| width | (number) 宽度 | 1 |
| height | (number) 高度 | 1 |
| updatable | (boolean) 如果网格是可更新的则为 true | false |
| subdivisions | (number) 沿每个轴的平方细分数 | 1 |
2.创建球体
// 创建球体
const sphere = MeshBuilder.CreateSphere(
'sphere',
{ diameter: 1, segments: 60 },
scene
)
sphere.position.x = 1
//创建椭球体
const elliptic = MeshBuilder.CreateSphere('elliptic', {
diameterX: 1,
diameterY: 0.5,
diameterZ: 0.5,
})
//创建弧形
const arc = MeshBuilder.CreateSphere('arc', {
arc: 0.25,
sideOrientation: Mesh.DOUBLESIDE,
})
//创建弧和切片
const slice = MeshBuilder.CreateSphere('slice', {
slice: 0.3,
sideOrientation: Mesh.DOUBLESIDE,
})
效果如图:
| 选项属性 | value | 默认值 |
|---|---|---|
| segments | (number) 水平段数 | 32 |
| diameter | (number) 球的直径 | 1 |
| diameterX | (number) X 轴上的直径,覆盖直径选项 | diameter |
| diameterY | (number) Y 轴上的直径,覆盖直径选项 | diameter |
| diameterZ | (number) Z 轴上的直径,覆盖直径选项 | diameter |
| arc | (number) 圆周(纬度)在 0 与 1 之间的比率 | 1 |
| slice | (number) 0 与 1 之间的高度(经度)比 | 1 |
| updatable | (boolean) 如果网格是可更新的则为 true | false |
| sideOrientation | (number) 侧方位 | DEFAULTSIDE |
| frontUVs | (Vector4) ONLY WHEN sideOrientation:BABYLON.Mesh.DOUBLESIDE set | Vector4(0, 0, 1,1) |
| backUVs | (Vector4) ONLY WHEN sideOrientation:BABYLON.Mesh.DOUBLESIDE set | Vector4(0, 0, 1,1) |
3.创建圆柱体或圆锥体
//创建圆柱体
const cylinder = MeshBuilder.CreateCylinder(
'cylinder',
{ height: 2 },
scene
)
cylinder.position.x = 2
//创建圆锥体
const cone = MeshBuilder.CreateCylinder(
'cone',
{ height: 2, diameterTop: 0 },
scene
)
cone.position.x = 1
//创建棱柱
const prism = MeshBuilder.CreateCylinder('prism', {
tessellation: 5,
})
//创建弧形
const arc = MeshBuilder.CreateCylinder('arc', {
arc: 0.6,
sideOrientation: Mesh.DOUBLESIDE,
})
arc.position.x = -2
//创建蛋糕切片
const slice = MeshBuilder.CreateCylinder('slice', {
arc: 0.1,
height: 0.3,
sideOrientation: Mesh.DOUBLESIDE,
enclose: true,
})
slice.position.x = -2
slice.position.z = 1
效果如图:
| 选项属性 | value | 默认值 |
|---|---|---|
| height | (number) 高度 | 2 |
| diameterTop | (number) 上盖的直径,可以为零创建一个圆锥体,覆盖直径选项 | 1 |
| diameterBottom | (number) 底盖的直径,不能为零,覆盖直径选项 | 1 |
| diameter | (number) 两个瓶盖的直径 | 1 |
| tessellation | (number) 径向边数 | 24 |
| subdivisions | (number) 环数 | 1 |
| faceColors | (Color4[]) | Color4(1, 1, 1, 1) |
| faceUV | (Vector4[]) | UVs(0, 0, 1, 1) |
| arc | (number) 0 与 1 的周长比 | 1 |
| hasRings | (boolean) 使细分相互独立,所以它们变成不同的面 | false |
| enclose | (boolean) 在每细分一个圆柱体上添加两个额外的面以使其围绕高度轴关闭 | false |
| updatable | (boolean) 如果网格是可更新的则为 true | false |
| sideOrientation | (number) 侧方位 | DEFAULTSIDE |
| frontUVs | (Vector4) ONLY WHEN sideOrientation:BABYLON.Mesh.DOUBLESIDE set | Vector4(0,0, 1,1) |
| backUVs | (Vector4) ONLY WHEN sideOrientation:BABYLON.Mesh.DOUBLESIDE set | Vector4(0,0, 1,1) |
4.创建胶囊
//创建胶囊
const capsule = MeshBuilder.CreateCapsule('capsule', { height: 2 }, scene)
capsule.position.x = 3
//使用细分创建胶囊
const capsule1 = MeshBuilder.CreateCapsule(
'capsule1',
{
height: 2,
radius: 0.5,
tessellation: 4, //胶囊上的圆柱部分的数量
capSubdivisions: 1, //平行于方向运行的胶囊帽部分上的子段数
topCapSubdivisions: 12, //顶部细分
},
scene
)
capsule1.position.x = 2
//胶囊方向
const capsule2 = MeshBuilder.CreateCapsule(
'capsule2',
{
height: 2,
radius: 0.25,
tessellation: 36,
capSubdivisions: 6,
subdivisions: 6, //平行于方向的胶囊管段上的子段数
orientation: Vector3.Forward(), //开始时的方向
},
scene
)
//不同半径的胶囊
const capsule3 = MeshBuilder.CreateCapsule(
'capsule3',
{ height: 3, radius: 0.5, radiusTop: 0.8 },
scene
)
capsule3.position.x = -2
效果如图:
| 选项属性 | value | 默认值 |
|---|---|---|
| orientation? | (Vector3) 开始时的方向 | Vector3.Up |
| subdivisions | (number) 平行于方向的胶囊管段上的子段数 | 2 |
| tessellation | (number) 胶囊上的圆柱部分的数量. | 16 |
| height | (number) 高度(长度) | 1 |
| radius | (number) 半径 | 0.25 |
| capSubdivisions | (number) 平行于方向运行的胶囊帽部分上的子段数 | 6 |
| radiusTop? | (number) 顶部半径 | |
| radiusBottom? | (number) 底部半径 | |
| topCapSubdivisions? | (number) 顶部细分 | |
| bottomCapSubdivisions? | (number) 底部细分 |
5.创建平面(平行于xy平面的平面)
//创建单面平面
const plane = MeshBuilder.CreatePlane(
'plane',
{ height: 2, width: 1 },
scene
)
//创建双面平面
const plane1 = MeshBuilder.CreatePlane(
'plane1',
{ height: 2, width: 1, sideOrientation: Mesh.DOUBLESIDE },
scene
)
plane1.position.x = -2
单面平面旋转到后方是看不到的,双面平面前后方都可以看到。效果如图:
| 选项属性 | value | 默认值 |
|---|---|---|
| size | (number) 尺寸 | 1 |
| width | (number) 宽度 | size |
| height | (number) 高度 | size |
| updatable | (boolean) 如果网格是可更新的则为 true | false |
| sideOrientation | (number) 侧方位 | DEFAULTSIDE |
| sourcePlane | (Plane) 源平面(math)网格将转换为源平面 | null |
| frontUVs | (Vector4) ONLY WHEN sideOrientation:BABYLON.Mesh.DOUBLESIDE set | Vector4(0,0, 1,1) |
| backUVs | (Vector4) ONLY WHEN sideOrientation:BABYLON.Mesh.DOUBLESIDE set | Vector4(0,0, 1,1) |
6.创建圆盘或正多边形
//创建圆盘
const disc = MeshBuilder.CreateDisc('disc', {sideOrientation: Mesh.DOUBLESIDE}, scene)
disc.position.x = 2
//创建正三角形
const disc1 = MeshBuilder.CreateDisc('disc1', { tessellation: 3,sideOrientation: Mesh.DOUBLESIDE }, scene)
//创建扇区
const disc2 = MeshBuilder.CreateDisc(
'disc2',
{ tessellation: 12, arc: 5 / 6,sideOrientation: Mesh.DOUBLESIDE },
scene
)
disc2.position.x = -2
效果如图:
| 选项属性 | value | 默认值 |
|---|---|---|
| radius | (number) 圆盘或多边形的半径 | 0.5 |
| tessellation | (number) 圆盘/多边形边数 | 64 |
| arc | (number) 弧,0 与 1 的周长比 | 1 |
| updatable | (boolean) 如果网格是可更新的则为 true | false |
| sideOrientation | (number) 侧方位 | DEFAULTSIDE |
7.创建环面与环结
环结是一个连续的形状,围绕着环面的表面旋转。匝数由绕组整数p和q决定。最简单的打结结是用2和3为p和q的组合。创建的环结的起源是在底层环面的中心。
//创建环面
const torus = MeshBuilder.CreateTorus(
'torus',
{
thickness: 0.25, //厚度
diameter: 1, //直径
},
scene
)
torus.position.x = 2
//创建低pq环结
const torusKnot = MeshBuilder.CreateTorusKnot(
'torusKnot',
{
radius: 0.5,
tube: 0.1, //厚度
radialSegments: 128, //径向段数
p: 6,
q: 4,
},
scene
)
//创建高pq环结
const torusKnot1 = MeshBuilder.CreateTorusKnot(
'torusKnot1',
{
radius: 0.5,
tube: 0.01, //厚度
radialSegments: 1024, //径向段数
p: 100,
q: 120,
},
scene
)
torusKnot1.position.x = -2
//创建高非整数pq环结
const torusKnot2 = MeshBuilder.CreateTorusKnot(
'torusKnot2',
{
radius: 0.5,
tube: 0.01, //厚度
radialSegments: 1024, //径向段数
p:-117.885,
q:-169.656465,
},
scene
)
torusKnot2.position.x = -2
torusKnot2.position.z = -2
效果如图:
| 环面选项属性 | value | 默认值 |
|---|---|---|
| diameter | (number) 环面直径 | 1 |
| thickness | (number) 厚度 | 0.5 |
| tessellation | (number) 沿着圆的段数 | 16 |
| updatable | (boolean) 如果网格是可更新的则为 true | false |
| sideOrientation | (number) 侧方位 | DEFAULTSIDE |
| frontUVs | (Vector4) ONLY WHEN sideOrientation:BABYLON.Mesh.DOUBLESIDE set | Vector4(0,0, 1,1) |
| backUVs | (Vector4) ONLY WHEN sideOrientation:BABYLON.Mesh.DOUBLESIDE set | Vector4(0,0, 1,1) |
| 环结选项属性 | value | 默认值 |
|---|---|---|
| radius | (number) 环结半径 | 2 |
| tube | (number) 厚度 | 0.5 |
| radialSegments | (number) 径向段数 | 32 |
| tubularSegments | (number) 管状段数 | 32 |
| p | (number) 绕组数 | 2 |
| q | (number) 绕组数 | 3 |
| updatable | (boolean) 如果网格是可更新的则为 true | false |
| sideOrientation | (number) 侧方位 | DEFAULTSIDE |
| frontUVs | (Vector4) ONLY WHEN sideOrientation:BABYLON.Mesh.DOUBLESIDE set | Vector4(0,0, 1,1) |
| backUVs | (Vector4) ONLY WHEN sideOrientation:BABYLON.Mesh.DOUBLESIDE set | Vector4(0,0, 1,1) |
8.创建线、虚线
//创建线
const myPoint = [
new Vector3(-2, -1, 0),
new Vector3(0, 1, 0),
new Vector3(2, -1, 0),
]
//线的颜色
const myColors = [
new Color4(1, 0, 0, 1),
new Color4(0, 1, 0, 1),
new Color4(0, 0, 1, 1),
new Color4(1, 1, 0, 1),
]
//闭合线
myPoint.push(myPoint[0])
const lines = MeshBuilder.CreateLines('lines', {
points: myPoint,
colors: myColors,
})
lines.position.x = 1
//创建螺旋线
const myPoints = []
const deltaTheta = 0.2
const deltaY = 0.005
const radius = 1
let theta = 0
let Y = 0
for (let i = 0; i < 400; i++) {
myPoints.push(
new Vector3(radius * Math.cos(theta), Y, radius * Math.sin(theta))
)
theta += deltaTheta
Y += deltaY
}
const lines1 = MeshBuilder.CreateLines('lines1', {
points: myPoints,
updatable: true,
})
lines1.position.x = -2
//创建虚线
const points = [
new Vector3(-2, -1, 0),
new Vector3(0, 1, 0),
new Vector3(2, -1, 0),
]
points.push(points[0])
const lines2 = MeshBuilder.CreateDashedLines('lines2', {
points: points,
dashSize: 1000, //破折号的大小
gapSize: 500, //间隙大小
dashNb: 80, //预定的破折号数
})
lines2.color = Color3.Red()
lines2.position.x = -2
lines2.position.z = 1
效果如图:
| 线选项属性 | value | 默认值 |
|---|---|---|
| points | (Vector3[]) 点,路径(必选) | |
| updatable | (boolean) 如果网格是可更新的则为 true | false |
| instance | (LineMesh) 要更新的线网格实例 | null |
| colors | (Color4[]) 每个点的颜色 | null |
| useVertexAlpha | (boolean) 如果不需要 alpha 混合,则为 false 。 | true |
| 虚线选项属性 | value | 默认值 |
|---|---|---|
| points | (Vector3[]) 点,路径(必选) | |
| dashSize | (number) 破折号的大小 | 3 |
| gapSize | (number) 间隙大小 | 1 |
| dashNb | (number) 预定的破折号数 | 200 |
| updatable | (boolean) 如果网格是可更新的则为true | false |
| instance | (LineMesh) 要更新的线网格实例 | null |
二、网格的变换(位置、旋转、缩放)
为了充分理解在场景中定位网格的所有方法,我们需要了解参考系。
当在场景中创建一个世界时,我们使用 世界空间 中的参考框架;场景中的每一个网格也都有自己的 局部空间,其在世界空间中的局部原点为(-1,2,1)。
即使用世界坐标系和局部坐标系两种坐标系作为参考框架。
所有的位置、旋转和尺寸大小都由三维向量来描述,可以使用 new Vector3(x,y,z) 来分别设置它们。
1.位置
/**
* 创建一个立方体
* 配置项:名称,选项(可以为物体设置长宽,体积等),场景
*/
const box = MeshBuilder.CreateBox('box', { size: 1 }, scene)
// 对于一个网格,不管它是否被旋转,下列都是正确的。
box.position.x = 2
box.position.y = 3
box.position.z = 4
// box.position = new Vector3(2, 3, 4) //(2, 3, 4)
// box.position.addInPlace(new Vector3(2, 3, 4)) //(-1 + 2, 2 + 3, 1 + 4) = (1, 5, 5)
// box.translate(new Vector3(2, 3, 4), 1, Space.WORLD) //(-1 + 2, 2 + 3, 1 + 4) = (1, 5, 5)
// 使用以下方法得到的位置取决于网格的方向。在不知道网格旋转的情况下,不可能给出一个结果位置。
// box.translate(new Vector3(2, 3, 4), 1, Space.LOCAL);
// box.setPositionWithLocalVector(new Vector3(2, 3, 4))
// box.locallyTranslate(new Vector3(2, 3, 4));
2.旋转
在网格创建时,旋转中心是网格的局部原点,并且旋转总是逆时针的。
使用局部坐标轴旋转物体时,首先绕y轴旋转,然后绕x轴旋转,最后绕z轴旋转。
使用世界坐标轴旋转物体时,首先绕z轴旋转,然后绕x轴旋转,最后绕y轴旋转。
/**
* 创建一个立方体
* 配置项:名称,选项(可以为物体设置长宽,体积等),场景
*/
const box = MeshBuilder.CreateBox('box', { size: 1 }, scene)
//旋转
box.rotation.x = Math.PI / 4
box.rotation.y = Tools.ToRadians(45)
box.rotation.z = Math.PI / 3
// box.rotation = new Vector3(Math.PI / 4, Tools.ToRadians(45), Math.PI / 3)
//排序旋转,进行首先是关于x轴,然后是y轴,然后是z轴的一系列的旋转,利用addRotation方法
// box.addRotation(Math.PI / 2, 0, 0)
// box.addRotation(0, 0, Math.PI / 3)
// box.addRotation(0, Math.PI / 8, 0)
// box.rotation.addRotation(Math.PI / 2, 0, 0).addRotation(0, 0, Math.PI / 3).addRotation(0, Math.PI / 8);
3.缩放
/**
* 创建一个立方体
* 配置项:名称,选项(可以为物体设置长宽,体积等),场景
*/
const box = MeshBuilder.CreateBox('box', { size: 1 }, scene)
//缩放
box.scaling.x = 0.5
box.scaling.y = 2
box.scaling.z = 0.5
// box.scaling = new Vector3(0.5, 2, 0.5)
三、网格克隆与实例
/**
* 创建一个立方体
* 配置项:名称,选项(可以为物体设置长宽,体积等),场景
*/
const box = MeshBuilder.CreateBox('box', { size: 1 }, scene)
//克隆
const box1 = box.clone('box2')
box1.position.x = -2
//创建实例,使用硬件加速渲染来绘制大量相同网格
for (let index = 0; index < 10; index++) {
let newInstance = box.createInstance('i' + index)
newInstance.position.x = index
newInstance.position.z = index
}
更多网格相关知识可以去官网 网格 学习。