简介
Babylon.js 是一款基于 WebGL/WebGPU 的开源 3D 引擎,由微软主导开发并持续维护,专注于在浏览器中创建高性能交互式 3D/2D 图形应用。支持 JavaScript/TypeScript 开发,适用于游戏、工业可视化、VR/AR 及产品展示等场景。
官方网址:Babylon.js: Powerful, Beautiful, Simple, Open - Web-Based 3D At Its Best
https://www.babylonjs.com/
渲染效果图


一、BabyLon.js 安装
1、CND方式导入
<!-- 核心库 -->
<script src="https://cdn.babylonjs.com/babylon.js"></script>
<!-- 加载器(选装) -->
<script src="https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
2、npm安装(推荐)
npm install @babylonjs/core @babylonjs/materials
二、封装BabyLonApp类
1、创建BabylonThreeD.ts文件
在项目文件夹src内任意位置创建文件

2、构建BabyLonApp类
2.1、导入babylon核心模块及其相关模块
import * as BABYLON from 'babylonjs' // 导入核心模块
import * as GUI from 'babylonjs-gui' // 导入GUI系统
import 'babylonjs-loaders' // 导入模型加载器
3、构建BabyLonApp封装类及相关成员
3.1、构造函数及成员初始化
export class BabyLonApp {
mEngine: BABYLON.Engine | null = null // 引擎
mScene: BABYLON.Scene | null = null // 场景
constructor(readonly canvas: HTMLCanvasElement) { // 构造函数
this.mEngine = new BABYLON.Engine(canvas) // 创建引擎
this.mScene = onCreateScene(this.mEngine, this.canvas) // 创建场景
window.addEventListener('resize', () => { this.onResize() }) // 监听窗口大小变化
}
}
3.2、babylon调试模式
babylon调试模式无特殊需求可不开启,默认关闭状态;
onDebug (debugOn: boolean = false) { // 调试函数
if (this.mScene === null) return // 如果场景为空,则返回
if (debugOn) {
this.mScene.debugLayer.show({ overlay: true }) // 显示调试层
} else {
this.mScene.debugLayer.hide() // 隐藏调试层
}
}
3.3、babylon运行渲染
onRunBabyLon () { // 运行渲染函数
this.onDebug(false) // 关闭调试层
if (this.mEngine === null) return // 如果引擎为空,则返回
this.mEngine.runRenderLoop(() => { // 渲染函数
if (this.mScene !== null) this.mScene.render() // 渲染场景
})
}
3.4、babylon暂停渲染
onPauseRender () { // 暂停渲染函数
if (this.mEngine) {
this.mEngine.stopRenderLoop() // 停止渲染循环
}
}
3.5、babylon停止渲染
onStopBabyLon () { // 停止渲染函数
if (this.mEngine && this.mScene) {
this.mEngine.stopRenderLoop() // 停止渲染循环
this.mEngine = null // 清空引擎
this.mScene = null // 清空场景
window.removeEventListener('resize', () => { this.onResize() }) // 解绑窗口大小改变事件
}
}
3.6、babylon重置画布尺寸
onResize() { // 重置窗口尺寸函数(窗口大小改变时重置画布大小)
if (this.mEngine !== null) this.mEngine.resize() // 如果引擎不为空,则重置画布大小
}
3.7、babylon加载模型文件
onImportMeshAsyncScene () { // 加载导入场景模型函数
BABYLON.SceneLoader.ImportMeshAsync(
null, // 定加载的模型名称为空''或为null表示加载全部
'', // 模型文件目录路径(静态路径/网络路径皆可),如:http://xxxxx/ 或 ./(表示public文件夹下)
'', // 模型文件名,如:xxxx.glb/xxx.obj/xxx.ply/xxx.stl/xxx.gltf...等格式
this.mScene, // 目标场景,默认为当前场景
(e: any) => { // 加载进度回调
console.log('当前加载进度:', Math.round((e.loaded * 100) / e.total)) // 当前模型加载进度
}
).then(result => { // 模型加载成功
const model = result.meshes[0] // 获取导入的模型根网格
model.renderingGroupId = 1 // 设置物体的渲染组优先级-数值越高越靠后
this.onResize() // 调整场景尺寸
}).catch(err => { // 模型加载失败
console.log('模型加载失败, ', err)
})
}
4、创建场景
const onCreateScene = (engine: BABYLON.Engine, canvas: HTMLCanvasElement) => { // 创建场景函数
const scene = new BABYLON.Scene(engine) // 创建场景
const camera = new BABYLON.ArcRotateCamera(CAMERANAME, Math.PI / 4, Math.PI / 4, 100, BABYLON.Vector3.Zero(), scene) // 创建相机
camera.inertia = 0 // 禁用惯性
camera.attachControl(canvas, true)
// 移除默认鼠标输入并添加自定义输入
camera.inputs.removeByType('ArcRotateCameraPointersInput')
const customInput = new CustomArcRotateCameraInput()
customInput.camera = camera
camera.inputs.add(customInput)
scene.clearColor = new BABYLON.Color4(0.5, 0.6, 0.8, 1) // 设定场景背景色
// 添加一个 DirectionalLight 作为太阳光
const sunLight = new BABYLON.DirectionalLight(SUNLIGHT, new BABYLON.Vector3(2, -2, 1), scene)
sunLight.position = new BABYLON.Vector3(20, 40, 20)
sunLight.intensity = 1.0
return scene // 返回场景
}
5、构建自定义相机操作类CustomArcRotateCameraInput
class CustomArcRotateCameraInput implements BABYLON.ICameraInput<BABYLON.ArcRotateCamera> { // 自定义相机操作类
// 需要绑定的相机
camera: BABYLON.ArcRotateCamera = null!;
// 鼠标状态变量
private _isPointerDown: boolean = false;
private _isMiddlePointerDown: boolean = false;
private _lastX: number = 0;
private _lastY: number = 0;
// 限制距离范围
private _minRadius: number = 2; // 最小距离
private _maxRadius: number = 1000; // 最大距离
// 绑定事件监听
attachControl(noPreventDefault?: boolean): void {
const engine = this.camera.getEngine();
const element = engine.getRenderingCanvas();
if (!element) return;
//const dt : number = engine.getDeltaTime() * 1000;
element.addEventListener("pointerdown", (event:any) => {
if (event.button === 0) {
this._isPointerDown = true; // 左键拖动
} else if (event.button === 2) {
this._isMiddlePointerDown = true; // 右键拖动
}
this._lastX = event.clientX;
this._lastY = event.clientY;
if (!noPreventDefault) event.preventDefault();
});
element.addEventListener("pointermove", (event:any) => {
const deltaX = event.clientX - this._lastX;
const deltaY = event.clientY - this._lastY;
if (this._isPointerDown) {
// 左键拖动:旋转相机
this.camera.alpha -= deltaX * 0.005; // 水平旋转速度
this.camera.beta -= deltaY * 0.005; // 垂直旋转速度
this.camera.beta = BABYLON.Scalar.Clamp(this.camera.beta, -Math.PI / 2, Math.PI / 2); // 限制垂直角度
} else if (this._isMiddlePointerDown) {
// 右键拖动:平移焦点
const moveSpeed = this.camera.radius * 0.0015; // 移动速度与距离成正比
const forward = this.camera.getForwardRay().direction; // 相机的前方向
const right = BABYLON.Vector3.Cross(forward, BABYLON.Vector3.Up()).normalize(); // 相机的右方向
const up = BABYLON.Vector3.Cross(right, forward).normalize(); // 相机的上方向
const translation = right.negate().scale(-deltaX * moveSpeed).add(up.scale(deltaY * moveSpeed));
this.camera.target.addInPlace(translation); // 平移焦点
}
this._lastX = event.clientX;
this._lastY = event.clientY;
if (!noPreventDefault) event.preventDefault();
});
element.addEventListener("pointerup", (event:any) => {
if (event.button === 0) {
this._isPointerDown = false;
} else if (event.button === 2) {
this._isMiddlePointerDown = false;
}
});
element.addEventListener("pointerleave", () => {
this._isPointerDown = false;
this._isMiddlePointerDown = false;
});
element.addEventListener("wheel", (event:any) => {
if (!noPreventDefault) event.preventDefault();
// 滚轮缩放
const sensScale = 0.001;
const scaleRatio = Math.exp(event.deltaY * sensScale);
this.camera.radius = BABYLON.Scalar.Clamp(this.camera.radius * scaleRatio, this._minRadius, this._maxRadius); // 限制距离
});
}
// 解绑事件监听
detachControl(element?: HTMLElement): void {
if (!element) return;
element.removeEventListener("pointerdown", () => {});
element.removeEventListener("pointermove", () => {});
element.removeEventListener("pointerup", () => {});
element.removeEventListener("pointerleave", () => {});
element.removeEventListener("wheel", () => {});
}
getClassName(): string {
return "CustomArcRotateCameraInput"
}
getTypeName(): string {
return "CustomArcRotateCameraInput"
}
getSimpleName(): string {
return "customMouseInput"
}
}
三、Vue组件中使用BabyLonApp类
1、canvas元素
<canvas ref="mBabyLonCanvasRef" id="renderCanvas" class="three-d-canvas-dom"></canvas>
2、导入BabyLonApp类
import { BabyLonApp } from '@/XXX/BabyLonThreeD' // 导入BabyLonApp(路径为自己创建路径)
3、声明canvas节点及BabyLonApp成员
const mBabyLonCanvasRef = ref<HTMLCanvasElement | null>(null) // canvas节点
const mBabyLonApp = ref<BabyLonApp | null>(null) // Babylon构造器实例
4、初始化BabyLonApp对象
const onInitBabyLon = () => { // 初始化BabyLon函数
if (mBabyLonCanvasRef.value === null || mThreeDSceneRef.value === null) return // 若canvas节点为空 则直接结束
if (mBabyLonApp.value !== null) { // 若实例不为空 则先停止/销毁实例
mBabyLonApp.value.onStopBabyLon() // 停止三维场景运行
mBabyLonApp.value = null // 清空实例
}
mBabyLonApp.value = new BabyLonApp(mBabyLonCanvasRef.value) // 构造BabyLon对象
mBabyLonApp.value.onRunBabyLon() // 运行BabyLon
mBabyLonApp.value.onImportMeshAsyncScene() // 模型异步加载
}
四、BabyLonApp类完整代码
import * as BABYLON from 'babylonjs' // 导入核心模块
import * as GUI from 'babylonjs-gui' // 导入GUI系统
import 'babylonjs-loaders' // 导入模型加载器
const CAMERANAME:string = 'GS_CAMERA' // 相机名称
const SUNLIGHT:string = 'GS_SUNLIGHT' // 光源名称
export class BabyLonApp {
mEngine: BABYLON.Engine | null = null // 引擎
mScene: BABYLON.Scene | null = null // 场景
constructor(readonly canvas: HTMLCanvasElement) { // 构造函数
this.mEngine = new BABYLON.Engine(canvas) // 创建引擎
this.mScene = onCreateScene(this.mEngine, this.canvas) // 创建场景
window.addEventListener('resize', () => { this.onResize() }) // 监听窗口大小变化
}
onDebug (debugOn: boolean = false) { // 调试函数
if (this.mScene === null) return // 如果场景为空,则返回
if (debugOn) {
this.mScene.debugLayer.show({ overlay: true }) // 显示调试层
} else {
this.mScene.debugLayer.hide() // 隐藏调试层
}
}
onRunBabyLon () { // 运行渲染函数
this.onDebug(false) // 关闭调试层
if (this.mEngine === null) return // 如果引擎为空,则返回
this.mEngine.runRenderLoop(() => { // 渲染函数
if (this.mScene !== null) this.mScene.render() // 渲染场景
})
}
onPauseRender () { // 暂停渲染函数
if (this.mEngine) {
this.mEngine.stopRenderLoop() // 停止渲染循环
}
}
onStopBabyLon () { // 停止渲染函数
if (this.mEngine && this.mScene) {
this.mEngine.stopRenderLoop() // 停止渲染循环
this.mEngine = null // 清空引擎
this.mScene = null // 清空场景
window.removeEventListener('resize', () => { this.onResize() }) // 解绑窗口大小改变事件
}
}
onImportMeshAsyncScene () { // 加载导入场景模型函数
BABYLON.SceneLoader.ImportMeshAsync(
null, // 定加载的模型名称为空''或为null表示加载全部
'', // 模型文件目录路径(静态路径/网络路径皆可),如:http://xxxxx/ 或 ./(表示public文件夹下)
'', // 模型文件名,如:xxxx.glb/xxx.obj/xxx.ply/xxx.stl/xxx.gltf...等格式
this.mScene, // 目标场景,默认为当前场景
(e: any) => { // 加载进度回调
console.log('当前加载进度:', Math.round((e.loaded * 100) / e.total)) // 当前模型加载进度
}
).then(result => { // 模型加载成功
const model = result.meshes[0] // 获取导入的模型根网格
model.renderingGroupId = 1 // 设置物体的渲染组优先级-数值越高越靠后
this.onResize() // 调整场景尺寸
}).catch(err => { // 模型加载失败
console.log('模型加载失败, ', err)
})
}
onResize() { // 重置窗口尺寸函数(窗口大小改变时重置画布大小)
if (this.mEngine !== null) this.mEngine.resize() // 如果引擎不为空,则重置画布大小
}
}
const onCreateScene = (engine: BABYLON.Engine, canvas: HTMLCanvasElement) => { // 创建场景函数
const scene = new BABYLON.Scene(engine) // 创建场景
const camera = new BABYLON.ArcRotateCamera(CAMERANAME, Math.PI / 4, Math.PI / 4, 100, BABYLON.Vector3.Zero(), scene) // 创建相机
camera.inertia = 0 // 禁用惯性
camera.attachControl(canvas, true)
// 移除默认鼠标输入并添加自定义输入
camera.inputs.removeByType('ArcRotateCameraPointersInput')
const customInput = new CustomArcRotateCameraInput()
customInput.camera = camera
camera.inputs.add(customInput)
scene.clearColor = new BABYLON.Color4(0.5, 0.6, 0.8, 1) // 设定场景背景色
// 添加一个 DirectionalLight 作为太阳光
const sunLight = new BABYLON.DirectionalLight(SUNLIGHT, new BABYLON.Vector3(2, -2, 1), scene)
sunLight.position = new BABYLON.Vector3(20, 40, 20)
sunLight.intensity = 1.0
return scene // 返回场景
}
class CustomArcRotateCameraInput implements BABYLON.ICameraInput<BABYLON.ArcRotateCamera> { // 自定义相机操作类
// 需要绑定的相机
camera: BABYLON.ArcRotateCamera = null!;
// 鼠标状态变量
private _isPointerDown: boolean = false;
private _isMiddlePointerDown: boolean = false;
private _lastX: number = 0;
private _lastY: number = 0;
// 限制距离范围
private _minRadius: number = 2; // 最小距离
private _maxRadius: number = 1000; // 最大距离
// 绑定事件监听
attachControl(noPreventDefault?: boolean): void {
const engine = this.camera.getEngine();
const element = engine.getRenderingCanvas();
if (!element) return;
//const dt : number = engine.getDeltaTime() * 1000;
element.addEventListener("pointerdown", (event:any) => {
if (event.button === 0) {
this._isPointerDown = true; // 左键拖动
} else if (event.button === 2) {
this._isMiddlePointerDown = true; // 右键拖动
}
this._lastX = event.clientX;
this._lastY = event.clientY;
if (!noPreventDefault) event.preventDefault();
});
element.addEventListener("pointermove", (event:any) => {
const deltaX = event.clientX - this._lastX;
const deltaY = event.clientY - this._lastY;
if (this._isPointerDown) {
// 左键拖动:旋转相机
this.camera.alpha -= deltaX * 0.005; // 水平旋转速度
this.camera.beta -= deltaY * 0.005; // 垂直旋转速度
this.camera.beta = BABYLON.Scalar.Clamp(this.camera.beta, -Math.PI / 2, Math.PI / 2); // 限制垂直角度
} else if (this._isMiddlePointerDown) {
// 右键拖动:平移焦点
const moveSpeed = this.camera.radius * 0.0015; // 移动速度与距离成正比
const forward = this.camera.getForwardRay().direction; // 相机的前方向
const right = BABYLON.Vector3.Cross(forward, BABYLON.Vector3.Up()).normalize(); // 相机的右方向
const up = BABYLON.Vector3.Cross(right, forward).normalize(); // 相机的上方向
const translation = right.negate().scale(-deltaX * moveSpeed).add(up.scale(deltaY * moveSpeed));
this.camera.target.addInPlace(translation); // 平移焦点
}
this._lastX = event.clientX;
this._lastY = event.clientY;
if (!noPreventDefault) event.preventDefault();
});
element.addEventListener("pointerup", (event:any) => {
if (event.button === 0) {
this._isPointerDown = false;
} else if (event.button === 2) {
this._isMiddlePointerDown = false;
}
});
element.addEventListener("pointerleave", () => {
this._isPointerDown = false;
this._isMiddlePointerDown = false;
});
element.addEventListener("wheel", (event:any) => {
if (!noPreventDefault) event.preventDefault();
// 滚轮缩放
const sensScale = 0.001;
const scaleRatio = Math.exp(event.deltaY * sensScale);
this.camera.radius = BABYLON.Scalar.Clamp(this.camera.radius * scaleRatio, this._minRadius, this._maxRadius); // 限制距离
});
}
// 解绑事件监听
detachControl(element?: HTMLElement): void {
if (!element) return;
element.removeEventListener("pointerdown", () => {});
element.removeEventListener("pointermove", () => {});
element.removeEventListener("pointerup", () => {});
element.removeEventListener("pointerleave", () => {});
element.removeEventListener("wheel", () => {});
}
getClassName(): string {
return "CustomArcRotateCameraInput"
}
getTypeName(): string {
return "CustomArcRotateCameraInput"
}
getSimpleName(): string {
return "customMouseInput"
}
}
五、组件使用完整示例代码
<template>
<div class="three-D-scene-box">
<div class="three-D-scene">
<canvas ref="mBabyLonCanvasRef" id="renderCanvas" class="three-d-canvas-dom"></canvas>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import { BabyLonApp } from '@/views/Home/Components/ThreeDScene/BabyLon/BabyLonThreeD' // babylon对象
const mThreeDSceneRef = ref<HTMLDivElement | null>(null) // 三维场景元素节点
const mBabyLonCanvasRef = ref<HTMLCanvasElement | null>(null) // canvas节点
const mBabyLonApp = ref<BabyLonApp | null>(null) // Babylon实例
onMounted(() => { // 生命周期函数-组件挂载后执行
onInitBabyLon()
})
onUnmounted(() => { // 生命周期函数-组件卸载执行
if (mBabyLonApp.value !== null) { // 停止三维场景
mBabyLonApp.value.onStopBabyLon() // 停止三维场景运行
mBabyLonApp.value = null // 清空实例
}
})
const onInitBabyLon = async () => { // 初始化BabyLon函数
if (mBabyLonCanvasRef.value === null) return // 若canvas节点为空 则直接结束
if (mBabyLonApp.value !== null) { // 若实例不为空 则先停止/销毁实例
mBabyLonApp.value.onStopBabyLon() // 停止三维场景运行
mBabyLonApp.value = null // 清空实例
}
mBabyLonApp.value = new BabyLonApp(mBabyLonCanvasRef.value) // 构造BabyLon对象
mBabyLonApp.value.onRunBabyLon() // 运行BabyLon
mBabyLonApp.value.onImportMeshAsyncScene() // 加载导入场景模型函数
}
</script>
<style scoped lang="sass">
</style>
个人笔记分享,若有错误之处还望大佬纠错指教。

3129

被折叠的 条评论
为什么被折叠?



