• VUE3+Cesium+Three项目构建


    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相关依赖:

    1. npm install three --save
    2. or
    3. 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

    1. <script setup>
    2. import { onMounted } from "vue";
    3. import * as Cesium from "cesium";
    4. import "./Widgets/widgets.css";
    5. import * as THREE from "three";
    6. // console.log(flightData);
    7. // 设置cesium的token
    8. Cesium.Ion.defaultAccessToken = "yourToken";
    9. // cesium默认资源路径
    10. window.CESIUM_BASE_URL = "/";
    11. // 设置默认的视角为中国
    12. Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees(
    13. // 西边经度
    14. 89.5,
    15. // 南边维度
    16. 20.4,
    17. // 东边经度
    18. 110.4,
    19. // 北边维度
    20. 61.2
    21. );
    22. onMounted(() => {
    23. main();
    24. });
    25. // 初始化cesium渲染器
    26. // 初始化three渲染器
    27. // 初始化2个库的3D物体
    28. // 循环渲染
    29. // three全局对象
    30. let three = {
    31. renderer: null,
    32. camera: null,
    33. scene: null,
    34. };
    35. // 设置全局cesium对象
    36. let cesium = {
    37. viewer: null,
    38. };
    39. function main() {
    40. // 设置北京显示模型的渲染范围
    41. var minWGS84 = [115.39, 38.9];
    42. var maxWGS84 = [117.39, 40.9];
    43. // 设置cesium容器
    44. var cesiumContainer = document.getElementById("cesiumContainer");
    45. // three.js物体
    46. let objects3D = [];
    47. //封装three物体(使three物体具有经纬度)
    48. function Object3D(mesh, minWGS84, maxWGS84) {
    49. this.threeMesh = mesh;//物体
    50. this.minWGS84 = minWGS84;//范围
    51. this.maxWGS84 = maxWGS84;//范围
    52. }
    53. // 初始化cesium渲染器
    54. function initCesium() {
    55. cesium.viewer = new Cesium.Viewer(cesiumContainer, {
    56. useDefaultRenderLoop: false,
    57. selectionIndicator: false,
    58. homeButton: false,
    59. infoBox: false,
    60. sceneModePicker: false,
    61. navigationHelpButton: false,
    62. animation: false,
    63. timeline: false,
    64. fullscreenButton: false,
    65. baseLayerPicker: false,
    66. clock: false,
    67. geocoder: false,
    68. // 天地图矢量路径图
    69. imageryProvider: new Cesium.WebMapTileServiceImageryProvider({
    70. 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",
    71. layer: "tdtBasicLayer",
    72. style: "default",
    73. format: "image/jpeg",
    74. tileMatrixSetID: "GoogleMapsCompatible",
    75. }),
    76. //cesium中webgl选项
    77. contextOptions: {
    78. webgl: {
    79. //透明度
    80. alpha: false,
    81. // 抗锯齿
    82. antialias: true,
    83. //深度检测
    84. depth: true,
    85. },
    86. },
    87. });
    88. // 地图叠加
    89. var imageryLayers = cesium.viewer.imageryLayers;
    90. console.log(imageryLayers);
    91. var layer = imageryLayers.addImageryProvider(
    92. new Cesium.WebMapTileServiceImageryProvider({
    93. 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",
    94. layer: "tdtBasicLayer",
    95. style: "default",
    96. format: "image/jpeg",
    97. tileMatrixSetID: "GoogleMapsCompatible",
    98. })
    99. );
    100. layer.alpha = 0.5;
    101. // 设置前往地点
    102. let center = Cesium.Cartesian3.fromDegrees(
    103. (minWGS84[0] + maxWGS84[0]) / 2,
    104. (minWGS84[1] + maxWGS84[1]) / 2,
    105. 20000
    106. );
    107. // 设置相机飞往该区域
    108. cesium.viewer.camera.flyTo({
    109. destination: center,
    110. duration: 2,
    111. orientation: {
    112. heading: Cesium.Math.toRadians(0),
    113. pitch: Cesium.Math.toRadians(-90),
    114. roll: 0,
    115. },
    116. });
    117. }
    118. //初始化Three
    119. function initThree() {
    120. // 设置相机配置
    121. let fov = 45;//视角
    122. let aspect = window.innerWidth / window.innerHeight;//宽高比例
    123. let near = 0.1;
    124. let far = 10 * 1000 * 1000;//视域范围
    125. // 初始化场景
    126. three.scene = new THREE.Scene();
    127. three.camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
    128. three.renderer = new THREE.WebGLRenderer({
    129. antialias: true,//抗锯齿
    130. alpha: true,
    131. });
    132. // 设置渲染器大小
    133. three.renderer.setSize(window.innerWidth, window.innerHeight);
    134. // 添加环境光
    135. let ambientLight = new THREE.AmbientLight(0xffffff, 1);
    136. three.scene.add(ambientLight);
    137. // 添加three.jscanvas元素到cesium容器
    138. cesiumContainer.appendChild(three.renderer.domElement);
    139. }
    140. // 创建three.js物体
    141. function createMesh() {
    142. let geometry = new THREE.BoxBufferGeometry(1, 1, 1);
    143. let material = new THREE.MeshBasicMaterial({
    144. color: 0x00ff00,
    145. });
    146. let mesh = new THREE.Mesh(geometry, material);
    147. // 放大物体
    148. mesh.scale.set(100, 100, 100); // 放大
    149. mesh.position.set(0, 0, 50); // 平移
    150. let meshGroup = new THREE.Group();
    151. meshGroup.add(mesh);
    152. // 添加至场景
    153. three.scene.add(meshGroup);
    154. // 创建3d物体
    155. let OB3d = new Object3D(
    156. meshGroup,
    157. [minWGS84[0], minWGS84[1]],
    158. [maxWGS84[0], maxWGS84[1]]
    159. );
    160. // 添加到3d物体数组
    161. objects3D.push(OB3d);
    162. }
    163. function renderThree() {
    164. // 设置相机跟cesium保持一致
    165. three.camera.fov = Cesium.Math.toDegrees(cesium.viewer.camera.frustum.fovy);
    166. // 声明一个将cesium框架的cartesian3转换为three.js的vector3(笛卡尔坐标转换为三维向量)
    167. let cartToVec = function (cart) {
    168. return new THREE.Vector3(cart.x, cart.y, cart.z);
    169. };
    170. // 将3D的物体通过经纬度转换成对应的位置
    171. objects3D.forEach((item, index) => {
    172. // 通过经纬度获取中心点的位置
    173. let center = Cesium.Cartesian3.fromDegrees(
    174. (item.minWGS84[0] + item.maxWGS84[0]) / 2,
    175. (item.minWGS84[1] + item.maxWGS84[1]) / 2
    176. );
    177. item.threeMesh.position.copy(cartToVec(center));
    178. //计算朝向(切面方向-切线向量)
    179. //中心高度点
    180. let centerHeight = Cesium.Cartesian3.fromDegrees(
    181. (item.minWGS84[0] + item.maxWGS84[0]) / 2,
    182. (item.minWGS84[1] + item.maxWGS84[1]) / 2,
    183. 1
    184. );
    185. //左下
    186. let bottomLeft = cartToVec(
    187. Cesium.Cartesian3.fromDegrees(item.minWGS84[0], item.minWGS84[1])
    188. );
    189. //左上
    190. let topLeft = cartToVec(
    191. Cesium.Cartesian3.fromDegrees(item.minWGS84[0], item.maxWGS84[1])
    192. );
    193. //朝向()
    194. let latDir = new THREE.Vector3()
    195. .subVectors(bottomLeft, topLeft)
    196. .normalize();
    197. // console.log(item);
    198. //设置查看方向
    199. item.threeMesh.lookAt(centerHeight.x, centerHeight.y, centerHeight.z);
    200. //设置朝向
    201. item.threeMesh.up.copy(latDir);
    202. });
    203. //设置摄像机矩阵
    204. // 设置相机跟cesium保持一致
    205. three.camera.matrixAutoUpdate = false;//自动更新
    206. //复制cesium相机矩阵
    207. let cvm = cesium.viewer.camera.viewMatrix;
    208. let civm = cesium.viewer.camera.inverseViewMatrix;
    209. // three相机默认朝向0,0,0
    210. three.camera.lookAt(0, 0, 0);
    211. // 设置threejs相机矩阵
    212. three.camera.matrixWorld.set(
    213. civm[0],
    214. civm[4],
    215. civm[8],
    216. civm[12],
    217. civm[1],
    218. civm[5],
    219. civm[9],
    220. civm[13],
    221. civm[2],
    222. civm[6],
    223. civm[10],
    224. civm[14],
    225. civm[3],
    226. civm[7],
    227. civm[11],
    228. civm[15]
    229. );
    230. three.camera.matrixWorldInverse.set(
    231. cvm[0],
    232. cvm[4],
    233. cvm[8],
    234. cvm[12],
    235. cvm[1],
    236. cvm[5],
    237. cvm[9],
    238. cvm[13],
    239. cvm[2],
    240. cvm[6],
    241. cvm[10],
    242. cvm[14],
    243. cvm[3],
    244. cvm[7],
    245. cvm[11],
    246. cvm[15]
    247. );
    248. //设置宽高比例
    249. let width = cesiumContainer.clientWidth;
    250. let height = cesiumContainer.clientHeight;
    251. three.camera.aspect = width / height;
    252. //更新相机矩阵
    253. three.camera.updateProjectionMatrix();
    254. //设置尺寸大小
    255. three.renderer.setSize(width, height);
    256. three.renderer.clear();
    257. three.renderer.render(three.scene, three.camera);
    258. }
    259. function renderCesium() {
    260. cesium.viewer.render();
    261. }
    262. //循环函数,不断请求动画帧渲染
    263. function loop() {
    264. requestAnimationFrame(loop);
    265. // cesium渲染
    266. renderCesium();
    267. // three.js渲染
    268. renderThree();
    269. }
    270. //初始化调用
    271. initCesium();
    272. initThree();
    273. createMesh();
    274. loop();
    275. }
    276. script>
    277. <style>
    278. * {
    279. margin: 0;
    280. padding: 0;
    281. }
    282. #cesiumContainer {
    283. width: 100vw;
    284. height: 100vh;
    285. position: relative;
    286. }
    287. #cesiumContainer>canvas {
    288. position: absolute;
    289. top: 0;
    290. left: 0;
    291. /* 设置鼠标事件穿透 */
    292. pointer-events: none;
    293. }
    294. style>

    实现效果:

    5. 项目模块化源码

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

  • 相关阅读:
    Python的浅拷贝和深拷贝
    【算法】斐波那契数列与台风的故事
    HNU工训中心STC-B学习板大作业-基于OLED模块的多功能MP4
    代码随想录算法训练营第二十二天丨 二叉树part09
    生成式AI - Knowledge Graph Prompting:一种基于大模型的多文档问答方法
    【Java项目-飞翔的小鸟】附源码
    软件著作权申请材料及申请流程?
    剑指offer(C++)-JZ10:斐波那契数列(时间复杂度O(logn)解法)
    JSX语法
    渗透测试-域内密码凭证获取
  • 原文地址:https://blog.csdn.net/damadashen/article/details/126550168