需求很简单,在已有的三维场景中加载工业点云模型,模型如下
点云数据过大,尝试用threejs直接加载结果直接崩了,在cloudcomPare软件中加载发现模型点位高达2500多万,这肯定不行,找方法优化模型(点位去重)。
克隆一份并进行抽稀
tools->other->Remove duplicate points菜单访问。这个工具根据点之间的最小距离删除点云中的重复点。
选择一个或几个点云然后启动这个工具。CloudCompare只需要输入点之间的最小距离这一个参数(默认值非常小):
抽稀后五百多万,满足我们的需求了,模型质量达到需求标准。
接下来我们看看,怎么用three.js把模型加载出来。我们用的是pcd格式,不同格式可以用cloudcomPare转换导出。
话不多说,直接贴代码
<template>
<div style="height:1080px; width:1920px ">
<div id="three" style="height: 100%; width: 100%"></div>
</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"; // 放大缩小旋转等控制操作
var clock = new THREE.Clock();
var FPS = 30;
var renderT = 1 / FPS; //单位秒 间隔多长时间渲染渲染一次
var timeS = 0;
const scene = new THREE.Scene(); // 场景
const loader = new PCDLoader(); //PCD加载器
export default {
components: {},
data() {
return {
animationId: null,
elem: null,
scene: null,
// mesh: null, //网格模型对象
camera: null, //相机对象
renderer: null, //渲染器对象
loader: null,
controls: null,
publicPath: process.env.BASE_URL // public
};
},
beforeDestroy() {
this.destroyModel();
},
mounted() {
// 初始化模型
this.initModel(`/static/models/pcd/matrixaiColorPcd.pcd`, "three")
},
methods: {
initModel(pcdPath, domName) {
this.elem = document.getElementById(domName);
this.camera = new THREE.PerspectiveCamera(
30, // 视野
this.elem.clientWidth / this.elem.clientHeight, // 纵横比
0.1, // 近平面
1000 // 远平面
);
// 渲染器
this.renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
this.renderer.setClearColor(new THREE.Color(0x303030)); // 背景色
this.renderer.setSize(this.elem.clientWidth, this.elem.clientHeight);
this.elem.appendChild(this.renderer.domElement);
const THIS = this;
try {
//加载PCD文件
loader.load(
pcdPath,
function (points) {
points.material.color = new THREE.Color(); // 模型颜色
scene.add(points);
// 构造盒子
var middle = new THREE.Vector3();
points.geometry.computeBoundingBox();
points.geometry.boundingBox.getCenter(middle);
points.applyMatrix4(
new THREE.Matrix4().makeTranslation(
-middle.x,
-middle.y,
-middle.z
)
);
// 比例
var largestDimension = Math.max(
points.geometry.boundingBox.max.x,
points.geometry.boundingBox.max.y,
points.geometry.boundingBox.max.z
);
THIS.camera.position.y = largestDimension * 1;
THIS.animate();
THIS.controls = new OrbitControls(
THIS.camera,
THIS.renderer.domElement
);
THIS.controls.addEventListener("change", THIS.animate); // 监听鼠标、键盘事件 放大缩小等
},
function (xhr) {
// console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
},
//第二层 捕捉报错
function (error) {
console.log(error)
THIS.$Message.error("模型地址不对,请稍候再试!");
}
);
} catch (error) {
console.log(error)
THIS.$Message.error("模型地址不对,请稍候再试!");
}
},
animate() {
this.animationId = requestAnimationFrame(this.animate);
var T = clock.getDelta();
timeS = timeS + T;
if (timeS > renderT) {
this.renderer.render(scene, this.camera); //执行渲染操作
timeS = 0;
}
},
destroyModel() {
clearTimeout();
try {
scene.clear();
this.renderer.dispose();
this.renderer.forceContextLoss();
this.renderer.content = null;
cancelAnimationFrame(this.animationId); // 去除animationFrame
const gl = this.renderer.domElement.getContext("webgl");
gl && gl.getExtension("WEBGL_lose_context").loseContext();
} catch (e) {
console.log("销毁失败");
}
}
}
};
</script>
<style scoped>
</style>