<template>
<div ref="container" class="pcd-container" v-loading="loading"></div>
</template>
<script>
import * as THREE from 'three';
import { PCDLoader } from 'three/examples/jsm/loaders/PCDLoader.js'; // 注意是examples/jsm
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; // 放大缩小旋转等控制操作
import { GUI } from 'three/examples/jsm//libs/lil-gui.module.min.js';
export default {
data() {
return {
container: null,
gui: null,
scene: null, // 场景
renderer: null, // 渲染器
canvas: null,
camera: null, // 相机
modalData: null, // 加载的模型
loading: false
};
},
destroyed() {
this.destroyModel();
},
methods: {
onWindowResize() {
this.camera.aspect = this.container.clientWidth / this.container.clientHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
this.render();
},
init() {
// 获取容器
this.container = this.$refs.container;
console.log(1111, this.container.clientWidth);
// 创建渲染器
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
this.renderer.setPixelRatio(window.devicePixelRatio);
// 渲染器挂载
this.container.appendChild(this.renderer.domElement);
// 创建场景
this.scene = new THREE.Scene();
// 创建相机
/**
* 创建透视投影相机,该构造函数总共有四个参数,分别是fov,aspect,near,far 。fov表示摄像机视锥体垂直视野角度,最小值为0,最大值为180,默认值为50,实际项目中一般都定义45,因为45最接近人正常睁眼角度;aspect表示摄像机视锥体长宽比,默认长宽比为1,即表示看到的是正方形,实际项目中使用的是屏幕的宽高比;near表示摄像机视锥体近端面,这个值默认为0.1,实际项目中都会设置为1;far表示摄像机视锥体远端面,默认为2000,这个值可以是无限的,说的简单点就是我们视觉所能看到的最远距离。
*/
this.camera = new THREE.PerspectiveCamera(45, this.container.clientWidth / this.container.clientHeight, 1, 100000);
this.camera.position.set(70, 0, 0);
this.scene.add(this.camera);
// 创建控制器
const controls = new OrbitControls(this.camera, this.renderer.domElement);
controls.addEventListener('change', () => {
this.render();
});
window.addEventListener('resize', this.onWindowResize);
},
/**
* @description: 渲染点云数据
* @param {*}
* @author: wyh
* @Date: 2024-08-02 15:32:14
*/
initCloud() {
this.clearModel();
const material = new THREE.PointsMaterial({
color: 0xff0000, // 模型颜色
size: 0.05 // 模型大小
}); // 配置模型的材质对象
this.geometry = new THREE.BufferGeometry(); // 创建图形对象
var vertices = new Float32Array(); // 创建图形的顶点对象
this.geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
// 点云对象
this.pointCloud = new THREE.Points(this.geometry, material);
this.scene.add(this.pointCloud);
/**
* 创建坐标轴
*
* */
// 创建一个AxesHelper对象,长度为1 通常 X 轴是红色,Y 轴是绿色,Z 轴是蓝色。
const axesHelper = new THREE.AxesHelper(5000);
// 将AxesHelper添加到场景中
this.scene.add(axesHelper);
this.render();
},
/**
* @description: 更新点云数据
* @param {*}
* @author: wyh
* @Date: 2024-08-02 16:59:27
*/
updateCloud(points) {
if (this.pointCloud) {
this.pointCloud.geometry.setAttribute('position', new THREE.Float32BufferAttribute(points, 3));
this.render();
} else {
this.initCloud();
}
},
/**
* @description: 渲染模型
* @param {*}
* @author: wyh
* @Date: 2024-06-20 10:35:40
*/
initModel(modalData) {
this.clearModel();
this.modalData = modalData;
// 开发环境跨级问题
if (process.env.NODE_ENV !== 'production') {
const tempUrls = modalData.pcdPath.replace('http://', '').split('/');
tempUrls.splice(0, 1);
const tempUrl = '/PCD_URL/' + tempUrls.join('/');
this.modalData.pcdPath = tempUrl;
}
this.loadModal();
},
loadModal() {
const loader = new PCDLoader();
this.loading = true;
loader.load(this.modalData.pcdPath, (points) => {
this.loading = false;
/**
* 设置不同颜色
// 访问点的位置数据(如果需要的话)
// const positions = points.geometry.attributes.position.array;
// // 我们有 pointsCount 个点
// var pointsCount = points.geometry.attributes.position.count;
// 注意:不是所有的 PCD 文件都包含颜色数据
// if (points.geometry.attributes.color) {
// const colors = points.geometry.attributes.color.array;
// // 修改局部颜色的逻辑
// // 例如,将所有在特定区域内的点设置为红色
// for (let i = 0; i < pointsCount; i++) {
// const x = positions[i * 3];
// const y = positions[i * 3 + 1];
// const z = positions[i * 3 + 2];
// if (x > this.modalData.min.x && x < this.modalData.max.x && y > this.modalData.min.y && y < this.modalData.max.y && z > this.modalData.min.z && z < this.modalData.max.z) {
// // 设置颜色为红色(RGB 255, 0, 0)
// colors[i * 3] = 255;
// colors[i * 3 + 1] = 0;
// colors[i * 3 + 2] = 0;
// }
// }
// } else {
// var colors = new Float32Array(pointsCount * 3).fill(255);
// // 修改局部颜色的逻辑
// // 例如,将所有在特定区域内的点设置为红色
// for (let i = 0; i < pointsCount; i++) {
// const x = positions[i * 3];
// const y = positions[i * 3 + 1];
// const z = positions[i * 3 + 2];
// if (x > this.modalData.min.x && x < this.modalData.max.x && y > this.modalData.min.y && y < this.modalData.max.y && z > this.modalData.min.z && z < this.modalData.max.z) {
// // 设置颜色为红色(RGB 255, 0, 0)
// colors[i * 3] = 255;
// colors[i * 3 + 1] = 0;
// colors[i * 3 + 2] = 0;
// }
// }
// const colorAttribute = new THREE.Float32BufferAttribute(colors, 3); // 3 是每个顶点的分量数(RGB)
// points.geometry.setAttribute('color', colorAttribute);
// // 设置颜色
// points.material.vertexColors = true;
// }
*/
points.geometry.center();
points.geometry.rotateX(Math.PI);
this.scene.add(points);
});
},
/**
* @description: 添加控制器
* @param {*}
* @author: wyh
* @Date: 2024-06-26 14:49:14
*/
addGui() {
if (this.gui) {
return;
}
// 实例化一个gui对象
this.gui = new GUI({ autoPlace: false }); // 设置 autoPlace 为 false
// 改变交互界面style属性
this.gui.domElement.style.right = '0px';
this.gui.domElement.style.width = '300px';
this.gui.domElement.style.top = '0';
this.gui.domElement.style.position = 'absolute';
// gui界面上增加交互界面,摄像头位置
this.gui.add(this.camera.position, 'x', 0, 100).onChange(this.render);
this.gui.add(this.camera.position, 'y', -10, 10).onChange(this.render);
this.gui.add(this.camera.position, 'z', -10, 10).onChange(this.render);
this.gui.open();
// 将 GUI 添加到容器中
this.container.appendChild(this.gui.domElement);
},
/**
* @description: 清除模型
* @param {*}
* @author: wyh
* @Date: 2024-06-20 10:35:51
*/
clearModel() {
if (this.scene) {
this.scene.traverse((child) => {
if (child.material) {
child.material.dispose();
}
if (child.geometry) {
child.geometry.dispose();
}
child = null;
});
this.scene.clear();
}
},
render() {
this.renderer.render(this.scene, this.camera);
},
/**
* @description: 初始化一个盒子
* @param {*}
* @author: wyh
* @Date: 2024-06-20 10:58:30
*/
initBox() {
// 初始化边界值
const { min, max } = this.modalData;
// 创建包围盒的几何体和材质
const boxGeometry = new THREE.BoxGeometry(
max.x - min.x, // 宽度
max.y - min.y, // 高度
max.z - min.z // 深度
);
const boxMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000, transparent: true, opacity: 0.2 });
// 创建线框材质
const boundingBox = new THREE.Mesh(boxGeometry, boxMaterial);
// 将包围盒移动到点云的中心位置(可选)
boundingBox.position.set((min.x + max.x) / 2, (min.y + max.y) / 2, (min.z + max.z) / 2);
// 添加到场景中
this.scene.add(boundingBox);
},
destroyModel() {
try {
if (this.renderer) {
// 清除渲染器
this.renderer.dispose();
// 释放内存
this.renderer.forceContextLoss();
this.renderer.content = null;
this.renderer = null;
this.camera = null;
// 清除场景
this.scene.traverse((child) => {
if (child.material) {
child.material.dispose();
}
if (child.geometry) {
child.geometry.dispose();
}
child = null;
});
this.scene.clear();
this.scene = null;
this.container.innerHTML = '';
this.gui = null;
}
} catch (e) {
console.log('销毁失败');
}
}
}
};
</script>
<style lang="scss" scoped>
.pcd-container {
width: 100%;
height: 100%;
position: relative;
}
</style>
three渲染点云
最新推荐文章于 2025-01-16 11:29:47 发布