Three.js 开发之加载外部GLTF模型和压缩(一)
Three.js 开发3D智慧楼宇(Vue)在VUE项目里使用three.js搭建智慧园区和楼宇,控制空调、灯光、窗帘的开关等设备,实现智能化楼宇。环境搭建除了vue项目所需要的基本依赖,可用vue-cli脚手架快速搭建不多介绍,我们还需要再额外安装three.js的一些依赖来开发:这边用到了这2两个依赖,一个是整个three.js 的所有资源,一个是three的控件,控制选择缩放的,在packa
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'// 性能监测,类似游戏的FPS
export 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.6
let 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;
// 背景透明 配合 alpha
renderer.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就是解析后的模型,类型是group
scene.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-blog.csdnimg.cn/20210226140733576.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.y
obj.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的坐标值即可。
To be continued…
更多推荐
所有评论(0)