# babylonjs
# dat.gui
观察图形的变化,调控范围检测
import * as dat from "dat.gui";
const gui = new dat.GUI();
gui
.add(cube.position, "x")
.min(0)
.max(5)
.step(0.01)
.name("移动x轴")
.onChange((value) => {
console.log("值被修改:", value);
})
.onFinishChange((value) => {
console.log("完全停下来:", value);
});
// 修改物体的颜色
const params = {
color: "#ffff00",
fn: () => {
// 让立方体运动起来
gsap.to(cube.position, { x: 5, duration: 2, yoyo: true, repeat: -1 });
},
};
gui.addColor(params, "color").onChange((value) => {
console.log("值被修改:", value);
cube.material.color.set(value);
});
// 设置选项框
gui.add(cube, "visible").name("是否显示");
var folder = gui.addFolder("设置立方体");
folder.add(cube.material, "wireframe");
// 设置按钮点击触发某个事件
folder.add(params, "fn").name("立方体运动");
//控制变量
var gui = new dat.GUI();
// console.warn(camera)
gui.add(camera, 'alpha', -Math.PI,0);
gui.add(camera, 'beta',0,Math.PI);
gui.add(light.direction,'x',0,10,0.01)
# 简单的babylonjs实例
- new Engine(canvas, true)
- new Scene(this.engine)
- this.engine.runRenderLoop
- this.scene.render()
- new ArcRotateCamera
- camera.attachControl(this.canvas)
- new HemisphericLight
// main.ts
import { ArcRotateCamera, Engine, HemisphericLight, MeshBuilder, Scene, Vector3 } from "babylonjs";
import WorldAxis from "./WorldAxis";
import * as dat from "dat.gui";
export default class Basicscene {
engine: Engine;
scene: Scene;
constructor(readonly canvas: HTMLCanvasElement) {
// 创建引擎
this.engine = new Engine(canvas, true)
// 创建场景
this.scene = this.createscene()
// 持续运行
this.engine.runRenderLoop(() => {
// 场景渲染
this.scene.render()
})
}
createscene(): Scene {
// 构建一个场景
const scene = new Scene(this.engine)
// 坐标轴
const worldAxis = new WorldAxis(this.scene); // scene即场景
// 创建相机
const camera = new ArcRotateCamera('camera', 0, 0, 5, new Vector3(0, 0, 0))
//console.log(camera,camera.position)
// // 根据场景可重新调整相机的位置
//camera.setPosition(new Vector3(5, 0, 0));
// 增加交互事件
camera.attachControl(this.canvas)
// 创建物体
const box = MeshBuilder.CreateBox('box')
// 创建灯光
const light = new HemisphericLight('light', new Vector3(0, 1, 0), this.scene)
//控制变量
var gui = new dat.GUI();
// console.warn(camera)
gui.add(camera, 'alpha', -Math.PI,0);
gui.add(camera, 'beta',0,Math.PI);
gui.add(light.direction,'x',0,10,0.01)
// 返回场景
return scene
}
}
import { Mesh, Scene, Color4, MeshBuilder, Vector3 } from 'babylonjs'
export default class WorldAxis {
private axisX: Mesh;
private axisY: Mesh;
private axisZ: Mesh;
constructor(scene: Scene) {
this.axisX = MeshBuilder.CreateLines("axisX", {
colors: [new Color4(255, 0, 0, 1), new Color4(255, 0, 0, 1)],
points: [new Vector3(0, 0, 0), new Vector3(80, 0, 0)],
}, scene);
this.axisY = MeshBuilder.CreateLines("axisY", {
colors: [new Color4(0, 255, 0, 1), new Color4(0, 255, 0, 1)],
points: [new Vector3(0, 0, 0), new Vector3(0, 80, 0)]
}, scene);
this.axisZ = MeshBuilder.CreateLines("axisZ", {
colors: [new Color4(0, 0, 255, 1), new Color4(0, 0, 255, 1)],
points: [new Vector3(0, 0, 0), new Vector3(0, 0, 80)]
}, scene);
}
}
# babylonjs 引入模型
- SceneLoader.ImportMeshAsync异步引入模型
- 通过场景获取mesh Id或者Name
import {SceneLoader} from "babylonjs";
async importMesh() {
const result = await SceneLoader.ImportMeshAsync('',
'https://assets.babylonjs.com/meshes/',
'both_houses_scene.babylon')
console.log(result)
const ground = this.scene.getMeshById('ground')!
ground.position.y = -.5
const house1 = this.scene.getMeshByName('detached_house')!
house1.position.y = 1
house1.rotation.x = Math.PI / 4
}
- 引入复杂的图形时(.glb)可能需要需要babylonjs-loaders去帮助解析
import {SceneLoader} from "babylonjs";
import 'babylonjs-loaders'
SceneLoader.ImportMeshAsync("", "https://assets.babylonjs.com/meshes/", "village.glb");
# babylonjs创建mesh
- MeshBuilder.CreateBox('box') 创建立方体
- MeshBuilder.CreateGround 创建地面
- MeshBuilder.CreateCylinder创建柱体
const box = MeshBuilder.CreateBox('box')
const ground = MeshBuilder.CreateGround(
'ground',
{height:10,width:10}
)
const roof =MeshBuilder.CreateCylinder('roof',{
height:1.2,// 高度
diameter:1.3,//直径
tessellation:3//份数
})
# 设置物体的大小
// 自己设置
const box = MeshBuilder.CreateBox('box',{
// width:2,
// height:1.5,
// depth:3
})
// 利用缩放1
box.scaling.set(3,3,3)
// 利用缩放2
box.scaling = new Vector3(1,1,1)
# 设置物体方向和旋转
- 角度制可借助Tools方法实现转化
import { Tools } from "babylonjs";
ground.position.y = -.5
// 角度制
box.rotation.y =Tools.ToRadians(45)
// 弧度制
box.rotation.y = Math.PI/4
# babylonjs设置纹理材质
- new StandardMaterial 创建标准材质
- groundMat.diffuseColor=new Color3()设置漫反射颜色
- ground.material
- boxMat.diffuseTexture = new Texture()
// 广场
const ground = MeshBuilder.CreateGround('ground',{height:10,width:10})
const groundMat = new StandardMaterial('groundMat')
groundMat.diffuseColor = new Color3(0,1,0)
// 添加到物体上
ground.material = groundMat
// 物体
const box = MeshBuilder.CreateBox('box')
const boxMat = new StandardMaterial('boxMat')
// 设置漫反射纹理
boxMat.diffuseTexture = new Texture('https://www.babylonjs-playground.com/textures/floor.png')
box.material = boxMat
# babylonjs不同面设置不同的纹理 faceUV
In the faceUV array faces are numbered 0 for back, 1 front, 2 right, 3 left, 4 top and 5 bottom.
const faceUV = [];
faceUV[0] = new Vector4(0.5, 0.0, 0.75, 1.0); //rear face
faceUV[1] = new Vector4(0.0, 0.0, 0.25, 1.0); //front face
faceUV[2] = new Vector4(0.25, 0, 0.5, 1.0); //right side
faceUV[3] = new Vector4(0.75, 0, 1.0, 1.0); //left side
// 创建物体
const box = MeshBuilder.CreateBox('box',{
faceUV,
wrap:true
})
const boxMat = new StandardMaterial('boxMat')
boxMat.diffuseTexture = new Texture('https://assets.babylonjs.com/environments/cubehouse.png')
box.material = boxMat
# babylonjs 合并网格 MergeMeshes
box和roof是两个不同的网格对象,通过Mesh.MergeMeshes合并成一个整体,第一个参数数组谁在前就默认采取谁的纹理材质,除非后续第六个参数设置true,则会避免纹理材质的覆盖。
第五个参数 如果为true(默认值为false),则将网格细分为子网格。
第六个参数如果为true(默认值为false),则将网格细分为具有多个材质的子网格,忽略subdivideWithSubMeshes。
import { Mesh } from "babylonjs";
const house = Mesh.MergeMeshes([box, roof], true, false, undefined, false, true)
# babylonjs 复制mesh
复制网格的两种主要方法是克隆网格或创建网格实例。克隆可以复制一个独立的网格,而实例的材质仍与原始网格相关联。 不能更改网格实例的材质。
- clonedHouse = house.clone("clonedHouse")
- instanceHouse = house.createInstance("instanceHouse")
Mesh.MergeMeshes()的第一个参数是Mesh[],此时就推荐使用clone而非创建实例
const clonedHouse = house.clone("clonedHouse")
clonedHouse.position.x=3
const clonedHouseMat = new StandardMaterial('clonedHouseMat')
clonedHouseMat.diffuseColor = new Color3(1,0,0)
clonedHouse.material = clonedHouseMat
const clonedHouse = house.createInstance("clonedHouse")
clonedHouse.position.x=3
const clonedHouseMat = new StandardMaterial('clonedHouseMat')
//设置不会生效
clonedHouseMat.diffuseColor = new Color3(1,0,0)
clonedHouse.material = clonedHouseMat
# babylonjs 音频
import {Sound} from "babylonjs";
// new Sound(
// '',
// 'https://playground.babylonjs.com/sounds/cellolong.wav',
// this.scene,
// null,
// {loop:true,autoplay:true}
// )
const sound = new Sound(
'',
'https://playground.babylonjs.com/sounds/cellolong.wav',
this.scene,
)
console.log(sound)
setTimeout(()=>{
sound.play()
},3000)
# babylonjs相对坐标实例
- Color3.xxx()实例方法
- Color3.Blue().toColor4()转化成rgba形式
import { ArcRotateCamera, Color3, DynamicTexture, Engine, HemisphericLight, MeshBuilder, Scene, StandardMaterial, TransformNode, Vector3 } from "babylonjs";
export default class ParentChild {
engine: Engine;
scene: Scene;
constructor(readonly canvas: HTMLCanvasElement) {
// 创建引擎
this.engine = new Engine(canvas, true)
// 创建场景
this.scene = this.createscene(canvas)
// 持续运行
this.engine.runRenderLoop(() => {
// 场景渲染
this.scene.render()
})
}
createscene(canvas:HTMLCanvasElement): Scene {
const scene = new Scene(this.engine);
const camera = new ArcRotateCamera("camera", -Math.PI / 2.2, Math.PI / 2.5, 15, new Vector3(0, 0, 0));
camera.attachControl(canvas, true);
const light = new HemisphericLight("light", new Vector3(0, 1, 0));
const faceColors = [];
faceColors[0] = Color3.Blue().toColor4();
faceColors[1] = Color3.Teal().toColor4();
faceColors[2] = Color3.Red().toColor4();
faceColors[3] = Color3.Purple().toColor4();
faceColors[4] = Color3.Green().toColor4();
faceColors[5] = Color3.Yellow().toColor4();
// 立方体六个面通过faceColors设置不同颜色
const boxParent = MeshBuilder.CreateBox("Box", {faceColors:faceColors});
//size是给长宽高设置相同的值
const boxChild = MeshBuilder.CreateBox("Box", {size: 0.5, faceColors:faceColors});
// 设置boxParent为boxChild的父位置,而不是原点了
boxChild.setParent(boxParent);
// 同上,不同的写法
// boxChild.parent =boxParent
boxChild.position.x = 0;
boxChild.position.y = 2;
boxChild.position.z = 0;
boxChild.rotation.x = Math.PI / 4;
boxChild.rotation.y = Math.PI / 4;
boxChild.rotation.z = Math.PI / 4;
boxParent.position.x = 2;
boxParent.position.y = 0;
boxParent.position.z = 0;
boxParent.rotation.x = 0;
boxParent.rotation.y = 0;
boxParent.rotation.z = -Math.PI / 4;
const boxChildAxes = this.localAxes(1, scene);
boxChildAxes.parent = boxChild;
this.showAxis(6, scene);
return scene;
}
showAxis (size:number, scene:Scene) {
const makeTextPlane = (text:string, color:string, size:number) => {
//第四个参数generateMipMaps:Mipmap技术有点类似于LOD技术,但是不同的是,LOD针对的是模型资源,而Mipmap针对的纹理贴图资源,开启后会提高体验但更占内存
const dynamicTexture = new DynamicTexture("DynamicTexture", 50, scene, true);
dynamicTexture.hasAlpha = true;
dynamicTexture.drawText(text, 5, 40, "bold 36px Arial", color , "transparent", true);
// 创建文字所在的位置的平面
const plane = MeshBuilder.CreatePlane("TextPlane", {size}, scene);
plane.material = new StandardMaterial("TextPlaneMaterial", scene);
console.warn(plane.material)
// 测试文字所在的平面,可注释掉下面三行查看
// plane.material.diffuseColor = new Color3(0,1,0)
// 去除背面,设置后旋转到背面,xyz写的文字就看不见了
plane.material.backFaceCulling = false;
// 高光颜色
plane.material.specularColor = new Color3(0, 0, 0);
//漫反射纹理
plane.material.diffuseTexture = dynamicTexture;
return plane;
};
const axisX = MeshBuilder.CreateLines("axisX", { points: [
Vector3.Zero(), new Vector3(size, 0, 0), new Vector3(size * 0.95, 0.05 * size, 0),
new Vector3(size, 0, 0), new Vector3(size * 0.95, -0.05 * size, 0)
]});
axisX.color = new Color3(1, 0, 0);
const xChar = makeTextPlane("X", "red", size / 10);
xChar.position = new Vector3(0.9 * size, -0.05 * size, 0);
const axisY = MeshBuilder.CreateLines("axisY", { points:[
Vector3.Zero(), new Vector3(0, size, 0), new Vector3( -0.05 * size, size * 0.95, 0),
new Vector3(0, size, 0), new Vector3( 0.05 * size, size * 0.95, 0)
]});
axisY.color = new Color3(0, 1, 0);
const yChar = makeTextPlane("Y", "green", size / 10);
yChar.position = new Vector3(0, 0.9 * size, -0.05 * size);
const axisZ = MeshBuilder.CreateLines("axisZ", { points: [
Vector3.Zero(), new Vector3(0, 0, size), new Vector3( 0 , -0.05 * size, size * 0.95),
new Vector3(0, 0, size), new Vector3( 0, 0.05 * size, size * 0.95)
]});
axisZ.color = new Color3(0, 0, 1);
const zChar = makeTextPlane("Z", "blue", size / 10);
zChar.position = new Vector3(0, 0.05 * size, 0.9 * size);
};
/*********************************************************************/
/*******************************Local Axes****************************/
localAxes (size:number, scene:Scene) {
const local_axisX = MeshBuilder.CreateLines("local_axisX", { points: [
Vector3.Zero(), new Vector3(size, 0, 0), new Vector3(size * 0.95, 0.05 * size, 0),
new Vector3(size, 0, 0), new Vector3(size * 0.95, -0.05 * size, 0)
]}, scene);
local_axisX.color = new Color3(1, 0, 0);
const local_axisY = MeshBuilder.CreateLines("local_axisY", { points: [
Vector3.Zero(), new Vector3(0, size, 0), new Vector3(-0.05 * size, size * 0.95, 0),
new Vector3(0, size, 0), new Vector3(0.05 * size, size * 0.95, 0)
]}, scene);
local_axisY.color = new Color3(0, 1, 0);
const local_axisZ = MeshBuilder.CreateLines("local_axisZ", { points: [
Vector3.Zero(), new Vector3(0, 0, size), new Vector3(0, -0.05 * size, size * 0.95),
new Vector3(0, 0, size), new Vector3(0, 0.05 * size, size * 0.95)
]}, scene);
local_axisZ.color = new Color3(0, 0, 1);
/**
* 变换节点(TransformNode)是一个未渲染但可用作变换中心的对象。与使用空网格作为父节点相比,
* 这可以减少内存使用量并提高渲染速度,而且比使用枢轴矩阵更简单。
*/
const local_origin = new TransformNode("local_origin");
// 设置就会按照坐标轴
local_axisX.parent = local_origin;
local_axisY.parent = local_origin;
local_axisZ.parent = local_origin;
return local_origin;
}
}
小立方体坐标系正确需要:
- 坐标轴的父级是小立方体
- 设置一个变换节点TransformNode,坐标轴的每个轴的的父级指向它
# babylonjs绘制复杂图形
- CreateCylinder需要借助earcut,引入并且绑定到window上让其自动调佣
- 绘图时绘制了一面给出depth,背面会自动绘制出
import { ArcRotateCamera, Color3, Engine, HemisphericLight, Material, Mesh, MeshBuilder, Scene, SceneLoader, Sound, StandardMaterial, Texture, Tools, Vector3, Vector4 } from "babylonjs";
import WorldAxis from "./WorldAxis";
import 'babylonjs-loaders'
import * as earcut from 'earcut'
(window.earcut = earcut)
export default class Car {
engine: Engine;
scene: Scene;
constructor(readonly canvas: HTMLCanvasElement) {
// 创建引擎
this.engine = new Engine(canvas, true)
// 创建场景
this.scene = this.createscene()
// 持续运行
this.engine.runRenderLoop(() => {
// 场景渲染
this.scene.render()
})
}
createscene(): Scene {
const scene = new Scene(this.engine);
const worldAxis = new WorldAxis(this.scene); // scene即场景
const camera = new ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2.5, 3, new Vector3(0, 0, 0));
camera.attachControl(this.canvas, true);
const light = new HemisphericLight("light", new Vector3(1, 1, 0));
const car = this.buildCar();
car.rotation.x = -Math.PI/2
return scene;
}
buildCar() {
//base
const outline = [
new Vector3(-0.3, 0, -0.1),
new Vector3(0.2, 0, -0.1),
]
//curved front
for (let i = 0; i < 20; i++) {
outline.push(new Vector3(0.2 * Math.cos(i * Math.PI / 40), 0, 0.2 * Math.sin(i * Math.PI / 40) - 0.1));
}
//top
outline.push(new Vector3(0, 0, 0.1));
outline.push(new Vector3(-0.3, 0, 0.1));
//back formed automatically
// 设置faceUV 0正面 1 侧面 2背面
const faceUV = [];
faceUV[0] = new Vector4(0, 0.5, 0.38, 1);
faceUV[1] = new Vector4(0, 0, 1, 0.5);
faceUV[2] = new Vector4(0.38, 1, 0, 0.5);
const car = MeshBuilder.ExtrudePolygon("car", { shape: outline, depth: 0.2, faceUV });
//car material
const carMat = new StandardMaterial("carMat");
carMat.diffuseTexture = new Texture("https://assets.babylonjs.com/environments/car.png");
car.material = carMat;
const wheelUV = [];
wheelUV[0] = new Vector4(0, 0, 1, 1);
wheelUV[1] = new Vector4(0, 0.5, 0, 0.5);
wheelUV[2] = new Vector4(0, 0, 1, 1);
const wheelMat = new StandardMaterial('wheelMat')
wheelMat.diffuseTexture = new Texture('https://assets.babylonjs.com/environments/wheel.png')
const wheelRB = MeshBuilder.CreateCylinder("wheelRB", { diameter: 0.125, height: 0.05, faceUV: wheelUV })
wheelRB.material = wheelMat
wheelRB.parent = car;
wheelRB.position.z = -0.1;
wheelRB.position.x = -0.2;
wheelRB.position.y = 0.035;
const wheelRF = wheelRB.clone("wheelRF");
wheelRF.position.x = 0.1;
const wheelLB = wheelRB.clone("wheelLB");
wheelLB.position.y = -0.2 - 0.035;
const wheelLF = wheelRF.clone("wheelLF");
wheelLF.position.y = -0.2 - 0.035;
// const wheelLF1 = wheelRF.createInstance("wheelLF");
// 方案1 return newCar
// const newCar = Mesh.MergeMeshes([car,wheelRB,wheelRF,wheelLB,wheelLF], true, false, undefined, false, true)!
// 方案2 绑定父级 注意添加位置在克隆之前,否则后续clone的wheel的父级不生效
// wheelRB.setParent(car)
return car;
}
}
# 案例视角移动
import { ArcRotateCamera, Axis, Color3, Engine, HemisphericLight, MeshBuilder, Scene, SceneLoader, Space, Vector3 } from "babylonjs";
import WorldAxis from "./WorldAxis";
export default class Basicscene {
engine: Engine;
scene: Scene;
constructor(readonly canvas: HTMLCanvasElement) {
// 创建引擎
this.engine = new Engine(canvas, true)
// 创建场景
this.scene = this.createscene()
// 持续运行
this.engine.runRenderLoop(() => {
// 场景渲染
this.scene.render()
})
}
createscene(): Scene {
// 构建一个场景
const scene = new Scene(this.engine)
// 坐标轴
const worldAxis = new WorldAxis(this.scene); // scene即场景
// 创建相机
const camera = new ArcRotateCamera('camera', -Math.PI / 2, Math.PI / 2.5, 10, new Vector3(0, 0, 0))
//console.log(camera,camera.position)
// // 根据场景可重新调整相机的位置
//camera.setPosition(new Vector3(5, 0, 0));
// 增加交互事件
camera.attachControl(this.canvas)
// 创建物体
// 创建灯光
const light = new HemisphericLight('light', new Vector3(0, 1, 0), this.scene)
const faceColors = [];
faceColors[0] = Color3.Blue().toColor4();
faceColors[1] = Color3.Teal().toColor4();
faceColors[2] = Color3.Red().toColor4();
faceColors[3] = Color3.Purple().toColor4();
faceColors[4] = Color3.Green().toColor4();
faceColors[5] = Color3.Yellow().toColor4();
const sphere = MeshBuilder.CreateBox("sphere", { size: .5, faceColors });
sphere.position = new Vector3(2, 0, 2);
const points = [];
points.push(new Vector3(2, 0, 2));
points.push(new Vector3(2, 0, -2));
points.push(new Vector3(-2, 0, -2));
// points.push(new Vector3(-2, 0, 2));
points.push(points[0]); //close the triangle;
MeshBuilder.CreateLines("triangle", { points: points })
class Slide {
turn: number
dist: number
constructor(turn: number, dist: number) {
this.turn = turn;
this.dist = dist;
}
}
const track: any[] = [];
track.push(new Slide(Math.PI / 2, 4));
track.push(new Slide(3 * Math.PI / 4, 8));
track.push(new Slide(3 * Math.PI / 4, 8 + 4 * Math.sqrt(2)));
// track.push(new Slide(Math.PI / 2, 4));
// track.push(new Slide(Math.PI / 2 , 8));
// track.push(new Slide(Math.PI / 2, 12));
// track.push(new Slide(Math.PI / 2, 16));
let distance = 0;
let step = 0.05;
let p = 0;
// sphere.rotation.x = Math.PI / 12
scene.onBeforeRenderObservable.add(() => {
sphere.movePOV(0, 0, step);
distance += step;
if (distance > track[p].dist) {
sphere.rotate(Axis.Y, track[p].turn, Space.LOCAL);
p += 1;
p %= track.length;
if (p === 0) {
distance = 0;
sphere.position = new Vector3(2, 0, 2); //reset to initial conditions
sphere.rotation = Vector3.Zero();//prevents error accumulation
}
}
});
// 返回场景
return scene
}
}
← webgl babylonjs API →