vue中集成cesium和threejs

简介: vue中集成cesium和threejs

1.cesium和threejs结合


Three.js是一个轻量级的跨浏览器JavaScript库,用于在浏览器中创建和显示动画3D计算机图形。Cesium是一个3D库,旨在创建数字地球,其渲染与真实地球非常精确。

cesium的基本渲染原理与Three.js没有太大区别。通过在两个场景中复制cesium的球面坐标系和匹配的数字地球,很容易将两个单独的渲染引擎层整合到一个主场景中。


初始化Cesium渲染器

初始化Three.js渲染器

初始化这两个库的3D对象

循环渲染器


参考依据是威尔逊等将Three.js与Cesium集成的文章,链接地址:https://www.cesium.com/blog/2017/10/23/integrating-cesium-with-threejs/


2.vue中集成cesium和threejs


2.1 vue中集成cesium


使用vue-cli创建项目,这里使用的是vue-cli4。


1.安装cesium依赖

npm install cesium

2.配置vue.config.js

const CopyWebpackPlugin = require('copy-webpack-plugin')
const webpack = require('webpack')
const path = require('path')
let cesiumSource = './node_modules/cesium/Source'
let cesiumWorkers = '../Build/Cesium/Workers'
module.exports = {
    publicPath: "./",
    outputDir: "dist",
    lintOnSave: false,
    devServer: {
        open: process.platform === "darwin",
        host: "0.0.0.0",
        port: 5000,
        https: false,
        hotOnly: false
    },
    configureWebpack: {
        output: {
            sourcePrefix: ' '
        },
        amd: {
            toUrlUndefined: true
        },
        resolve: {
            alias: {
                'vue$': 'vue/dist/vue.esm.js',
                '@': path.resolve('src'),
                'cesium': path.resolve(__dirname, cesiumSource)
            }
        },
        plugins: [
            new CopyWebpackPlugin([{ from: path.join(cesiumSource, cesiumWorkers), to: 'Workers' }]),
            new CopyWebpackPlugin([{ from: path.join(cesiumSource, 'Assets'), to: 'Assets' }]),
            new CopyWebpackPlugin([{ from: path.join(cesiumSource, 'Widgets'), to: 'Widgets' }]),
            new CopyWebpackPlugin([{ from: path.join(cesiumSource, 'ThirdParty/Workers'), to: 'ThirdParty/Workers' }]),
            new webpack.DefinePlugin({
                CESIUM_BASE_URL: JSON.stringify('./')
            })
        ],
        module: {
            unknownContextCritical: /^.\/.*$/,
            unknownContextCritical: false
        }
    }
};

3.配置main.js全局引入cesium相关文件

import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
//引入cesium相关文件
var Cesium = require('cesium/Cesium');
var widgets = require('cesium/Widgets/widgets.css');
Vue.prototype.Cesium = Cesium
Vue.prototype.widgets = widgets
new Vue({
    render: h => h(App)
}).$mount('#app')

4.测试cesium

<template>
  <div id="container" class="box">
    <div id="cesiumContainer"></div>
  </div>
</template>
<script>
export default {
  name: 'Home',
  mounted(){
    this.init()
  },
  methods: {
    init() {
      let Cesium = this.cesium
      //这里的token使用自己注册的
      Cesium.Ion.defaultAccessToken = 'xxxxx'
      let viewer = new Cesium.Viewer('cesiumContainer');
    }
  }
};
</script>
<style lang='scss' scoped>
html,
body,
#cesiumContainer {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  overflow: hidden;
}
.box {
  height: 100%;
} 
</style>


2.2 引入threejs


安装threejs

npm install threejs

注意,threejs和ceiusm结合会有版本问题,具体可以参考https://blog.csdn.net/u011540323/article/details/103522075


2.3 实现效果



2.4 完整代码


vue+cesium+threejs

<template>
    <div>
        <div id="cesiumContainer"></div>
        <div id="ThreeContainer"></div>
    </div>
</template>
<script>
import * as THREE from 'three'
export default {
    data () {
        return {
            minWGS84: [115.23, 39.55],
            maxWGS84: [116.23, 41.55],
            objects3D: [],
            three: {
                renderer: null,
                camera: null,
                scene: null
            },
            cesium: {
                viewer: null
            },
            ce: null,
        }
    },
    methods: {
        initCesium() {
            let Cesium = this.Cesium;
            // let cesiumContainer = document.getElementById("cesiumContainer");
            Cesium.Ion.defaultAccessToken = 'xxxxxxx';  //注:这里需使用自己的token
            this.cesium.viewer = new Cesium.Viewer("cesiumContainer", {
                useDefaultRenderLoop: false,    //关闭自动渲染循环,这意味着需要手动调用render()方法来渲染场景
                selectionIndicator: false,  //关闭选中指示器
                homeButton: false,  //关闭回到初始位置按钮
                sceneModePicker: false, //关闭场景模式选择器
                navigationHelpButton: false,    //表示关闭导航帮助按钮
                animate: false, //关闭动画
                timeline: false,    //关闭时间线
                fullscreenButton: false,    //关闭全屏按钮
                navigationInstructionsInitiallyVisible: false,
                allowTextureFilterAnisotropic: false,
                contextOptions: {   //配置WebGL上下文选项
                    webgl: {
                        alpha: false,
                        antialias: true,
                        preserveDrawingBuffer: true,
                        failIfMajorPerformanceCaveat: false,
                        depth: true,
                        stencil: false,
                        anialias: false
                    }
                },
                targetFrameRate: 60,    //目标帧率
                resolutionScale: 0.1,   //场景分辨率的缩放比例
                orderIndependentTranslucency: true, 
                baseLayerPicker: true,  //启用底图选择器
                geocoder: false,    //关闭地名查找功能
                automaticallyTrackDataSourceClocks: false,  //关闭自动跟踪数据源时钟
                dataSources: null,
                clock: null,
                terrainShadows: Cesium.ShadowMode.DISABLED, //关闭地形阴影
                infoBox: false  //关闭信息框
            });
            //设置场景中心点的经纬度和高度,将相机飞到该点
            let center = Cesium.Cartesian3.fromDegrees(
                (this.minWGS84[0] + this.maxWGS84[0]) / 2,
                ((this.minWGS84[1] + this.maxWGS84[1]) / 2) - 1,
                200000
            );
            this.ce = center;
            //将相机飞到指定位置
            this.cesium.viewer.camera.flyTo({
                destination: center,    //相机位置
                orientation: {  //相机朝向
                    heading: Cesium.Math.toRadians(0),
                    pitch: Cesium.Math.toRadians(-60),
                    roll: Cesium.Math.toRadians(0)
                },
                duration: 3 //飞行动画的时长
            });
        },
        initThree() {
            let ThreeContainer = document.getElementById("ThreeContainer");
            let fov = 45;
            let width = window.innerWidth;
            let height = window.innerHeight;
            let aspect = width / height;
            let near = 1;
            let far = 10 * 1000 * 1000; // needs to be far to support Cesium's world-scale rendering
            this.three.scene = new THREE.Scene();    //场景
            this.three.camera = new THREE.PerspectiveCamera(fov, aspect, near, far); //透视相机
            this.three.renderer = new THREE.WebGLRenderer({  //渲染器
                alpha: true //true,代表透明度为0,完全透明(渲染器设置背景为透明,达成叠加效果)
            });
            ThreeContainer.appendChild(this.three.renderer.domElement);
        },
        Object3D(mesh, minWGS84, maxWGS84) {
            this.threeMesh = mesh;  //three网格对象
            this.minWGS84 = minWGS84;   //位置边界框最小坐标
            this.maxWGS84 = maxWGS84;   //位置边界框最大坐标
        },
        init3DObject() {
            //创建three球体
            let geometry1 = new THREE.SphereGeometry(1, 32, 32);
            let sphere = new THREE.Mesh(geometry1, new THREE.MeshPhongMaterial({
                color: 0xffffff,
                side: THREE.DoubleSide
            })); 
            sphere.scale.set(5000, 5000, 5000); //网格模型xyz方向都缩放5000倍
            sphere.uuid = "sphere";
            //创建组对象group
            var group = new THREE.Group();
            group.add(sphere);  //把球体添加到组
            this.three.scene.add(group); //把组添加到场景中
            group.position.set(this.ce.x, this.ce.y, this.ce.z);   //设置组的位置
            let ob3D = new this.Object3D(group, this.minWGS84, this.maxWGS84);
            this.objects3D.push(ob3D);
            //创建three十二面体
            let geometry2 = new THREE.DodecahedronGeometry();
            let dodecahedronMesh = new THREE.Mesh(geometry2, new THREE.MeshNormalMaterial()); 
            dodecahedronMesh.scale.set(5000, 5000, 5000);
            dodecahedronMesh.position.z += 15000;
            dodecahedronMesh.rotation.x = Math.PI / 2; // Three.js 渲染 z-up 而 Cesium 渲染 y-up,使three也变成y朝上
            dodecahedronMesh.uuid = "12面体";
            var group2 = new THREE.Group();
            group2.add(dodecahedronMesh)
            this.three.scene.add(group2); 
            group2.position.set(this.ce.x, this.ce.y, this.ce.z);
            //添加到对象数组
            let ob3D2 = new this.Object3D(group2, this.minWGS84, this.maxWGS84);
            this.objects3D.push(ob3D2);
            console.log(this.objects3D);
            /*******************************************添加灯光**********************************/
            //添加点光源
            var spotLight = new THREE.SpotLight(0xffffff);
            spotLight.position.set(0, 0, 50000);
            spotLight.castShadow = true; //设置光源投射阴影
            spotLight.intensity = 1;
            group.add(spotLight)
            //添加环境光
            var hemiLight = new THREE.HemisphereLight(0xff0000, 0xff0000, 1);
            group.add(hemiLight);
        },
        loop() {
            requestAnimationFrame(this.loop);
            this.renderCesium(); //渲染cesium
            this.renderThreeObj();   //渲染three
        },
        renderCesium() {
            this.cesium.viewer.render();
        },
        renderThreeObj() {
            let Cesium = this.Cesium;
            let ThreeContainer = document.getElementById("ThreeContainer");
            // 设置相机跟cesium保持一致,使用的cesium的相机为主相机,使three的相机与cesium保持一致即可
            this.three.camera.fov = Cesium.Math.toDegrees(this.cesium.viewer.camera.frustum.fovy) 
            this.three.camera.updateProjectionMatrix();
            // 笛卡尔坐标转换为三维向量
            var cartToVec = function(cart) {
                return new THREE.Vector3(cart.x, cart.y, cart.z);
            };
            // 配置three对象的位置,进行坐标变换才能使对象在地球上正确显示
            for (let id in this.objects3D) {
                let minWGS84 = this.objects3D[id].minWGS84;
                let maxWGS84 = this.objects3D[id].maxWGS84;
                // 物体中心位置计算为对象的最小和最大WGS84纬度和经度值的平均值,并且把经纬度坐标(WGS84)转笛卡尔坐标
                var center = Cesium.Cartesian3.fromDegrees((minWGS84[0] + maxWGS84[0]) / 2, (minWGS84[1] + maxWGS84[1]) / 2);
                // 向前方向计算为高度为1的Cartesian3位置,以便对象指向远离地球中心的方向
                var centerHigh = Cesium.Cartesian3.fromDegrees((minWGS84[0] + maxWGS84[0]) / 2, (minWGS84[1] + maxWGS84[1]) / 2, 1);
                // 使用 WGS84 区域从左下角到左上角的方向作为向上矢量
                var bottomLeft = cartToVec(Cesium.Cartesian3.fromDegrees(minWGS84[0], minWGS84[1]));
                var topLeft = cartToVec(Cesium.Cartesian3.fromDegrees(minWGS84[0], maxWGS84[1]));
                var latDir = new THREE.Vector3().subVectors(bottomLeft, topLeft).normalize();
                // 物体位置调整
                this.objects3D[id].threeMesh.position.copy(center); //位置设置为中心位置
                //_3Dobjects[id].threeMesh.lookAt(centerHigh);    // threejs-r87版本 
                //threejs-r87以上版本,需改写成如下
                this.objects3D[id].threeMesh.lookAt(centerHigh.x, centerHigh.y, centerHigh.z);    
                this.objects3D[id].threeMesh.up.copy(latDir);   //网格的向上矢量设置为计算出的向上矢量方向
            }
            //关闭相机自动更新
            this.three.camera.matrixAutoUpdate = false;
            // cesium相机位置
            var cvm = this.cesium.viewer.camera.viewMatrix;
            var civm = this.cesium.viewer.camera.inverseViewMatrix;
            //NOTE:r87后版本,threejs源码中相机的lookat重新调整了矩阵,需要将原来放在相机设置参数前的这行代码上移到此处
            three.camera.lookAt(new THREE.Vector3(0, 0, 0));
            // 同步Three相机位置设置
            this.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]
            );
            this.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]
            );
            // three.camera.lookAt(new THREE.Vector3(0, 0, 0));// threejs-r87版本 ,r87以后版本需要上移
            // 相机设置参数
            var width = ThreeContainer.clientWidth;
            var height = ThreeContainer.clientHeight;
            var aspect = width / height;
            this.three.camera.aspect = aspect;
            this.three.camera.updateProjectionMatrix();  // 相机参数更新
            this.three.renderer.setSize(width, height);
            this.three.renderer.render(this.three.scene, this.three.camera);
        }
    },
    mounted () {
        this.initCesium();
        this.initThree();
        this.init3DObject();
        this.loop();
    }
}
</script>
<style scoped>
#cesiumContainer {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
    margin: 0;
    overflow: hidden;
    padding: 0;
    font-family: sans-serif;
}
#ThreeContainer {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
    margin: 0;
    overflow: hidden;
    padding: 0;
    font-family: sans-serif;
    /* 关闭three鼠标控制器 */
    pointer-events: none;   
}
</style>
相关文章
|
25天前
|
缓存 资源调度 JavaScript
Vue集成Excalidraw实现在线画板功能
Excalidraw是一款开源在线绘图工具,适用于白板、思维导图、原型设计等场景。支持手绘风格、多种图形元素、导出功能及多人协作,深受开发者喜爱。本文档介绍了如何在Vue项目中集成Excalidraw,包括安装依赖、配置文件修改、页面添加等步骤,帮助开发者快速上手。
116 0
Vue集成Excalidraw实现在线画板功能
|
3月前
|
前端开发 JavaScript 开发者
Express.js与前端框架的集成:React、Vue和Angular的示例与技巧
本文介绍了如何将简洁灵活的Node.js后端框架Express.js与三大流行前端框架——React、Vue及Angular进行集成,以提升开发效率与代码可维护性。文中提供了详细的示例代码和实用技巧,展示了如何利用Express.js处理路由和静态文件服务,同时在React、Vue和Angular中构建用户界面,帮助开发者快速掌握前后端分离的开发方法,实现高效、灵活的Web应用构建。
65 3
|
4月前
|
存储 JavaScript 前端开发
Vue中通过集成Quill富文本编辑器实现公告的发布。Vue项目中vue-quill-editor的安装与使用【实战开发应用】
文章展示了在Vue项目中通过集成Quill富文本编辑器实现公告功能的完整开发过程,包括前端的公告发布、修改、删除操作以及后端的数据存储和处理逻辑。
Vue中通过集成Quill富文本编辑器实现公告的发布。Vue项目中vue-quill-editor的安装与使用【实战开发应用】
|
5月前
|
资源调度 JavaScript
Electron 集成 Vue —— electron-vue
Electron 集成 Vue —— electron-vue
61 0
|
7月前
|
资源调度 JavaScript 测试技术
Vue的集成测试:使用VueTestUtils进行单元测试的技术博文
【4月更文挑战第24天】本文介绍了如何使用VueTestUtils进行Vue.js项目的集成测试。首先,需安装VueTestUtils和vue-template-compiler。接着,展示了如何编写测试用例,包括使用`mount`和`shallowMount`方法挂载组件,以及通过`wrapper`操作和断言组件行为。文章还讨论了单元测试与集成测试的区别,并提到了模拟依赖、交互、组件状态管理和断言的策略。最后,强调了测试的可读性和可维护性对代码质量的重要性。通过VueTestUtils,开发者能更高效地进行Vue组件的测试。
|
7月前
|
XML NoSQL JavaScript
sprinboot+vue集成neo4j图数据库
sprinboot+vue集成neo4j图数据库
|
7月前
|
JavaScript 前端开发 搜索推荐
实时聊天应用:集成Python的WebSockets和Vue构建前端界面
【4月更文挑战第10天】本文介绍了如何使用Python的WebSockets和Vue.js构建实时聊天应用。通过WebSockets实现服务器与客户端的双向通信,借助Vue.js创建高效用户界面。步骤包括设计应用架构、实现WebSocket服务器、创建Vue.js项目、构建前端界面、集成WebSockets、接收和显示消息、性能优化及测试部署。这种技术组合为开发实时聊天应用提供了强大且灵活的解决方案,随着技术发展,未来的聊天应用将更加智能、个性化。
356 0
|
JavaScript 前端开发 开发者
Vue系列教程(18)- 集成UI框架(ElementUI)
Vue系列教程(18)- 集成UI框架(ElementUI)
256 1
|
JSON JavaScript 算法
[Vue]之Jwt的入门和Jwt工具类的使用及Jwt集成spa项目
[Vue]之Jwt的入门和Jwt工具类的使用及Jwt集成spa项目
178 0
|
27天前
|
JavaScript API 开发者
Vue是如何进行组件化的
Vue是如何进行组件化的