vue使用three.js 加载glb,gltf,obj,并做点击处理
·
three文档:Three.js电子书《Three.js零基础入门教程》_郭隆邦
先看代码
template>
<div class="three" v-loading="loading" element-loading-text="拼命加载中" element-loading-spinner="el-icon-loading" element-loading-background="rgba(0, 0, 0, 0.8)">
<!-- 检查站模型 -->
<div id="three_div" @click="onMouseClick"></div>
</div>
</template>
<script>
import * as THREE from 'three';
import { OBJLoader, MTLLoader } from 'three-obj-mtl-loader'; //obj
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; //gltf
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'; //gltf
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
// 引入桑基图
export default {
name: 'three',
components: {
},
data() {
return {
scene: null, //场景对象实例
camera: null, //相机实例
renderer: null, //渲染器实例
renderEnabled: true,
timer: null,//定时器
objectModal: null, //模型实例
mixer: null,//混合器实例
clock: new THREE.Clock(),//时钟对象
loading:true
};
},
beforeDestroy() {
this.clearCache(this.objectModal); //清除模型
this.clearRenderer(); //清除模型
window.removeEventListener('resize', this.ResizeFun, false); //监听页面的变化
},
methods: {
ResizeFun() {
let container = document.getElementById('three_div');
this.camera.aspect = container.clientWidth / container.clientHeight;
this.camera.updateProjectionMatrix(); // 如果相机的一些属性发生了变化,需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
this.renderer.setSize(container.clientWidth, container.clientHeight);
},
timeRender() {
this.renderEnabled = true;
if (this.timer) {
clearTimeout(this.timer);
}
this.timer = setTimeout(() => {
this.renderEnabled = false;
}, 3000);
},
clearCache(obj) {
obj.geometry.dispose();
obj.material.dispose();
},
clearRenderer() {
if (this.renderer) {
this.renderer.dispose();
this.renderer.forceContextLoss();
this.renderer.context = null;
this.renderer.domElement = null;
this.renderer = null;
}
},
// 初始化场景
init() {
let container = document.getElementById('three_div');
//场景
this.scene = new THREE.Scene();
// 2、创建摄像机 PerspectiveCamera('视角', '指定投影窗口长宽比', '从距离摄像机多远开始渲染', '截止多远停止渲染100')
this.camera = new THREE.PerspectiveCamera(
75,
container.clientWidth / container.clientHeight,
100,
10000
);
this.camera.position.set(750, 150, 500); //设置相机位置
this.camera.lookAt(this.scene.position); //设置相机方向(指向的场景对象)
// 渲染器
this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); //创建渲染器对象
this.renderer.setSize(container.clientWidth, container.clientHeight); //设置渲染区域尺寸
this.renderer.setClearColor(0xeeeeee, 0); //设置背景颜色
container.appendChild(this.renderer.domElement); //body元素中插入canvas对象
//控制器
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
this.controls.addEventListener('change', () => {
// this.timeRender();
}); //监听页面的缩放,拖拽,旋转
this.controls.target.set(400, -200, 0);//设置加载位置 左右 上下 前后
this.controls.minDistance = 100; //设置相机距离原点的最远距离
this.controls.maxDistance = 1000; //设置相机距离原点的最远距离
this.controls.maxPolarAngle = Math.PI / 1; //设置可旋转的范围
this.controls.enableZoom = true; //是否可以缩放
this.controls.enablePan = true; //是否可拖拽
this.controls.update();
// 三维坐标轴
// var axis = new THREE.AxisHelper(3);//定位中心点
// this.scene.add(axis);
},
//给模型添加点击事件za
onMouseClick(event) {
let raycaster = new THREE.Raycaster();
let mouse = new THREE.Vector2();
let div3D = document.getElementById('three_div');
// 通过鼠标点击的位置计算出raycaster所需要的点的位置,以屏幕中心为原点,值的范围为-1到1.
let div3DLeft = div3D.getBoundingClientRect().left;
let div3DTop = div3D.getBoundingClientRect().top;
mouse.x = (event.clientX - div3DLeft) / div3D.clientWidth * 2 - 1;
mouse.y = -((event.clientY - div3DTop) / div3D.clientHeight) * 2 + 1;
// 通过鼠标点的位置和当前相机的矩阵计算出raycaster
raycaster.setFromCamera(mouse, this.camera);
// 获取raycaster直线和所有模型相交的数组集合
this.raycastMeshes(this.clickApp, raycaster);
},
raycastMeshes(callback, raycaster) {
let intersects = [];
// 获取整个场景
let theScene = this.scene || new THREE.Scene();
// 获取鼠标点击点的射线
let theRaycaster = raycaster || new THREE.Raycaster();
// 对场景及其子节点遍历
for (let i in theScene.children) {
// 如果场景的子节点是Group或者Scene对象
if (
theScene.children[i] instanceof THREE.Group ||
theScene.children[i] instanceof THREE.Scene
) {
// 场景子节点及其后代,被射线穿过的模型的数组集合
// intersects = theRaycaster.intersectObjects(theScene.children[i].children, true)
let rayArr = theRaycaster.intersectObjects(
theScene.children[i].children,
true
);
intersects.push(...rayArr);
} else if (theScene.children[i] instanceof THREE.Mesh) {
// 如果场景的子节点是Mesh网格对象,场景子节点被射线穿过的模型的数组集合
// intersects.push(theRaycaster.intersectObject(theScene.children[i]))
}
}
intersects = this.filtersVisibleFalse(intersects); // 过滤掉不可见的
// 被射线穿过的模型的数组集合
if (intersects && intersects.length > 0) {
return callback(intersects);
}
// this.hiddenDetailDiv()
return null;
},
// 有些对象是包含了很多children的所以需要遍历,并push到intersects数组去重。
filtersVisibleFalse(arr) {
let arrList = arr;
if (arr && arr.length > 0) {
arrList = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i].object.visible) {
arrList.push(arr[i]);
}
}
}
return arrList;
},
// 点击到位置的
clickApp(intersects) {
if (intersects[0].object !== undefined) {
console.log(intersects[0].object, '这就是成功点击到的对象了~');
}
},
// 加载模型obj+mtl
loadObj() {
// let manager = new THREE.LoadingManager();
// // eslint-disable-next-line no-undef
// new MTLLoader(manager).load('/static/models/c4d视频文件.mtl', (materials) => {
// materials.preload();
// // eslint-disable-next-line no-undef
// new OBJLoader(manager)
// .setMaterials(materials)
// .load('/static/models/c4d.obj', (obj) => {
// //初始展示的模型的缩放比例
// obj.scale.set(1, 1, 1);
// this.objectModal = obj;
// this.scene.add(obj);
// this.timeRender();
// });
// });
// 加载单个obj
let objloader = new OBJLoader();
objloader.load('./static/models/male02.obj', (obj) => {
//初始展示的模型的缩放比例
obj.scale.set(1, 1, 1);
this.objectModal = obj;
this.scene.add(obj);
this.timeRender();
});
},
//加载gltf模型
loadgltf() {
let loader = new GLTFLoader();
let dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('./static/gltf/');// 这个是加载draco算法,这样才能解析压缩后的gltf模型格式.
loader.setDRACOLoader(dracoLoader);
return new Promise(() => {
loader.load('./static/models/zzb.glb', (gltf) => {
this.loading=false;
//初始展示的模型的缩放比例
gltf.scene.scale.set(1, 1, 1);
this.objectModal = gltf;
this.scene.add(gltf.scene);
//骨骼网格模型作为参数创建一个混合器
this.mixer = new THREE.AnimationMixer(gltf.scene);
const clip = gltf.animations[0];//将第1帧动画设置为动画剪辑对象
const action = this.mixer.clipAction(clip);//使用动画剪辑对象创建AnimationAction对象
this.mixer.timeScale = 0.5;//默认1,可以调节播放速度
action.setDuration(2).play();//设置单此循环的持续时间(setDuration也可以调节播放速度)并开始动画
// this.timeRender();
});
});
},
// 灯光
light() {
let ambientLight = new THREE.AmbientLight(0xffffff); //环境光
this.scene.add(ambientLight);
// eslint-disable-next-line vars-on-top
var directionalLight = new THREE.DirectionalLight(0xfffffe, 2); //点散光
directionalLight.position.set(100, 100, 100);
directionalLight.position.multiplyScalar(0.3);
this.scene.add(directionalLight);
//点光源
let point = new THREE.PointLight(0xffffff);
point.position.set(400, 200, 300); //点光源位置
this.scene.add(point); //点光源添加到场景中
},
// 动画效果
animate() {
if (this.renderEnabled) {
if (this.renderer) {
//执行渲染操作 指定场景、相机作为参数
this.renderer.render(this.scene, this.camera);
}
}
requestAnimationFrame(this.animate);
if (this.mixer) {
const delta = this.clock.getDelta(); // 获取自上次调用的时间差
this.mixer.update(delta);
}
}
},
mounted() {
this.init();
this.light();
this.loadgltf();
this.animate();
}
};
</script>
一般就加载glb的模型 因为它是支持动画的,我这里的模型是压缩过的不然原始文件很大的浏览器会很卡的,压缩过的必须用draco算法,这样才能解析压缩后的gltf模型格式,压缩相关请看这里
模型要放到静态文件里边不然会加载不出来的。模型点击事件有点特殊相关注释也写了,有任何问题请留言或者私信我。
更多推荐
已为社区贡献1条内容
所有评论(0)