Three.js 开发3D智慧楼宇(Vue)
在VUE项目里使用three.js搭建智慧园区和楼宇,控制空调、灯光、窗帘的开关等设备,实现智能化楼宇。
环境搭建
除了vue项目所需要的基本依赖,可用vue-cli脚手架快速搭建不多介绍,我们还需要再额外安装three.js的一些依赖来开发:
这边用到了这2两个依赖,一个是整个three.js 的所有资源,一个是three的控件,控制选择缩放的,在package.json内,安装方法:cnpm i -D three three-orbit-controls即可。(这里我用的淘宝镜像,没有cnpm 的可直接npm安装,就是速度很慢。。。)
"three": "^0.122.0","three-orbit-controls": "^82.1.0",
three.js 的依赖里面基本包含了three的所有资源,我们直接全部引入即可。因为是ES6模块化形式的开发,我们引入的包都是module形式的。新建一个vue页面,基本代码如下:
<template><div id="box"><div id="my-3d"></div></div></template><script>import * as THREE from 'three/build/three.module'let OrbitControls = require('three-orbit-controls')(THREE)// 控件,控制缩放旋转的import Stats from 'three/examples/jsm/libs/stats.module'// 性能监测,类似游戏的FPSexport default {data() {return {};},methods: {init3D(){// 场景let scene = new THREE.Scene();// 相机设置let camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 500);camera.position.set(5, 25, 30);camera.lookAt(0, 0, 0);// 环境光 能保持整体都是亮点let ambientLight = new THREE.AmbientLight(0x404040)// 点光源 就像灯泡一样的效果 白色灯光 亮度0.6let pointLight = new THREE.PointLight(0xffffff, 0.6);// 将灯光加入到场景中scene.add(ambientLight)// 将灯光加到摄像机中 点光源跟随摄像机移动// 为什么这样做 因为这样可以让后期处理时的辉光效果更漂亮camera.add(pointLight);scene.add(camera);// 初始化渲染器let renderer = new THREE.WebGLRenderer({antialias: true,// 开启抗锯齿alpha: true});// 把自动清除颜色缓存关闭 这个如果不关闭 后期处理这块会不能有效显示renderer.autoClear = false;// 背景透明 配合 alpharenderer.setClearColor(0xffffff, 0);renderer.setPixelRatio(window.devicePixelRatio);renderer.setSize(window.innerWidth, window.innerHeight);// 伽马值启动 更像人眼观察的场景renderer.gammaInput = true;renderer.gammaOutput = true;// 坐标轴var helper = new THREE.AxesHelper(100);scene.add(helper);// 容器let container = document.getElementById('my-3d');container.appendChild(renderer.domElement);// 性能监测let stats = new Stats();container.appendChild(stats.dom);// 控制器的控件初始化let controls;const initControls = ()=> {controls = new OrbitControls(camera, renderer.domElement);controls.enableDamping = false;//是否有惯性controls.target.x = 0;controls.target.y = 0;controls.target.z = 0;controls.enableZoom = true;//缩放controls.autoRotate = false;//自动旋转//设置相机距离原点的最远距离controls.minDistance = 4;//设置相机距离原点的最远距离controls.maxDistance = 400;this.minPolarAngle = 0; this.maxPolarAngle = Math.PI / 2;controls.enablePan = true;//右键拖拽}initControls()let clock = new THREE.Clock();//声明一个时钟对象function render() {renderer.render(scene, camera);requestAnimationFrame(render);controls.update(clock.getDelta())stats.update();}render();},},mounted() {this.init3D()},};</script>
3.这样基本就OK了,打开页面只有一个三维坐标轴,这个坐标应该是遵循右手法则,x轴朝右,y轴朝上,z轴朝屏幕前的你。我们没有放其他玩意,放一个坐标轴做辅助参考的。此外, 尽量不要在data return里面定义sceen,camera之类的初始值,这样vue会对值做绑定遍历之类的操作,造成不必要的内存开销。直接在init3D这个函数内定义,变量直接保存在函数内;
加载外部模型
因为公司项目,肯定是要单独建模,设计师这边建模完毕,用3ds max工具导出指定格式,比如:FBX、OBJ等。这边我们需要解析然后在页面展示出来,在做其他的交互。下面以OBJ格式为例:使用3ds max导出来的时候,设计师会贴好材质图片,加好颜色,导出则会这些文件一起导出来,文件大概如下:lu.obj,lu.mtl,maps文件夹里面存放的是图片,也就是贴图。接下来就是模型解析了。我们的obj格式的模型需要使用objloader和mtlloader两个解析器来解析,同时搭配一个ddsloader来配合使用,这些loader不需要额外安装,只需要从node_modules安装包里面引入即可。也就是在three的包,three的包里面基本包括了所有需要的依赖。import {OBJLoader} from 'three/examples/jsm/loaders/OBJLoader'import {MTLLoader} from 'three/examples/jsm/loaders/MTLLoader'
使用的时候,先解析mtl,在解析obj,我的mtl和obj及贴图都放在/static/model目录下,需要注意一点:
1、所有的文件都不能用中文命名,不然会出现乱码; 2、贴图得图片引用路径一定要正确,不然解析不到。可以在编辑器打开obj/mtl文件查看下路径和名称是不是正确的,路径不支持中文!。
即可拿得到解析后的文件:
// 加载外部模型function loadBuildings(url){let mtlLoader = new MTLLoader();mtlLoader.load(`/static/model/${url}.mtl`, function(materials) {materials.preload();let objLoader = new OBJLoader();objLoader.setMaterials(materials);objLoader.load(`/static/model/${url}.obj`, function(obj) {console.log(obj) // 这个obj就是解析后的模型,类型是groupscene.add(obj);// 把解析后的模型加入到场景里面
这样检查完毕,就可以看到页面效果了。
OBJ格式转GLTF,并且压缩
网页中最适合的外部引入的格式是GLTF,我们使用3ds max导出来的是obj格式,可以用插件来转换,并且实现超高压缩比。600k => 20k !
1、首先安装obj2gltf;该插件的作用是把obj格式转为gltf格式。下面开始安装,建议全局安装,这样下次在其他文件夹或者目录下都可以用命令行来实现格式转换。
cnpm i -g obj2gltf
2、接着安装gltf-pipeline;该插件的作用是把现有的gltf格式模型进行压缩,跟图片压缩一样。减少大小,更快的加载。
cnpm i -g gltf-pipeline
3、两个都安装成功后,接下来就可以使用了。
如图:我要对这个lu.obj进行转格式,然后再压缩。(maps文件夹里面是材质图片,隶属于mtl)
输入命令:(参数:-i 是输入路径,-o 是输出路径,其他参数可参考插件官方文档,根据自己的需要添加即可,输入输出的路径一定要正确,不然会报找不到directory之类的错误)
obj2gltf -i ./static/model/park/lu.obj -o ./static/model/park/after/lu.gltf -u
得到结果:
4、下面来进行压缩,输入命令:
gltf-pipeline -i ./static/model/park/after/lu.gltf -o ./static/model/park/after/lu_mini.gltf -d
最后在after文件内会多出lu_mini.gltf 和lu_mini.bin文件。此时lu_mini.gltf是我们最终想要的压缩后的模型文件了。一般文件越大,压缩效果越好。(参数-d 是使用draco压缩算法)
5、解析GLTF格式模型:
因为刚才转换格式并且压缩了,所以解析要新加一个DRACO压缩算法模型解析器。在顶部解析器再引入两个解析器
import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader'import {DRACOLoader} from 'three/examples/jsm/loaders/DRACOLoader'
这里把压缩算法和gltf两个解析器绑定在一起,即可解析压缩后的gltf模型,从而可将模型添加至scene中:
![draco算法所依赖的文件](https://img-/0226140733576.png#pic_center)// 加载外部模型,转GLTF压缩后的function loadMiniModel(index,url) {let gltfLoader = new GLTFLoader();let dracoLoader = new DRACOLoader();dracoLoader.setDecoderPath( '/static/libs/draco/gltf/' );// 这个是加载draco算法,这样才能解析压缩后的gltf模型格式.gltfLoader.setDRACOLoader( dracoLoader );gltfLoader.load(`/static/model/obj/compress/${url}.gltf`, function(obj) {console.log(obj)// 这个obj就是解析后的模型,可添加到scene内。scene.add(obj.scene);
这样,格式转换和压缩也做好了。
使用tween.js实现相机视角切换动画、mesh位置改变动画
1、安装tween动画库:
cnpm i -D @tweenjs/tween.js
页面引入:
const TWEEN = require('@tweenjs/tween.js')
在业务代码里调用:(某个mesh挪到参数带来的位置)
function animateCamera(obj,end){let option = {// 执行动画的物体的初始位置x: obj.position.x,y: obj.position.y,z: obj.position.z,};let tween = new TWEEN.Tween(option).to({x: end.x,// 结束位置,相机固定cx,cy,cz三个位置,mesh通过传参y: end.y,z: end.z,},2000).onUpdate(function(e){obj.position.x = e.x// 这里面是一直在变动的,所以在这里设置位置值,e是参数对象,里面保存着动态的值obj.position.y = e.yobj.position.z = e.z}).delay(100)tween.onComplete(function(){// 动画执行完毕的回调// this.orbitControl.enabled = true;// loadBuildings(3,15,0,0.05, 'new/fan-1','fan')})tween.easing(TWEEN.Easing.Cubic.InOut);// 动画形式,tween.start();// 开始}
这样,在其他地方调用animateCamera()这个函数,传入一个mesh对象和一组x,y,z的坐标值即可。