CesiumJS整合ThreeJS插件封装

最近做项目有一个三维需求使用CesiumJS比较难以实现,发现THREEJS中效果比较合适,于是准备将THREEJS整合到CesiumJS中

为实现效果所需我们找到官方Integrating Cesium with Three.js博客,于是根据该博客提供的思路去实现整合

一、创建ThreeJS类

为了开发时更好的扩展性把他写到了一个ThreeJS 类里面,功能直接调用即可

function ThreeJs(viewer, cesiumGlobal, defaultStatic, threeConf) {
  if (viewer && threeConf) {
    const { threeGlobal, containerId, threeContainerId } = threeConf;
    Cesium = cesiumGlobal;
    THREE = threeGlobal;
    this._viewer = viewer;
    this._containerId = containerId;
    this._threeContainerId = threeContainerId;
    this._initContainer();
    this._initThree();
  }
}
//部分省略......

项目中使用

哔哩哔哩使用介绍 >>>

一个简单实用的cesium三维开发工具包


Youtube 案例介绍 https://www.youtube.com/watch?v=2G_dA-ExPPY


import { ThreeJs } from 'cesium_dev_kit'

// 初始化map
 initMap (){
     const ThreeJsObj = new ThreeJs({
        cesiumGlobal: Cesium,
        threeGlobal: THREE,
        containerId: 'cesiumContainer',
        threeContainerId: 'threeContainer',
//部分省略......
      })
      this.c_viewer = ThreeJsObj.viewer;
      this.threeJs = ThreeJsObj.threeJs;
      this.initThree(this.threeJs);
  },
  // 初始化ThreeJs
 initThree (ThreeJs) {
      const threeDObject = this.create3DOject(ThreeJs, this.c_viewer);
      ThreeJs.addThreeObjects(threeDObject)

      this.c_viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)//解除视角锁定
    },
    // 创建三维模型
  create3DOject (threeObj, viewer) {
      let three = threeObj._three;
      let _3Dobjects = [], _3DOB;
      let entity = {
        name: 'Polygon',
        polygon: {
          hierarchy: Cesium.Cartesian3.fromDegreesArray([
            minWGS84[0], minWGS84[1],
            maxWGS84[0], minWGS84[1],
            maxWGS84[0], maxWGS84[1],
            minWGS84[0], maxWGS84[1]
          ]),
          material: Cesium.Color.RED.withAlpha(0.1)
        }
      }
      let Polypon = viewer.entities.add(entity);
      let doubleSideMaterial = new THREE.MeshNormalMaterial({
        side: THREE.DoubleSide
      });
//部分省略......
      return _3Dobjects;
    }   

根据官网的思量整合后最终实现效果如下
实现效果
效果是实现了,但是这个封装耦合性相当高,多个功能都需要使用这个插件时特别蹩脚,于是开始了下一次造…

二、插件threeJs 优化

理解官方案例的思路整合关键主要有三个方面,第一个是同步Camera,第二个是同步Scene中的物体位置,第三个是需要同步CesiumJS和THREEJS 的render重绘程序

1、创建ThressScene工具

为了扩展与管理我们抽离一个Scene 的工具js

class Scene extends THREE.Scene {}

在这个js 里面去实现插件操作的优化

1.1、同步camera

创建一个updateCameraMatrix来同步项目位置,核心代码如下

  updateCameraMatrix() {
      //同步相机
      this.camera.fov = Cesium.Math.toDegrees(
        this.cesiumViewer.camera.frustum.fovy
      ) // ThreeJS FOV is vertical
      this.camera.updateProjectionMatrix()

      this.camera.matrixAutoUpdate = false
      const cvm = this.cesiumViewer.camera.viewMatrix
      const civm = this.cesiumViewer.camera.inverseViewMatrix
      this.camera.lookAt(this.cameraCenter)
//部分省略......
      const width = this.cesiumViewer.scene.canvas.clientWidth
      const height = this.cesiumViewer.scene.canvas.clientHeight
      this.camera.aspect = width / height
      this.camera.updateProjectionMatrix()

      this.renderer.setSize(width, height)

      return this
    }
1.2、同步场景中的物体

创建一个updateGroupMatrixWorld来进行物体位置同步,核心代码如下

	 // 重写add 方法
     add(object) {
      if (arguments.length > 1) {
        for (let i = 0; i < arguments.length; i++) {
          this.childrenGroup.add(arguments[i])
        }

        return this
      }
	//部分省略......
      return this
    }
    updateGroupMatrixWorld() {
      // 得到面向模型的前向方向
      const center = this.cartesian3ToVector(
        Cesium.Cartesian3.fromDegrees(this.lngLat[0], this.lngLat[1], 0)
      )
      // 使用从左下到左上的方向作为上向量
      const topLeft = this.cartesian3ToVector(
        Cesium.Cartesian3.fromDegrees(this.lngLat[0], this.lngLat[1], 2)
      )
      const latDir = new THREE.Vector3().subVectors(center, topLeft).normalize()

      // 配置实体的位置和方向
      // this.syncGroup.position.copy(center)
      this.syncGroup.lookAt(latDir)
      this.syncGroup.up.copy(latDir)
      this.syncGroup.updateMatrix()

      this.cameraOffset.copy(center)

      this.sphere.position.set(0 - center.x, 0 - center.y, 0 - center.z)
      this.syncGroup.up.set(0, 0, -1)
      this.up.set(0, 0, -1)

      return this
    }
1.3、同步render
    renderCesium() {
      this.cesiumViewer.render()
      return this
    }

    renderThree() {
      this.renderer.render(this, this.camera)
      return this
    }
    
   loop(callback) {
    const _loop = function () {
      let time = requestAnimationFrame(_loop)
      callback && callback(time)
    }
    _loop()
  }

2、插件使用

插件改完了心里还是有点鸡冻,马上在项目中引入开始测试

2.1 引入插件初始化map
import { initCesium } from 'cesium_dev_kit'

// 初始化map
 initMap (){
      const { viewer, threeJs, base, graphics, material } = new initCesium({
        cesiumGlobal: Cesium,
        threeGlobal: THREE,
        containerId: 'cesiumContainer',
        threeContainerId: 'threeContainer',
		//部分省略......
      })
      this.c_viewer = viewer;
      this.threeJs = threeJs;
      this.base = base;
      this.graphics = graphics;
      this.material = material
      this.initThree(this.threeJs);
      // this.getClickPosition()
      this.createAEllipsoid();
      this.createAFanShape();
  },
2.1 初始化Meshes

这里我们就使用官方的extrude案例

    initMeshes (scene) {
      // 环形 extrude
      const closedSpline = new THREE.CatmullRomCurve3([
        new THREE.Vector3(-60, 30, 60), // 左下
        new THREE.Vector3(-60, 100, 60), // 左中
        new THREE.Vector3(-60, 220, 60), // 左上
        new THREE.Vector3(60, 80, -60), // 右中
        new THREE.Vector3(60, 30, -60), // 右下
      ]);
      // 2、extrude settings
      closedSpline.curveType = "catmullrom";
      closedSpline.closed = true;
      const extrudeSettings = {
        steps: 100,
        bevelEnabled: false,
        extrudePath: closedSpline,
      };
      // 3、construct shape
      const r = 20; // 截面半径
      const pts1 = [];
      const count = 3; // 截面的棱边数量
      for (let index = 0; index < count; index++) {
        // index/count 几分之几,2π为周长
        const a = (index / count) * Math.PI * 2;
        pts1.push(new THREE.Vector2(r * Math.cos(a), r * Math.sin(a)));
      }
      const shape1 = new THREE.Shape(pts1);
      // create geometry
      const geometry1 = new THREE.ExtrudeGeometry(shape1, extrudeSettings);
      // create material
      const material1 = new THREE.MeshLambertMaterial({
        color: 0xb00000,
      });
      // assembly meshes
      const mesh1 = new THREE.Mesh(geometry1, material1);
      // add mesh to scene
      scene.add(mesh1);
		//部分省略......
    },
2.2 初始化threeScene
    initThree (ThreeJs) {
      const { scene, camera } = ThreeJs.initThree({ center, axesHelper: true, threeHabit: false });
      this.initLight(scene, camera)
      this.initMeshes(scene);
      this.flyto(scene);
      ThreeJs.loop(function () {
        scene.update();
      })
    },
2.3 效果预览

光线投射
光线投射 图形挤压图形挤压烟花效果
烟花效果

奔跑的士兵
奔跑的士兵

嗯嗯测试了几个功能看了基本上问题不大了,ok先告一段落后期继续优化…

感谢您的阅读,最后附上插件下载与源码地址

  • 插件下载安装
npm install cesium_dev_kit
请使用cesium@1.107之后的版本提供的构造函数和API。全局安装@vue/cli,vue3创建cesium+three.js深度融合的项目,项目名称为vue-cesium,选择babel、vue-router、vuex、scss、Linter/F和vue3,其他的默认。删除views目录下的所有js文件和components目录下的文件,在views目录下新建CesiumView.vue文件,修改App.vue和router目录下的main.js文件,将router目录下的index.js文件里的路由器模型设置为hash模型。安装cesium和three,修改vue.config.js,在vue.config.js里添加const CesiumSourc = "node_modules/cesium/Source",添加const=CesiumWorker="../Build/Cesium/Workers",把CesiumSourc路径下的Assets、ThirdParty、Widgets复制到dist目录下。把CesiumSource路径下的CesiumWorker复制到dist目录下,把public目录下的models复制到dist目录下。在CesiumView.vue里添加 import * as Cesium from "cesium/Cesium";、 import * as THREE from "three"; 、import "cesium/Widgets/widgets.css,使用最新版本提供的构造函数或api编写代码,在CesiumView.vue中添加const czml = [ { id: "document", name: "CZML Model", version: "1.0", clock: { interval: "2025-08-07T09:30:00Z/2025-08-10T09:30:00Z", currentTime: "2025-08-07T09:30:00Z", multiplier: 1, range: "LOOP_STOP", step: "SYSTEM_CLOCK_MULTIPLIER" } }, { id: "aircraftModel", name: "Cesium Air", position: { epoch: "2025-08-07T09:30:00Z", cartographicDegrees: [ 0, 113.60194, 23.525223, 520, 1200, 113.578067, 23.541241, 1200, 2500, 113.487823, 23.594139, 2500 ], }, model: { gltf: "models/CesiumAir/Cesium_Air.glb", minimumPixelSize: 128, maximumScale: 200, }, }, ];,dataSource加载czml并监听czml获取czml模型的位置和自动调整czml模型的方向,创建particleCount数量为5000个的粒子系统,建立float32Array()缓冲区存储记录每个粒子的位置、大小、颜色、速度、随机值,使用glsl着色器渲染粒子冒烟着火的特效,把粒子系统的三维坐标转换成cesium的三维坐标添加到czml模型上显示出来。注意,此项目不添加额外的ui控制和ui显示。
08-08
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

奔跑的痕迹

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

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

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

打赏作者

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

抵扣说明:

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

余额充值