<script setup>
import { ref, getCurrentInstance, onMounted, beforeUnmount } from 'vue'
import * as BABYLON from '@babylonjs/core/Legacy/legacy' // 全部引入
import '@babylonjs/loaders' // 模型加载loader
import * as GUI from '@babylonjs/gui/2D' // 交互组件
const { proxy } = getCurrentInstance()
const emit = defineEmits(['customChange'])
let engine = ref(null)
let scene = ref(null)
let camera = ref(null)
// 模型加载进度百分比
let progress = ref(0)
// 是否完成模型渲染
let isRendering = ref(false)
// 是否展示视频
let showVideo = ref(false)
// 自适应渲染
const engineResize = ()=> {
engine.resize();
}
// 重置模型
const reset = ()=> {
scene.activeCamera.restoreState();
}
onMounted(() => {
let canvas = document.getElementById('canvas');
// 初始化 BABYLON 3D engine
engine = new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true, disableWebGL2Support: false });
// 自定义loading加载效果
function customLoadingScreen() {
console.log('customLoadingScreen creation');
}
customLoadingScreen.prototype.displayLoadingUI = function() {
console.log('customLoadingScreen loading')
};
customLoadingScreen.prototype.hideLoadingUI = function() {
window.document.getElementById('loadingScreen').style.display = 'none';
};
engine.loadingScreen = new customLoadingScreen();
// 初始化一个场景 scene
scene = new BABYLON.Scene(engine);
// 设置背景色透明
scene.clearColor = new BABYLON.Color4(0, 0, 0, 0);
// 初始化相机 camera
camera = new BABYLON.ArcRotateCamera('Camera', 0, 0, 0, new BABYLON.Vector3(0, 0, 0), scene);
/*
* 天空盒
*/
// 创建天空盒
const skybox = BABYLON.Mesh.CreateBox('skyBox', 21000, scene),
skyboxMaterial = new BABYLON.StandardMaterial('skyboxMaterial', scene);
// 关闭掉材质的背面剔除(在盒子内部也可以看到盒子)
skyboxMaterial.backFaceCulling = false;
// 删除盒子上的反射光(天空不会反射太阳)
skyboxMaterial.disableLighting = true;
// 载入天空贴图(CubeTexture是贴图加载器,只能被应用到reflectionTexture)
skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture('textures/sky', scene);
// 修改贴图模式(reflectionTexture是反射贴图,但我们需要天空盒贴图)
skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
skybox.material = skyboxMaterial;
// 设置天空盒跟随相机位置移动(盒子不会收缩)
skybox.infiniteDistance = true;
/*
* 3D模型
*/
// 引入外部obj模型
BABYLON.SceneLoader.Append('babylon/1/', 'model.glb', scene, (object) => {
// 设置默认相机和灯光
scene.createDefaultCameraOrLight(true, true, true);
const light = new BABYLON.HemisphericLight('light', new BABYLON.Vector3(1, 1, 1));
// 设置灯光亮度
light.intensity = 1;
// 镜面反射 漫反射 环境光颜色调整
light.diffuse = new BABYLON.Color3(1, 1, 1);
light.specular = new BABYLON.Color3(1, 1, 1);
// 渲染模型后调整相机角度、位置、观察对象的三维坐标
scene.activeCamera.alpha = 0.0239;
scene.activeCamera.beta = 1.51;
scene.activeCamera.radius = 51.9;
scene.activeCamera.setPosition(new BABYLON.Vector3(51.85, 4.32, 4.45));
scene.activeCamera.setTarget(new BABYLON.Vector3(0.04, 1.4, 3.2));
// 设置横向旋转角度上下限
scene.activeCamera.upperBetaLimit = Math.PI * 0.5;
scene.activeCamera.lowerBetaLimit = 0;
// 设置镜头到目标位置距离半径的最大值
scene.activeCamera.upperRadiusLimit = 102;
// 设置鼠标滚轮灵敏度(数值越小灵敏度越高)
scene.activeCamera.wheelPrecision = 10;
// 控制鼠标平移相机镜头灵敏度(数值越小灵敏度越高|为0的时候取消平移操作)
scene.activeCamera.panningSensibility = 200;
// 存储当前相机状态
scene.activeCamera.storeState();
// 关闭自定义loading效果、展示标题、展示按钮
setTimeout(() => {
engine.hideLoadingUI();
emit('showTitle', true);
isRendering = true;
});
}, (progressEvent) => {
// 设置模型加载进度百分比
progress = (progressEvent.loaded / progressEvent.total).toFixed(0) * 100;
});
// 注册渲染循环 runRenderLoop
engine.runRenderLoop(() => {
scene.render();
});
// 在 DOM 更新后执行回调
nextTick(() => {
console.log('DOM 已更新');
// 注册resize监听事件
window.addEventListener('resize', engineResize);
});
})
beforeUnmount(() => {
// 离开页面销毁resize监听事件
window.removeEventListener('resize', engineResize, false);
})
</script>
<template>
<div :class="isRendering ? 'containor bg' : 'containor'">
<div id="loadingScreen" class="flex_column_center">
<span class="loading"></span>
<span class="progress">{{ progress }}%</span>
<span class="text">3D模型加载中...</span>
</div>
<canvas id="canvas"></canvas>
<div class="btn_list flex_middle" v-if="isRendering">
<el-button type="warning" size="small" @click="reset"><i class="el-icon-refresh"></i> 重置</el-button>
</div>
</div>
</template>
<style lang="scss" scoped>
/*scrollbar styles*/
::-webkit-scrollbar {
width: 12px;
height: 12px;
// border-radius: 100px;
}
::-webkit-scrollbar-thumb {
// border-radius: 100px;
background: var(--color-ref-kl-primary10);
}
::-webkit-scrollbar-track-piece {
// border-radius: 100px;
background: transparent;
}
::-webkit-scrollbar-corner {
background: transparent;
}
/*scrollbar styles*/
#app {
height: 100%;
color: #4b4b4b;
font-size: 13px;
font-family: 'Microsoft YaHei';
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.flex {
display: flex;
}
.flex_center {
@extend .flex;
align-items: center;
}
.flex_left {
@extend .flex_center;
justify-content: flex-start;
}
.flex_right {
@extend .flex_center;
justify-content: flex-end;
}
.flex_middle {
@extend .flex_center;
justify-content: center;
}
.flex_column {
@extend .flex;
flex-direction: column;
justify-content: center;
}
.flex_column_center {
@extend .flex_column;
align-items: center;
}
.public_radius {
border-radius: 8px;
}
.echarts {
height: 100%;
overflow: hidden;
}
.containor {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
&.bg {
background-color: #8ecbe3;
}
#loadingScreen {
position: absolute;
width: 100%;
height: 100%;
.loading {
display: inline-block;
position: relative;
width: 100px;
height: 100px;
border: 8px solid #0934f7;
border-radius: 50%;
animation: rotate 1s linear infinite;
&:after {
position: absolute;
left: 50%;
top: 50%;
width: 110px;
height: 110px;
content: '';
transform: translate(-50%, -50%);
border: 8px solid transparent;
border-bottom-color: #00eaff;
border-radius: 50%;
}
}
.progress {
margin-top: -60px;
color: #6be031;
font-size: 16px;
font-weight: 700;
}
.text {
margin-top: 60px;
color: #f5a327;
font-size: 14px;
}
}
canvas {
width: 100%;
height: 100%;
outline: none;
cursor: pointer;
}
.btn_list {
position: absolute;
bottom: 0;
width: 100%;
height: 50px;
z-index: 99;
button {
margin: 0 15px 0 0;
&:last-child {
margin: 0;
}
}
}
.video_main {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 800px;
height: 500px;
z-index: 999;
video {
outline: none;
}
}
}
</style>
Useful links
- Official web site: www.babylonjs.***
- Online playground to learn by experimentating
- Online sandbox where you can test your .babylon and glTF scenes with a simple drag'n'drop
- Online shader creation tool where you can learn how to create GLSL shaders
- 3DS Max exporter can be used to generate a .babylon file from 3DS Max
- Maya exporter can be used to generate a .babylon file from Maya
- Blender exporter can be used to generate a .babylon file from Blender 3d
- Unity 5 (deprecated) exporter can be used to export your geometries from Unity 5 scene editor(animations are supported)
- glTF Tools by KhronosGroup
"@babylonjs/core": "^5.24.0",
"@babylonjs/gui": "^5.24.0",
"@babylonjs/loaders": "^5.24.0",
"@babylonjs/materials": "^5.24.0",
"@babylonjs/post-processes": "^5.24.0",
"@babylonjs/procedural-textures": "^5.24.0",
"@babylonjs/serializers": "^5.24.0",
"@babylonjs/viewer": "^5.24.0",
参见:
Babylon.js: Powerful, Beautiful, Simple, Open - Web-Based 3D At Its Best
Babylonjs中文网
GitHub - BabylonJS/Babylon.js: Babylon.js is a powerful, beautiful, simple, and open game and rendering engine packed into a friendly JavaScript framework.
Export To Babylon.js