前端菜鸡流水账日记 -- threejs和cesium

哈喽哇大家,今天来点不一样的,主要是因为今天没有后台系统的修改,所作的修改是在以cesium为基础的项目上,用threejs渲染一个模型,并且可以具有显示/隐藏的功能,那下边就让我们来看看怎么实现的把~

---------------------------------------------------------------------------------------------------------------------------------

思路:

1.下载对应的threejs库,并且引入

2.进行基础配置,比如相机、光照等等

3.引入模型

4.调整模型的位置大小

5.来进行显示/隐藏的操作

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

详细的代码:

1.下载

npm i threejs  //下载threejs库

// 引入threejs
import * as THREE from 'three';

//解码器的引入,如果不需要可以不写,写的话需要注意路径
import { GLTFLoader } from 'three/examples/jsm/Addons.js';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';

2.配置初始化(有一段可以不需要加,如果需要调整的话在加,可以根据需要选择,我的代码是用到了的,所以选择一起分享出来了)

// 初始化场景
const threeRenderer = new THREE.WebGLRenderer(); // 创建Three.js的渲染器

// 设置渲染器尺寸
threeRenderer.setSize(window.innerWidth, window.innerHeight);

// 将渲染器的canvas添加到DOM中
document.body.appendChild(threeRenderer.domElement);

//------------------------------------------- 

//这一段如果不是cesium和threejs相结合的可以不用加,主要是为了让两者的场景一开始就渲染到某一个位置以及视角的角度的调整,正常的只需要调整下边的相机就可以了

const cesiumCamera = viewer.camera;  // 管理cesium的场景视角和投影
    // 设置Cesium相机到一个初始位置
    console.log(cesiumCamera, 'cececeeeeeeeeeeee')
    // x: 116.646994,
    // z: 35.421154
    viewer.camera.flyTo({
      // destination: Cesium.Cartesian3.fromDegrees(0, 0, 1000), // 示例位置为经度0,纬度0,高度1000米
      // destination: Cesium.Cartesian3.fromDegrees(116.646994, 0, 35.421154), // 示例位置为经度0,纬度0,高度1000米
      // 飞过去了  ---高度原来为2000
      // destination: Cesium.Cartesian3.fromDegrees(-74.006015, 40.712728, 2000), // 经度-74.006015,纬度40.712728,高度2000米(纽约市中心)      // destination: position,
      destination: Cesium.Cartesian3.fromDegrees(116.612584231, 35.420733404, 2000), // 经度-74.006015,纬度40.712728,高度2000米(纽约市中心)      // destination: position,
      // ----------------
      orientation: {
        heading: Cesium.Math.toRadians(0), // 偏航角,0表示正北
        pitch: Cesium.Math.toRadians(-15), // 俯仰角,负数表示向上看,这里设置为-45度
        roll: Cesium.Math.toRadians(0), // 保持相机的翻滚角不变
      },
      // ----------------
      // 添加延时确保相机到位
      // duration: 0, // 立即到达  0改为1  ---改成1 就会变成灰色背景,不展示cesium渲染的东西
      duration: 1, // 延时1秒
    });
  
    
    document.getElementById('modelView').appendChild(threeRenderer.domElement);
//-------------------------------------------

// 初始化threejs的场景、相机
const threeScene = new THREE.Scene();  //创建新的场景,是指所有三维物体和相机要存在得空间
// 创建相机,透视相机,视野角度为75 
const threeCamera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
  
// threeCamera.position.set(100,200,600)  //默认相机位置,三维空间中得这些坐标上  //旧
threeCamera.position.z = 1000; // 初始设置相机位置 //新
threeCamera.lookAt(new THREE.Vector3(0, 0, 0)); // 相机看向场景中心 //新

// 添加环境光
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); // 色温和强度可调整
threeScene.add(ambientLight);

3-4.引模型和调整大小(引入的这个模型是,展示在cesium场景中的模型,和普通的threejs引入还不太一样,用到了创建cesium的实体,如果不创建实体,其实就是threejs的模型类型,但是只有创建实体才可以在ceisum场景中看到,所以可以根据需要选择,另外就是,模型的路径要换成自己的)

 // 使用GLTFLoader加载模型
    const loader = new GLTFLoader();
     // 加载draco解码器
     const dracdloader = new DRACOLoader();
     dracdloader.setDecoderPath('./draco/glft/');

    loader.setDRACOLoader(dracdloader);
    let model;
    let entity2;
    loader.load(
      // 资源路径 -- 要对应自己代码的路径
      '../models/car.glb', 
      // 加载成功回调
      function (gltf) {
        // 获取模型在地球上的笛卡尔位置
        // let longitude = -74.006015; // 例如:东经104.06度
        // let latitude = 40.712728;  // 例如:北纬30.67度
        // let height = -1000;   // 模型相对于海平面的高度(单位:米)
        let longitude = 116.612584231; // 例如:东经104.06度
        let latitude = 35.420733404;  // 例如:北纬30.67度
        let height = 2000;   // 模型相对于海平面的高度(单位:米)

        const cartesianPosition = Cesium.Cartesian3.fromDegrees(longitude, latitude, height);
    
        // 创建Cesium实体
        const entity = viewer.entities.add({
          position: cartesianPosition,
          model: {
            uri: '../models/car.glb', // 使用加载的模型URL
            scale: 100.0, // 根据需要调整模型大小
            heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, // 让模型贴合地面
          },
        });

        console.log('Model materials:', entity.model.material); // 打印模型材料信息
        
        // ----------------点击事件
        
        // 创建一个ScreenSpaceEventHandler实例来处理点击事件
        const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);

        // 定义点击事件处理函数
        function onLeftClick(movement) {
          const pickedObject = viewer.scene.pick(movement.position);
          if (Cesium.defined(pickedObject) && pickedObject.id === entity) {
            // 当点击的实体是我们刚刚添加的模型实体时,弹出提示框
            alert('模型被点击了!');
            console.log('点击了模型',entity2)
            viewer.entities.remove(entity2);
            entity2 = undefined; // 可选:清除引用以帮助垃圾回收
            console.log('entity2',entity2)
            // entity2.show = false  //可行
            // entity2.show = !entity2.show
          }
        }

          // 注册鼠标左键点击事件
          handler.setInputAction(onLeftClick, Cesium.ScreenSpaceEventType.LEFT_CLICK);
        // -----------------------

        // 删除Three.js的模型渲染
        threeScene.remove(model);
        document.getElementById('modelView').removeChild(threeRenderer.domElement);
        threeRenderer.dispose();
    
        console.log('模型加载成功了,并已添加到Cesium场景中');
      },
      undefined,(error) => {
        console.log('Error loading model:',error);
      }
    )

这一段代码中还有一个解码器,作用是如果模型过大的话就需要这个来进行一下操作,可以理解为压缩或者解压缩这中,下边也会有详细的解码器需要的配置的,上述代码中还有一个点击模型会出现一个警告框的事件,不需要的可以忽略

5.根据需要实现是否展示和隐藏,我遇到的场景是用if(isShow){} else {} 来进行判断的

let entity2 = null  //定义在最外边,否则容易出错

const loader2 = new GLTFLoader();
      if (isShow) {
        loader2.load(
          // 资源路径
          '../models/car.glb', 
          function (gltf) {
           let longitude2 = 116.593474298; // 例如:东经104.06度
           let latitude2 = 35.400849354;  // 例如:北纬30.67度
           let height2 = 2000;   // 模型相对于海平面的高度(单位:米)
           const cartesianPosition = Cesium.Cartesian3.fromDegrees(longitude2, latitude2, height2);
            // 创建Cesium实体
            entity2 = viewer.entities.add({
             position: cartesianPosition,
             model: {
               uri: '../models/car.glb', // 使用加载的模型URL
               scale: 100.0, // 根据需要调整模型大小
               heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, // 让模型贴合地面
             },
           });
           console.log(entity2,'222222222222222');
         },
         undefined,(error) => {
           console.log('Error loading model:',error);
         }
       )
       
      } else {
        if (entity2 !== undefined) { // 检查entity2是否已定义
          // console.log('entity2', entity2);
          viewer.entities.remove(entity2);
          entity2 = undefined; // 可选:清除引用以帮助垃圾回收
          // console.log('entity222', entity2);
        }

大概的代码就是这样的,用if将这个模型给抱起来,如果不符合的话就移除模型,需要注意的是,虽然他已经是else的了,但是并不影响需要在里边再次做判断,我就会老觉得已经是else了,就肯定是写上就是不展示的,但是事实不是这样的,有些时候还是要做判断,像这个一样,所以一定要多想多看一下

---------------------------------------------------------------------------------------------------------------------------------

至于viewer.entities.remove这个的作用  viewer.entities.remove 是 CesiumJS 库中用于从场景中删除特定实体(Entity)的方法。viewer.entities 是一个实体集合,其中包含了你在场景中添加的所有实体,如点、线、面、模型等。当你调用 viewer.entities.remove(entity) 时,你会从这个集合中移除指定的 entity,进而从3D视图中删除对应的可视化元素。 例如,在你提供的代码片段中,entity2 是一个包含模型(car.glb)的实体。当 isShow 变为 false 时,如果 entity2 已经被定义并且添加到 viewer.entities 集合中,viewer.entities.remove(entity2) 就会被调用来从3D场景中移除这个模型。这样,模型就不会再显示在Cesium的地球视图上了。 此外,移除实体还可以帮助释放内存,特别是当不再需要该实体时,因为Cesium会继续跟踪和更新场景中所有的实体,即使它们不在视口中。通过删除不必要的实体,可以优化性能和减少内存占用。

补充一点:

viewer.scene.primitives.remove 是 CesiumJS 中用于从场景的 primitives 集合中移除特定3D图元(Primitive)的方法。viewer.scene.primitives 包含了直接添加到场景中的所有非实体(Entity)3D图形对象,如几何体、3D Tiles、Billboard集等。 在你提供的代码片段中,viewer.scene.primitives.remove(wsgwDom6); 是用来从场景中移除之前通过 Cesium.Cesium3DTileset.fromUrl 加载的3D Tiles图层 wsgwDom6。这将导致整个3D Tiles图层及其包含的所有瓦片从3D视图中消失。通常,当不再需要3D Tiles图层或者需要更新图层时,会使用这个方法。移除图层也有助于释放内存,特别是对于大型3D数据集,因为它们可能会占用大量内存。

对应的解码器的资源我已经进行绑定了,但是貌似只能绑定一个,所以有需要的话,也可以私我拿都可以的

通过以上的操作,就可以实现渲染一个模型,以及控制他的显隐状态了,同样的也是我真实遇到的,也是研究了好久的,可能有些地方不正确和不专业,欢迎大家多多提意见,共同进步,有需要的小伙伴可以按需使用哈

  • 35
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值