three+vue点云模型加载

需求很简单,在已有的三维场景中加载工业点云模型,模型如下:
在这里插入图片描述
点云数据过大,尝试用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>
    

经过一些尝试和研究了threejs的PCDLoader以后,发现其加载性能还是不够友好,所以我重写了loader,目前支持多片点云模型加载,点云规模在5000万左右。

附上loader的代码,希望能对正在阅读这篇文章的你有所帮助。

function PCDLoader(manager) {
    THREE.Loader.call(this, manager);
    this.littleEndian = true;
}

PCDLoader.prototype = Object.create(THREE.Loader.prototype);
PCDLoader.prototype.constructor = PCDLoader;

PCDLoader.prototype.load = function (url, onLoad, onProgress, onError) {
    const scope = this;

    const loader = new THREE.FileLoader(scope.manager);
    loader.setPath(scope.path);
    loader.setResponseType("arraybuffer");
    loader.setRequestHeader(scope.requestHeader);
    loader.setWithCredentials(scope.withCredentials);
    loader.load(
        url,
        function (data) {
            try {
                onLoad(scope.parse(data, url));
            } catch (e) {
                handleLoadError(e, onError);
                scope.manager.itemError(url);
            }
        },
        onProgress,
        function (err) {
            handleLoadError(err, onError);
            scope.manager.itemError(url);
        }
    );
};

PCDLoader.prototype.parse = function (data, url) {
    function handleLoadError(err, onError) {
        if (onError) {
            onError(err.message || err);
        } else {
            console.error(err);
        }
    }

    // ... (前面的函数保持不变)

    function createBufferGeometry(position, normal, color) {
        const geometry = new THREE.BufferGeometry();

        if (position.length > 0) {
            geometry.setAttribute("position", new THREE.Float32BufferAttribute(position, 3));
        }
        if (normal.length > 0) {
            geometry.setAttribute("normal", new THREE.Float32BufferAttribute(normal, 3));
        }
        if (color.length > 0) {
            geometry.setAttribute("color", new THREE.Float32BufferAttribute(color, 3));
        }

        geometry.computeBoundingSphere();
        return geometry;
    }

    // ... (其余的代码)

    const geometry = createBufferGeometry(position, normal, color);
    const material = new THREE.PointsMaterial({ size: 0.005 });

    if (color.length > 0) {
        material.vertexColors = true;
    } else {
        material.color.setHex(Math.random() * 0xffffff);
    }

    const mesh = new THREE.Points(geometry, material);

    const reversedName = url.split("").reverse().join("");
    const reversedWithoutSlash = /([^\/]*)/.exec(reversedName);
    const reversedNameWithoutSlash = reversedWithoutSlash && reversedWithoutSlash[1];
    const name = reversedNameWithoutSlash ? reversedNameWithoutSlash.split("").reverse().join("") : "";

    mesh.name = name;

    return mesh;
};

// 导出PCDLoader类
window.PCDLoader = PCDLoader;

  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋名山大前端

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值