# 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;
    }

}

小立方体坐标系正确需要:

  1. 坐标轴的父级是小立方体
  2. 设置一个变换节点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
    }

}
最后更新: 10/20/2023, 8:40:11 AM