Three.js+vue模型加载(点云)

3,914 阅读1分钟

需求很简单,在已有的三维场景中加载工业点云模型,模型如下

image.png 点云数据过大,尝试用threejs直接加载结果直接崩了,在cloudcomPare软件中加载发现模型点位高达2500多万,这肯定不行,找方法优化模型(点位去重)。

image.png 克隆一份并进行抽稀

image.png

image.png

tools->other->Remove duplicate points菜单访问。这个工具根据点之间的最小距离删除点云中的重复点。

选择一个或几个点云然后启动这个工具。CloudCompare只需要输入点之间的最小距离这一个参数(默认值非常小):

image.png

抽稀后五百多万,满足我们的需求了,模型质量达到需求标准。

接下来我们看看,怎么用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>

image.png