three渲染点云

<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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值