Threejs 渲染点云图

文章介绍了如何在Vue2应用中使用Three.js库来渲染点云图,包括安装、组件化、PCD文件加载、模型控制器的使用以及动画和销毁模型的方法。
该文章已生成可运行项目,

 Vue2 为例

#安装 
npm install threejs

完整代码:已封装成组件,只需要通过组件传参数url即可渲染点云图(基础版)

<template>
  <div ref="threeDemo" style="height: 100%; width: 100%">
    <div id="PcdThree" style="height: 100%; width: 100%"></div>
  </div>
</template>

<script>
// 引入Three.js
import * as THREE from "three";

// 引入PCD加载器
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";
// 创建一个时钟对象Clock

var clock = new THREE.Clock();

// 设置渲染频率为30FBS,也就是每秒调用渲染器render方法大约30次

var FPS = 30;

var renderT = 1 / FPS; //单位秒  间隔多长时间渲染渲染一次

// 声明一个变量表示render()函数被多次调用累积时间

// 如果执行一次renderer.render,timeS重新置0

var timeS = 0;
var scene; //
var mesh; //网格模型对象

export default {
  components: {},
  name: "PcdThree",
  props: ['url'],
  data() {
    return {
      animationId: null, //
      isShowLoading: false,

      elem: null,

      camera: null, //相机对象

      renderer: null, //渲染器对象

      loader: null,

      controls: null,

      addURL: "/pcd/CloudPoint_20231017140135.pcd",// 点云图地址

      modelStatus: false,
    };
  },

  beforeDestroy() {
    this.destroyModel();
    window.removeEventListener("resize", this.resMode);
  },

  created() {},

  mounted() {
    // 初始化模型
    this.initModel(this.addURL, "PcdThree");
    window.addEventListener("resize", ()=>this.resMode(this.addURL));
  },
  watch: {
    url: {
      handler: async function (newValue, oldValue) {
        if(newValue){
          this.addURL = newValue;
          if(this.modelStatus){
            this.resMode(newValue);
          }
        }
      },
      immediate: true
    },
  },
  methods: {
    async resMode(value) {
      if(this.controls != null){
        this.controls.removeEventListener("change", this.animate);
      }
      await this.scene.traverse(function (v) {
        if (v.type === "Mesh") {
          v.geometry.dispose();
          v.material.dispose();
        }
      });
      while (this.scene.children.length > 0) {
        this.scene.remove(this.scene.children[0]);
      }

      // 重置渲染器输出画布canvas尺寸
      this.renderer.setSize(this.elem.clientWidth, this.elem.clientHeight);
      // 全屏情况下:设置观察范围长宽比aspect为窗口宽高比
      this.camera.aspect = this.elem.clientWidth / this.elem.clientHeight;
      // 如果相机的一些属性发生了变化,需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
      this.camera.updateProjectionMatrix();
      this.initModelAddress(value);
    },

    initModel(pcdPath, domName) {
      // console.log("开始初始化模型文件");
      this.modelStatus = true;//组件加载完毕

      this.elem = document.getElementById(domName); // 相机CanvasRenderer

      this.camera = new THREE.PerspectiveCamera(
        20, // 视野

        this.elem.clientWidth / this.elem.clientHeight, // 纵横比

        0.5, // 近平面

        1000 // 远平面
      ); // 渲染器

      // 设置相机位置
      this.renderer = new THREE.WebGLRenderer({
        antialias: true,

        alpha: true,
      });
      this.camera.lookAt({
        x: 0,
        y: 0,
        z: 0,
      });

      this.renderer.setClearColor(new THREE.Color(0x303030)); // 背景色

      this.renderer.setSize(this.elem.clientWidth, this.elem.clientHeight);

      this.elem.appendChild(this.renderer.domElement);

      this.scene = new THREE.Scene(); // 场景

      this.loader = new PCDLoader(); //PCD加载器

      // // 创建坐标轴辅助器
      // var axesHelper = new THREE.AxesHelper( 10 );
      // // 添加到场景中
      // this.scene.add( axesHelper );

      // 初始化模型
      // this.initModelAddress(pcdPath);
    },
    initModelAddress(address) {
      console.log("加载PCD文件", this.scene.position);
      const THIS = this; //第一层  捕捉报错
      try {
        //加载PCD文件

        THIS.loader.load(
          address,

          function (points) {
            // console.log("加载成功", points);
            // 模型点位大小
            points.material.size = 0.02;

            points.material.color = new THREE.Color(0x00ffff); // 模型颜色

            THIS.scene.add(points);
            // 构造盒子

            var middle = new THREE.Vector3();

            points.geometry.computeBoundingBox();
            points.geometry.translate(0, 0, 0);

            points.geometry.boundingBox.getCenter(middle);

            // THIS.camera.position.set(Math.PI / ,0,0)

            points.applyMatrix4(
              new THREE.Matrix4().makeTranslation(
                -middle.x / 1,

                -middle.y / 1,

                -middle.z / 1
              )
            ); // 比例

            var largestDimension = Math.max(
              points.geometry.boundingBox.max.x,

              points.geometry.boundingBox.max.y,

              points.geometry.boundingBox.max.z
            );

            THIS.camera.position.x = largestDimension * 1;
            THIS.camera.position.y = largestDimension * 1;
            THIS.camera.position.z = largestDimension * 1;

            THIS.animate();

            // 调用 OrbitControls 插件实现交互式操作
            THIS.controls = new OrbitControls(
              THIS.camera,
              THIS.renderer.domElement
            );

            // 把对象放到坐标原点
            points.geometry.center();

            // 绕轴旋转
            points.geometry.rotateX(Math.PI / 2);
            points.geometry.rotateY(Math.PI / -4);
            // points.geometry.rotateZ(Math.PI / 90);

            // 再把对象放回原来的地方
            points.geometry.translate(middle.x, middle.y, middle.z);

            //调用方式,设置x、y、z轴的旋转
            THIS.controls.addEventListener("change", THIS.animate); // 监听鼠标、键盘事件  放大缩小等 // THIS.isShowLoading = false;
          },

          function (xhr) {
            // console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
          }, //第二层 捕捉报错

          function (error) {
            // THIS.isShowLoading = false;

            THIS.$Message.error("模型地址不对,请稍候再试!");
          }
        );
      } catch (error) {
        // THIS.isShowLoading = false;

        THIS.$Message.error("模型地址不对,请稍候再试!");
      }
    },

    // 监听鼠标、键盘事件  放大缩小等
    animate() {
      this.animationId = requestAnimationFrame(this.animate); //.getDelta()方法获得两帧的时间间隔

      var T = clock.getDelta();

      timeS = timeS + T; // requestAnimationFrame默认调用render函数60次,通过时间判断,降低renderer.render执行频率

      if (timeS > renderT) {
        // 控制台查看渲染器渲染方法的调用周期,也就是间隔时间是多少

        // console.log(`调用.render时间间隔`, timeS * 1000 + "毫秒");

        this.renderer.render(this.scene, this.camera); //执行渲染操作 //renderer.render每执行一次,timeS置0

        timeS = 0;
      }
    }, // 销毁模型

    destroyModel() {
      console.log("销毁模型");
      clearTimeout();

      try {
        this.scene.traverse(function (v) {
          if (v.type === "Mesh") {
            v.geometry.dispose();
            v.material.dispose();
          }
        });
        while (this.scene.children.length > 0) {
          this.scene.remove(this.scene.children[0]);
        }
        this.scene.clear();

        this.renderer.dispose();
        this.renderer.clear();

        this.renderer.forceContextLoss();

        this.renderer.content = null;

        this.controls.removeEventListener("change", this.animate);

        cancelAnimationFrame(this.animationId); // 去除animationFrame

        const gl = this.renderer.domElement.getContext("webgl");

        gl && gl.getExtension("WEBGL_lose_context").loseContext();

        console.log("销毁成功");
      } catch (e) {
        console.log("销毁失败");
      }
    },
  },
};
</script>

本文章已经生成可运行项目
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值