返回 导航

Vue.js

hangge.com

Vue.js - 集成Babylon.js构建三维可视化场景教程1(安装配置、基本用法)

作者:hangge | 2026-06-10 09:39
    Babylon.js 是一个功能完备、工程化程度极高的 Web 3D 引擎,内置完整的渲染管线、相机系统、光照模型、PBR 材质、动画与物理支持,并且对 glTF / glb 等工业级三维模型格式有着良好的原生支持,非常适合用于构建复杂、长期演进的三维可视化系统。本文将演示如何在 Vue.js 项目中集成 Babylon.js,并创建一个简单的 3D 场景。

一、安装配置与基本用法

1,安装配置

进入 Vue 项目目录,执行如下命令安装 Babylon.js 及相关依赖库。
# 安装 Babylon.js 核心库
npm install babylonjs --save

# 安装 Babylon.js 加载器(用于加载3D模型)
npm install babylonjs-loaders --save

# 安装 GUI 库(可选,用于创建UI控件)
npm install babylonjs-gui --save

# 如果需要 inspector 工具(开发调试)
npm install babylonjs-inspector --save-dev

2,基本用法

(1)下面是一个简单的使用样例,里面包含场景、相机、渲染器、光源、地面、一个立方体、一个圆柱体、一个球体、窗口缩放响应、资源销毁与动画循环管理等内容。
<template>
    <div class="babylon-container">
        <div ref="canvasContainer" class="canvas-container">
            <canvas ref="renderCanvas" class="render-canvas"></canvas>
        </div>

        <!-- 加载状态 -->
        <div v-if="loading" class="loading-overlay">
            <div class="loader">加载中...</div>
        </div>

        <!-- 场景信息 -->
        <div class="scene-info">
            <p>物体数量: {{ objectCount }}</p>
            <p>FPS: {{ fps.toFixed(1) }}</p>
        </div>
    </div>
</template>

<script>
import * as BABYLON from 'babylonjs';

export default {
    name: "BabylonScene",
    data() {
        return {
            engine: null,
            scene: null,
            loading: true,
            error: null,
            objectCount: 0,
            fps: 60,
        };
    },
    mounted() {
        this.initBabylon();
    },
    beforeDestroy() {
        // 销毁组件时释放内存,防止内存泄漏
        if (this.engine) {
            this.engine.dispose();
        }
        window.removeEventListener("resize", this.onWindowResize);
    },
    methods: {
        initBabylon() {
            const canvas = this.$refs.renderCanvas;

            // 1. 初始化渲染引擎
            this.engine = new BABYLON.Engine(canvas, true,
                { preserveDrawingBuffer: true, stencil: true });

            // 2. 创建场景
            this.scene = this.createScene(this.engine, canvas);

            // 3. 隐藏加载遮罩
            this.loading = false;

            // 4. 注册渲染循环
            this.engine.runRenderLoop(() => {
                this.scene.render();
                this.fps = this.engine.getFps();
            });

            // 5. 监听窗口调整大小
            window.addEventListener("resize", this.onWindowResize);

            // 6. 初始化场景统计
            this.updateSceneInfo();
        },

        createScene(engine, canvas) {
            const scene = new BABYLON.Scene(engine);

            // 设置背景颜色
            scene.clearColor = BABYLON.Color3.FromHexString('#aaaaaa').toColor4();

            // 添加摄像头 (ArcRotateCamera 允许用户绕着中心点旋转)
            const camera = new BABYLON.ArcRotateCamera(
                'camera',
                -Math.PI / 2,
                Math.PI / 3,
                7,
                BABYLON.Vector3.Zero(),
                scene
            );
            camera.attachControl(canvas, true);

            // 设置相机控制
            camera.wheelPrecision = 50;
            camera.lowerRadiusLimit = 2;
            camera.upperRadiusLimit = 50;

            // 添加灯光
            const light1 = new BABYLON.HemisphericLight(
                'light1',
                new BABYLON.Vector3(1, 1, 0),
                scene
            );

            const light2 = new BABYLON.DirectionalLight(
                'light2',
                new BABYLON.Vector3(0, -1, 1),
                scene
            );
            light2.position = new BABYLON.Vector3(5, 10, 5)
            light2.intensity = 0.3

            // 添加地面
            const ground = BABYLON.MeshBuilder.CreateGround(
                'ground',
                { width: 5, height: 5 },
                scene
            );
            const groundMaterial = new BABYLON.StandardMaterial('groundMaterial', scene);
            groundMaterial.diffuseColor = new BABYLON.Color3(0.8, 0.8, 0.8);
            ground.material = groundMaterial;

            // 创建一个立方体
            const box = BABYLON.MeshBuilder.CreateBox("box", { size: 1 }, scene);
            box.position = new BABYLON.Vector3(-1.5, 0.75, 0);
            // 给立方体上色
            const material = new BABYLON.StandardMaterial("boxMat", scene);
            material.diffuseColor = new BABYLON.Color3(0, 0.5, 1);
            box.material = material;

            // 创建一个球体
            const sphere = BABYLON.MeshBuilder.CreateSphere(
                'sphere',
                { diameter: 1 },
                scene
            );
            sphere.position = new BABYLON.Vector3(0, 0.75, 0);
            // 给球体上色
            const sphereMaterial = new BABYLON.StandardMaterial('sphereMaterial', scene);
            sphereMaterial.diffuseColor = new BABYLON.Color3(0.2, 0.8, 0.2);
            sphere.material = sphereMaterial;

            // 创建一个圆柱体
            const cylinder = BABYLON.MeshBuilder.CreateCylinder(
                'cylinder',
                { height: 1, diameter: 1 },
                scene
            );
            cylinder.position = new BABYLON.Vector3(1.5, 0.75, 0);
            // 给圆柱体上色
            const cylinderMaterial = new BABYLON.StandardMaterial('cylinderMaterial', scene);
            cylinderMaterial.diffuseColor = new BABYLON.Color3(0.2, 0.2, 0.8);
            cylinder.material = cylinderMaterial;

            // 添加动画
            scene.onBeforeRenderObservable.add(() => {
                const time = Date.now() / 1000;
                box.rotation.y = time * 0.5;
                sphere.position.y = 0.5 + Math.sin(time) * 0.2;
                cylinder.rotation.x = time * 0.3;
            });

            return scene;
        },

        // 更新场景信息
        updateSceneInfo() {
            if (this.scene) {
                this.objectCount = this.scene.meshes.length;
            }
        },

        // 窗口大小变化处理
        onWindowResize() {
            if (this.engine) {
                this.engine.resize();
            }
        },
    },
};
</script>

<style scoped>
.babylon-container {
    position: relative;
    width: 100%;
    height: 100%;
}

.canvas-container {
    width: 100%;
    height: 100%;
    overflow: hidden;
}

.render-canvas {
    width: 100%;
    height: 100%;
    display: block;
    outline: none;
}

.loading-overlay {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.7);
    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 1000;
}

.loader {
    color: white;
    font-size: 18px;
}

.scene-info {
    position: absolute;
    bottom: 20px;
    left: 20px;
    background: rgba(0, 0, 0, 0.6);
    color: white;
    padding: 10px 15px;
    border-radius: 5px;
    font-size: 14px;
}

.scene-info p {
    margin: 5px 0;
}
</style>

(2)运行效果如下,立方体、圆柱体会在场景中自动不断地自转,而球体则会沿着 Y 轴上下反复移动。

(3)同时我们可以通过鼠标对三维场景进行直观交互,整体交互体验接近专业三维软件(如 CAD、建模工具)的操作方式:
  • 按住鼠标左键拖动可围绕目标物体旋转视角,从不同角度观察模型。
  • 滚动鼠标滚轮可实现视图的放大与缩小,即相机向目标点靠近或远离。
  • 按住鼠标右键拖动可平移视角,使整个场景在屏幕中上下左右移动。

3,添加阴影

(1)上面样例我们发现,虽然我们在场景里添加了灯光,但是并没有在地面上显示出物体的阴影。这时由于 Babylon.js 的阴影机制需要显式开启,其中:
  • 半球光(HemisphericLight)本身不支持投射阴影,它只用于环境漫反射,看起来“亮”,但永远不会产生阴影。
  • DirectionalLight / SpotLight / PointLight 需要创建 ShadowGenerator,否则也不会有阴影。
(2)下面我对前面的代码做个改进,创建 ShadowGenerator,让物体“投射阴影”,并让地面“接收阴影”。
<template>
    <div class="babylon-container">
        <div ref="canvasContainer" class="canvas-container">
            <canvas ref="renderCanvas" class="render-canvas"></canvas>
        </div>

        <!-- 加载状态 -->
        <div v-if="loading" class="loading-overlay">
            <div class="loader">加载中...</div>
        </div>

        <!-- 场景信息 -->
        <div class="scene-info">
            <p>物体数量: {{ objectCount }}</p>
            <p>FPS: {{ fps.toFixed(1) }}</p>
        </div>
    </div>
</template>

<script>
import * as BABYLON from 'babylonjs';

export default {
    name: "BabylonScene",
    data() {
        return {
            engine: null,
            scene: null,
            loading: true,
            error: null,
            objectCount: 0,
            fps: 60,
        };
    },
    mounted() {
        this.initBabylon();
    },
    beforeDestroy() {
        // 销毁组件时释放内存,防止内存泄漏
        if (this.engine) {
            this.engine.dispose();
        }
        window.removeEventListener("resize", this.onWindowResize);
    },
    methods: {
        initBabylon() {
            const canvas = this.$refs.renderCanvas;

            // 1. 初始化渲染引擎
            this.engine = new BABYLON.Engine(canvas, true,
                { preserveDrawingBuffer: true, stencil: true });

            // 2. 创建场景
            this.scene = this.createScene(this.engine, canvas);

            // 3. 隐藏加载遮罩
            this.loading = false;

            // 4. 注册渲染循环
            this.engine.runRenderLoop(() => {
                this.scene.render();
                this.fps = this.engine.getFps();
            });

            // 5. 监听窗口调整大小
            window.addEventListener("resize", this.onWindowResize);

            // 6. 初始化场景统计
            this.updateSceneInfo();
        },

        createScene(engine, canvas) {
            const scene = new BABYLON.Scene(engine);

            // 设置背景颜色
            scene.clearColor = BABYLON.Color3.FromHexString('#aaaaaa').toColor4();

            // 添加摄像头 (ArcRotateCamera 允许用户绕着中心点旋转)
            const camera = new BABYLON.ArcRotateCamera(
                'camera',
                -Math.PI / 2,
                Math.PI / 3,
                7,
                BABYLON.Vector3.Zero(),
                scene
            );
            camera.attachControl(canvas, true);

            // 设置相机控制
            camera.wheelPrecision = 50;
            camera.lowerRadiusLimit = 2;
            camera.upperRadiusLimit = 50;

            // 添加灯光
            const light1 = new BABYLON.HemisphericLight(
                'light1',
                new BABYLON.Vector3(1, 1, 0),
                scene
            );

            const light2 = new BABYLON.DirectionalLight(
                'light2',
                new BABYLON.Vector3(0, -1, 1),
                scene
            );
            light2.position = new BABYLON.Vector3(5, 10, 5)
            light2.intensity = 0.3

            // 添加阴影生成器(核心)
            const shadowGenerator = new BABYLON.ShadowGenerator(1024, light2)
            shadowGenerator.useBlurExponentialShadowMap = true
            shadowGenerator.blurKernel = 32

            // 添加地面
            const ground = BABYLON.MeshBuilder.CreateGround(
                'ground',
                { width: 5, height: 5 },
                scene
            );
            const groundMaterial = new BABYLON.StandardMaterial('groundMaterial', scene);
            groundMaterial.diffuseColor = new BABYLON.Color3(0.8, 0.8, 0.8);
            ground.material = groundMaterial;

            // 接收阴影
            ground.receiveShadows = true

            // 创建一个立方体
            const box = BABYLON.MeshBuilder.CreateBox("box", { size: 1 }, scene);
            box.position = new BABYLON.Vector3(-1.5, 0.75, 0);
            // 给立方体上色
            const material = new BABYLON.StandardMaterial("boxMat", scene);
            material.diffuseColor = new BABYLON.Color3(0, 0.5, 1);
            box.material = material;

            // 创建一个球体
            const sphere = BABYLON.MeshBuilder.CreateSphere(
                'sphere',
                { diameter: 1 },
                scene
            );
            sphere.position = new BABYLON.Vector3(0, 0.75, 0);
            // 给球体上色
            const sphereMaterial = new BABYLON.StandardMaterial('sphereMaterial', scene);
            sphereMaterial.diffuseColor = new BABYLON.Color3(0.2, 0.8, 0.2);
            sphere.material = sphereMaterial;

            // 创建一个圆柱体
            const cylinder = BABYLON.MeshBuilder.CreateCylinder(
                'cylinder',
                { height: 1, diameter: 1 },
                scene
            );
            cylinder.position = new BABYLON.Vector3(1.5, 0.75, 0);
            // 给圆柱体上色
            const cylinderMaterial = new BABYLON.StandardMaterial('cylinderMaterial', scene);
            cylinderMaterial.diffuseColor = new BABYLON.Color3(0.2, 0.2, 0.8);
            cylinder.material = cylinderMaterial;

            // 投射阴影
            shadowGenerator.addShadowCaster(box)
            shadowGenerator.addShadowCaster(sphere)
            shadowGenerator.addShadowCaster(cylinder)

            // 添加动画
            scene.onBeforeRenderObservable.add(() => {
                const time = Date.now() / 1000;
                box.rotation.y = time * 0.5;
                sphere.position.y = 0.5 + Math.sin(time) * 0.2;
                cylinder.rotation.x = time * 0.3;
            });

            return scene;
        },

        // 更新场景信息
        updateSceneInfo() {
            if (this.scene) {
                this.objectCount = this.scene.meshes.length;
            }
        },

        // 窗口大小变化处理
        onWindowResize() {
            if (this.engine) {
                this.engine.resize();
            }
        },
    },
};
</script>

<style scoped>
.babylon-container {
    position: relative;
    width: 100%;
    height: 100%;
}

.canvas-container {
    width: 100%;
    height: 100%;
    overflow: hidden;
}

.render-canvas {
    width: 100%;
    height: 100%;
    display: block;
    outline: none;
}

.loading-overlay {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.7);
    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 1000;
}

.loader {
    color: white;
    font-size: 18px;
}

.scene-info {
    position: absolute;
    bottom: 20px;
    left: 20px;
    background: rgba(0, 0, 0, 0.6);
    color: white;
    padding: 10px 15px;
    border-radius: 5px;
    font-size: 14px;
}

.scene-info p {
    margin: 5px 0;
}
</style>

(3)运行效果如下: 

附:添加坐标系辅助器

1,样例代码

    下面代码在前面的基础上做个改进,在原点位置添加一个坐标系辅助器(X 轴为红色,Y 轴为绿色,Z 轴为蓝色),方便我们进行定位。
<template>
    <div class="babylon-container">
        <div ref="canvasContainer" class="canvas-container">
            <canvas ref="renderCanvas" class="render-canvas"></canvas>
        </div>

        <!-- 加载状态 -->
        <div v-if="loading" class="loading-overlay">
            <div class="loader">加载中...</div>
        </div>

        <!-- 场景信息 -->
        <div class="scene-info">
            <p>物体数量: {{ objectCount }}</p>
            <p>FPS: {{ fps.toFixed(1) }}</p>
        </div>
    </div>
</template>

<script>
import * as BABYLON from 'babylonjs';

export default {
    name: "BabylonScene",
    data() {
        return {
            engine: null,
            scene: null,
            loading: true,
            error: null,
            objectCount: 0,
            fps: 60,
        };
    },
    mounted() {
        this.initBabylon();
    },
    beforeDestroy() {
        // 销毁组件时释放内存,防止内存泄漏
        if (this.engine) {
            this.engine.dispose();
        }
        window.removeEventListener("resize", this.onWindowResize);
    },
    methods: {
        initBabylon() {
            const canvas = this.$refs.renderCanvas;

            // 1. 初始化渲染引擎
            this.engine = new BABYLON.Engine(canvas, true,
                { preserveDrawingBuffer: true, stencil: true });

            // 2. 创建场景
            this.scene = this.createScene(this.engine, canvas);

            // 3. 隐藏加载遮罩
            this.loading = false;

            // 4. 注册渲染循环
            this.engine.runRenderLoop(() => {
                this.scene.render();
                this.fps = this.engine.getFps();
            });

            // 5. 监听窗口调整大小
            window.addEventListener("resize", this.onWindowResize);

            // 6. 初始化场景统计
            this.updateSceneInfo();

            // 7. 显示坐标系辅助器
            this.showAxisHelper(this.scene);
        },

        createScene(engine, canvas) {
            const scene = new BABYLON.Scene(engine);

            // 设置背景颜色
            scene.clearColor = BABYLON.Color3.FromHexString('#aaaaaa').toColor4();

            // 添加摄像头 (ArcRotateCamera 允许用户绕着中心点旋转)
            const camera = new BABYLON.ArcRotateCamera(
                'camera',
                -Math.PI / 2,
                Math.PI / 3,
                7,
                BABYLON.Vector3.Zero(),
                scene
            );
            camera.attachControl(canvas, true);

            // 设置相机控制
            camera.wheelPrecision = 50;
            camera.lowerRadiusLimit = 2;
            camera.upperRadiusLimit = 50;

            // 添加灯光
            const light1 = new BABYLON.HemisphericLight(
                'light1',
                new BABYLON.Vector3(1, 1, 0),
                scene
            );

            const light2 = new BABYLON.DirectionalLight(
                'light2',
                new BABYLON.Vector3(0, -1, 1),
                scene
            );
            light2.position = new BABYLON.Vector3(5, 10, 5)
            light2.intensity = 0.3

            // 添加阴影生成器(核心)
            const shadowGenerator = new BABYLON.ShadowGenerator(1024, light2)
            shadowGenerator.useBlurExponentialShadowMap = true
            shadowGenerator.blurKernel = 32

            // 添加地面
            const ground = BABYLON.MeshBuilder.CreateGround(
                'ground',
                { width: 5, height: 5 },
                scene
            );
            const groundMaterial = new BABYLON.StandardMaterial('groundMaterial', scene);
            groundMaterial.diffuseColor = new BABYLON.Color3(0.8, 0.8, 0.8);
            ground.material = groundMaterial;

            // 接收阴影
            ground.receiveShadows = true


            // 创建一个立方体
            const box = BABYLON.MeshBuilder.CreateBox("box", { size: 1 }, scene);
            box.position = new BABYLON.Vector3(-1.5, 0.75, 0);
            // 给立方体上色
            const material = new BABYLON.StandardMaterial("boxMat", scene);
            material.diffuseColor = new BABYLON.Color3(0, 0.5, 1);
            box.material = material;

            // 创建一个球体
            const sphere = BABYLON.MeshBuilder.CreateSphere(
                'sphere',
                { diameter: 1 },
                scene
            );
            sphere.position = new BABYLON.Vector3(0, 0.75, 0);
            // 给球体上色
            const sphereMaterial = new BABYLON.StandardMaterial('sphereMaterial', scene);
            sphereMaterial.diffuseColor = new BABYLON.Color3(0.2, 0.8, 0.2);
            sphere.material = sphereMaterial;

            // 创建一个圆柱体
            const cylinder = BABYLON.MeshBuilder.CreateCylinder(
                'cylinder',
                { height: 1, diameter: 1 },
                scene
            );
            cylinder.position = new BABYLON.Vector3(1.5, 0.75, 0);
            // 给圆柱体上色
            const cylinderMaterial = new BABYLON.StandardMaterial('cylinderMaterial', scene);
            cylinderMaterial.diffuseColor = new BABYLON.Color3(0.2, 0.2, 0.8);
            cylinder.material = cylinderMaterial;

            // 投射阴影
            shadowGenerator.addShadowCaster(box)
            shadowGenerator.addShadowCaster(sphere)
            shadowGenerator.addShadowCaster(cylinder)

            // 添加动画
            scene.onBeforeRenderObservable.add(() => {
                const time = Date.now() / 1000;
                box.rotation.y = time * 0.5;
                sphere.position.y = 0.5 + Math.sin(time) * 0.2;
                cylinder.rotation.x = time * 0.3;
            });

            return scene;
        },

        // 添加坐标系辅助器
        showAxisHelper(scene) {
            // X轴 - 红色
            const xAxis = BABYLON.MeshBuilder.CreateLines(
                'xAxis',
                {
                    points: [
                        BABYLON.Vector3.Zero(),
                        new BABYLON.Vector3(5, 0, 0)
                    ]
                },
                scene
            );
            xAxis.color = new BABYLON.Color3(1, 0, 0);

            // Y轴 - 绿色
            const yAxis = BABYLON.MeshBuilder.CreateLines(
                'yAxis',
                {
                    points: [
                        BABYLON.Vector3.Zero(),
                        new BABYLON.Vector3(0, 5, 0)
                    ]
                },
                scene
            );
            yAxis.color = new BABYLON.Color3(0, 1, 0);

            // Z轴 - 蓝色
            const zAxis = BABYLON.MeshBuilder.CreateLines(
                'zAxis',
                {
                    points: [
                        BABYLON.Vector3.Zero(),
                        new BABYLON.Vector3(0, 0, 5)
                    ]
                },
                scene
            );
            zAxis.color = new BABYLON.Color3(0, 0, 1);
        },

        // 更新场景信息
        updateSceneInfo() {
            if (this.scene) {
                this.objectCount = this.scene.meshes.length;
            }
        },

        // 窗口大小变化处理
        onWindowResize() {
            if (this.engine) {
                this.engine.resize();
            }
        },
    },
};
</script>

<style scoped>
.babylon-container {
    position: relative;
    width: 100%;
    height: 100%;
}

.canvas-container {
    width: 100%;
    height: 100%;
    overflow: hidden;
}

.render-canvas {
    width: 100%;
    height: 100%;
    display: block;
    outline: none;
}

.loading-overlay {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.7);
    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 1000;
}

.loader {
    color: white;
    font-size: 18px;
}

.scene-info {
    position: absolute;
    bottom: 20px;
    left: 20px;
    background: rgba(0, 0, 0, 0.6);
    color: white;
    padding: 10px 15px;
    border-radius: 5px;
    font-size: 14px;
}

.scene-info p {
    margin: 5px 0;
}
</style>

2,运行效果

评论

全部评论(0)

回到顶部