VUE3+Cesium+Three项目构建

23 篇文章 20 订阅
20 篇文章 1 订阅

CesiumJS是一个开源、免费的三维地图开发框架,Three.js是一个也开源、免费的三维渲染框架,两者都是基于WebGL技术、使用JavaScript开发的Web前端三维可视化框架,目前在国内外的三维Web开发中使用极为广泛。但Three.js是一个轻量级的跨浏览器JavaScript库,用于在浏览器中创建和显示动画3D计算机图形;而Cesium的可视化内容以地理空间数据为主,如卫星影像、地形、城市级三维模型等,数据量和空间范围都非常大,对数据精度要求更高,并且还具有一系列GIS相关的空间分析功能,但在模型的渲染及可视化方面略逊于Three。

因此,将Cesium的地理数据渲染和GIS功能与Three.js广泛而易用的通用3D API相结合,不仅可以优化模型的渲染精度,也可以加入许多地理信息相关的应用,为新的WebGL体验开启了许多可能性。

目录

1.Cesium+Three实现原理

2.VUE3+Cesium项目创建

3.导入Three

4.实现VUE3+Cesium+Three

5. 项目模块化源码


1.Cesium+Three实现原理

主要原理:两个框架分成不同的视图层,参考 HTML Canvas 元素,并将它们的控制器组合在同一个坐标系中。

可参考如下官方案例:

Integrating Cesium with Three.js – Cesiumhttps://cesium.com/blog/2017/10/23/integrating-cesium-with-threejs/

实现步骤:

1)在HTML中设置两个容器分别容纳Cesium和Three(或初始化Cesium容器后将Three容器追加到Cesium后)。

2)使Three产生的场景覆盖Cesium之上( Three.js DOM 元素位于 Cesium 之上),并禁用Three容器的鼠标事件,通过Cesium同步控制Three。

3)并分别对Cesium渲染器和Three渲染器进行初始化,通过禁用Cesium的默认渲染循环,我们可以将其动画帧与Three.js同步。并设置Three场景的渲染器背景为透明,以达成叠加效果。

4)初始化物体并分别加入至各自的场景之中(注意 Three.js 渲染 z-up 而 Cesium 渲染 y-up。)。

5)进行坐标转换,使对象在地球上正确显示。这包括将大地纬度/经度位置转换为笛卡儿XYZ,并使用WGS84区域从左下角到左上角的方向作为向上矢量,使物体指向地球中心。这也可以通过使用本地笛卡尔东北向或东北向下来计算。

6)请求关键帧并循环渲染器。

2.VUE3+Cesium项目创建

项目创建可参考往期内容:

VUE3构建Cesium项目_HM-hhxx!的博客-CSDN博客_cesium vue3使用VUE3构建cesium项目https://blog.csdn.net/damadashen/article/details/124896474

3.导入Three

安装Three相关依赖:

npm install three --save
or
yarn add three

Three初始化可参考往期内容:

VUE3_Three项目构建(基础模板)_HM-hhxx!的博客-CSDN博客_three项目VUE3配置Three开发环境(配置glsl语法支持)及项目基础模板。https://blog.csdn.net/damadashen/article/details/126064919

4.实现VUE3+Cesium+Three

<template>
  <div id="cesiumContainer" ref="cesiumContainer"></div>
</template>

<script setup>
import { onMounted } from "vue";
import * as Cesium from "cesium";
import "./Widgets/widgets.css";
import * as THREE from "three";
// console.log(flightData);

// 设置cesium的token
Cesium.Ion.defaultAccessToken = "yourToken";
// cesium默认资源路径
window.CESIUM_BASE_URL = "/";
// 设置默认的视角为中国
Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees(
  // 西边经度
  89.5,
  // 南边维度
  20.4,
  // 东边经度
  110.4,
  // 北边维度
  61.2
);

onMounted(() => {
  main();
});

// 初始化cesium渲染器
// 初始化three渲染器
// 初始化2个库的3D物体
// 循环渲染

// three全局对象
let three = {
  renderer: null,
  camera: null,
  scene: null,
};

// 设置全局cesium对象
let cesium = {
  viewer: null,
};

function main() {
  // 设置北京显示模型的渲染范围
  var minWGS84 = [115.39, 38.9];
  var maxWGS84 = [117.39, 40.9];

  // 设置cesium容器
  var cesiumContainer = document.getElementById("cesiumContainer");

  // three.js物体
  let objects3D = [];
  //封装three物体(使three物体具有经纬度)
  function Object3D(mesh, minWGS84, maxWGS84) {
    this.threeMesh = mesh;//物体
    this.minWGS84 = minWGS84;//范围
    this.maxWGS84 = maxWGS84;//范围
  }

  // 初始化cesium渲染器
  function initCesium() {
    cesium.viewer = new Cesium.Viewer(cesiumContainer, {
      useDefaultRenderLoop: false,
      selectionIndicator: false,
      homeButton: false,
      infoBox: false,
      sceneModePicker: false,
      navigationHelpButton: false,
      animation: false,
      timeline: false,
      fullscreenButton: false,
      baseLayerPicker: false,
      clock: false,
      geocoder: false,
      //     天地图矢量路径图
      imageryProvider: new Cesium.WebMapTileServiceImageryProvider({
        url: "http://t0.tianditu.com/vec_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=vec&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=30d07720fa76f07732d83c748bb84211",
        layer: "tdtBasicLayer",
        style: "default",
        format: "image/jpeg",
        tileMatrixSetID: "GoogleMapsCompatible",
      }),
      //cesium中webgl选项
      contextOptions: {
        webgl: {
          //透明度
          alpha: false,
          // 抗锯齿
          antialias: true,
          //深度检测
          depth: true,
        },
      },
    });
    // 地图叠加
    var imageryLayers = cesium.viewer.imageryLayers;
    console.log(imageryLayers);
    var layer = imageryLayers.addImageryProvider(
      new Cesium.WebMapTileServiceImageryProvider({
        url: "http://t0.tianditu.com/img_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=img&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=30d07720fa76f07732d83c748bb84211",
        layer: "tdtBasicLayer",
        style: "default",
        format: "image/jpeg",
        tileMatrixSetID: "GoogleMapsCompatible",
      })
    );
    layer.alpha = 0.5;
    // 设置前往地点
    let center = Cesium.Cartesian3.fromDegrees(
      (minWGS84[0] + maxWGS84[0]) / 2,
      (minWGS84[1] + maxWGS84[1]) / 2,
      20000
    );

    // 设置相机飞往该区域
    cesium.viewer.camera.flyTo({
      destination: center,
      duration: 2,
      orientation: {
        heading: Cesium.Math.toRadians(0),
        pitch: Cesium.Math.toRadians(-90),
        roll: 0,
      },
    });
  }
  //初始化Three
  function initThree() {
    // 设置相机配置
    let fov = 45;//视角
    let aspect = window.innerWidth / window.innerHeight;//宽高比例
    let near = 0.1;
    let far = 10 * 1000 * 1000;//视域范围

    // 初始化场景
    three.scene = new THREE.Scene();
    three.camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
    three.renderer = new THREE.WebGLRenderer({
      antialias: true,//抗锯齿
      alpha: true,
    });
    // 设置渲染器大小
    three.renderer.setSize(window.innerWidth, window.innerHeight);

    // 添加环境光
    let ambientLight = new THREE.AmbientLight(0xffffff, 1);
    three.scene.add(ambientLight);
    // 添加three.jscanvas元素到cesium容器
    cesiumContainer.appendChild(three.renderer.domElement);
  }

  // 创建three.js物体
  function createMesh() {
    let geometry = new THREE.BoxBufferGeometry(1, 1, 1);
    let material = new THREE.MeshBasicMaterial({
      color: 0x00ff00,
    });
    let mesh = new THREE.Mesh(geometry, material);

    // 放大物体
    mesh.scale.set(100, 100, 100); // 放大
    mesh.position.set(0, 0, 50); // 平移

    let meshGroup = new THREE.Group();
    meshGroup.add(mesh);
    // 添加至场景
    three.scene.add(meshGroup);
    // 创建3d物体
    let OB3d = new Object3D(
      meshGroup,
      [minWGS84[0], minWGS84[1]],
      [maxWGS84[0], maxWGS84[1]]
    );

    // 添加到3d物体数组
    objects3D.push(OB3d);
  }

  function renderThree() {
    // 设置相机跟cesium保持一致
    three.camera.fov = Cesium.Math.toDegrees(cesium.viewer.camera.frustum.fovy);
    // 声明一个将cesium框架的cartesian3转换为three.js的vector3(笛卡尔坐标转换为三维向量)
    let cartToVec = function (cart) {
      return new THREE.Vector3(cart.x, cart.y, cart.z);
    };
    // 将3D的物体通过经纬度转换成对应的位置
    objects3D.forEach((item, index) => {
      // 通过经纬度获取中心点的位置
      let center = Cesium.Cartesian3.fromDegrees(
        (item.minWGS84[0] + item.maxWGS84[0]) / 2,
        (item.minWGS84[1] + item.maxWGS84[1]) / 2
      );
      item.threeMesh.position.copy(cartToVec(center));

      //计算朝向(切面方向-切线向量)
      //中心高度点
      let centerHeight = Cesium.Cartesian3.fromDegrees(
        (item.minWGS84[0] + item.maxWGS84[0]) / 2,
        (item.minWGS84[1] + item.maxWGS84[1]) / 2,
        1
      );
      //左下
      let bottomLeft = cartToVec(
        Cesium.Cartesian3.fromDegrees(item.minWGS84[0], item.minWGS84[1])
      );
      //左上
      let topLeft = cartToVec(
        Cesium.Cartesian3.fromDegrees(item.minWGS84[0], item.maxWGS84[1])
      );
      //朝向()
      let latDir = new THREE.Vector3()
        .subVectors(bottomLeft, topLeft)
        .normalize();

      // console.log(item);
      //设置查看方向
      item.threeMesh.lookAt(centerHeight.x, centerHeight.y, centerHeight.z);
      //设置朝向
      item.threeMesh.up.copy(latDir);
    });

    //设置摄像机矩阵
    // 设置相机跟cesium保持一致
    three.camera.matrixAutoUpdate = false;//自动更新
    //复制cesium相机矩阵
    let cvm = cesium.viewer.camera.viewMatrix;
    let civm = cesium.viewer.camera.inverseViewMatrix;
    // three相机默认朝向0,0,0
    three.camera.lookAt(0, 0, 0);

    // 设置threejs相机矩阵
    three.camera.matrixWorld.set(
      civm[0],
      civm[4],
      civm[8],
      civm[12],
      civm[1],
      civm[5],
      civm[9],
      civm[13],
      civm[2],
      civm[6],
      civm[10],
      civm[14],
      civm[3],
      civm[7],
      civm[11],
      civm[15]
    );

    three.camera.matrixWorldInverse.set(
      cvm[0],
      cvm[4],
      cvm[8],
      cvm[12],
      cvm[1],
      cvm[5],
      cvm[9],
      cvm[13],
      cvm[2],
      cvm[6],
      cvm[10],
      cvm[14],
      cvm[3],
      cvm[7],
      cvm[11],
      cvm[15]
    );
    //设置宽高比例
    let width = cesiumContainer.clientWidth;
    let height = cesiumContainer.clientHeight;
    three.camera.aspect = width / height;
    //更新相机矩阵
    three.camera.updateProjectionMatrix();
    //设置尺寸大小
    three.renderer.setSize(width, height);
    three.renderer.clear();
    three.renderer.render(three.scene, three.camera);
  }
  function renderCesium() {
    cesium.viewer.render();
  }
  //循环函数,不断请求动画帧渲染
  function loop() {
    requestAnimationFrame(loop);
    // cesium渲染
    renderCesium();
    // three.js渲染
    renderThree();
  }
  //初始化调用
  initCesium();
  initThree();
  createMesh();
  loop();
}
</script>

<style>
* {
  margin: 0;
  padding: 0;
}

#cesiumContainer {
  width: 100vw;
  height: 100vh;
  position: relative;
}

#cesiumContainer>canvas {
  position: absolute;
  top: 0;
  left: 0;
  /* 设置鼠标事件穿透 */
  pointer-events: none;
}
</style>

实现效果:

5. 项目模块化源码

VUE3+Cesium+Three项目源码_HM-hhxx!的博客-CSDN博客Vue+Cesium+Three项目构建的详解及源码。https://blog.csdn.net/damadashen/article/details/126585531?spm=1001.2014.3001.5501

  • 6
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

HM-hhxx!

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

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

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

打赏作者

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

抵扣说明:

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

余额充值