three.js和cesium结合使用

通过cesium官网文章可以得知二者结合使用的方法

提前准备内容

  • node环境 (案例版本14.17.2)
  • 创建vue项目
  • 安装cesium依赖 (案例版本1.73.0)
  • 安装three.js依赖(案例版本0.155.0)

功能以及实现方法

1创建标签

<div class="container-integrate" style="">
    <!--cesium容器-->
    <div id="cesiumContainer" style="height: 100%;width: 100%;"></div>
    <!-- three.js场景 -->
    <div id="ThreeContainer"></div>
  </div>

2.初始化参数

   // 声明变量
   // 存储 three对象
   let three = {
     renderer: null,
     camera: null,
     scene: null,
   };

   // 存储cesium对象
   let cesium = {
     viewer: null,
   };
   // 模型定位范围,用来吧模型添加到这个执行为止
   let minWGS84 = [115.56936458615716, 39.284100766866445];
   let maxWGS84 = [117.10745052365716, 41.107831235616445];
   // 存储一下等待刷新的模型
   let _3Dobjects = []; // 可以是任何Three.js对象网格
   // cesium 容器
   let cesiumContainer = document.getElementById("cesiumContainer");

功能函数

  • 清空数据对象
 _3DObject() {
       //THREEJS 3DObject.mesh
       this.threeMesh = null;
       //location bounding box
       this.minWGS84 = null;
       this.maxWGS84 = null;
     }
  • 初始化cesium
initCesium() {
        cesium.viewer = new Cesium.Viewer(cesiumContainer, {
          useDefaultRenderLoop: false,
          selectionIndicator: false,
          homeButton: false,
          sceneModePicker: false,
          infoBox: false,
          navigationHelpButton: false,
          navigationInstructionsInitiallyVisible: false,
          animation: false,
          timeline: false,
          fullscreenButton: false,
          allowTextureFilterAnisotropic: false,
          baseLayerPicker: false,

          contextOptions: {
            webgl: {
              alpha: false,
              antialias: true,
              preserveDrawingBuffer: true,
              failIfMajorPerformanceCaveat: false,
              depth: true,
              stencil: false,
              anialias: false,
            },
          },

          targetFrameRate: 60,
          resolutionScale: 0.1,
          orderIndependentTranslucency: true,
          //加载底图
          imageryProvider: new Cesium.ArcGisMapServerImageryProvider({
            url:
              "https://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer",
          }),
          geocoder: false,
          automaticallyTrackDataSourceClocks: false,
          // creditContainer : "hidecredit", //注意:这里需要注释掉,否则会报找不到容器的问题
          dataSources: null,
          clock: null,
          terrainShadows: Cesium.ShadowMode.DISABLED,
        });
        let center = Cesium.Cartesian3.fromDegrees(
          (minWGS84[0] + maxWGS84[0]) / 2,
          (minWGS84[1] + maxWGS84[1]) / 2 - 1,
          200000
        );
        cesium.viewer.camera.flyTo({
          destination: center,
          orientation: {
            heading: Cesium.Math.toRadians(0),
            pitch: Cesium.Math.toRadians(-60),
            roll: Cesium.Math.toRadians(0),
          },
          duration: 3,
        });
      }
  • 初始化three.js
initThree() {
        let fov = 45;
        let width = window.innerWidth;
        let height = window.innerHeight;
        let aspect = width / height;
        let near = 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({ alpha: true });
        let Amlight = new THREE.AmbientLight(0xffffff, 2);
        three.scene.add(Amlight);
        // 注意这里,直接把three容器(canvas 添加到 cesium中,在cesium的canvas之下),
        // 这样的话,两个canvas才会重叠起来。
        cesium.viewer.cesiumWidget.canvas.parentElement.appendChild(
          three.renderer.domElement
        );
        // ThreeContainer.appendChild(three.renderer.domElement);
        var ThreeContainer = document.getElementById("ThreeContainer");
        ThreeContainer.appendChild(three.renderer.domElement);
      }

全部代码

<template>
  <div class="container-integrate" style="">
    <!--cesium容器-->
    <div id="cesiumContainer" style="height: 100%;width: 100%;"></div>
    <!-- three.js场景 -->
    <div id="ThreeContainer"></div>
  </div>
</template>

<script>
import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
// 水
import { Water } from "three/examples/jsm/objects/Water.js";
// 天空
import { Sky } from "three/examples/jsm/objects/Sky.js";
const Cesium = require("cesium/Cesium");
export default {
  name: "HelloWorld",
  mounted() {
    // three对象
    let three = {
      renderer: null,
      camera: null,
      scene: null,
    };

    //cesium对象
    let cesium = {
      viewer: null,
    };

    function pageload() {
      // 模型定位范围
      let minWGS84 = [115.56936458615716, 39.284100766866445];
      let maxWGS84 = [117.10745052365716, 41.107831235616445];

      // cesium 容器
      let cesiumContainer = document.getElementById("cesiumContainer");
      let _3Dobjects = []; // 可以是任何Three.js对象网格

      // three对象
      function _3DObject() {
        //THREEJS 3DObject.mesh
        this.threeMesh = null;
        //location bounding box
        this.minWGS84 = null;
        this.maxWGS84 = null;
      }

      // 初始化地球
      function initCesium() {
        cesium.viewer = new Cesium.Viewer(cesiumContainer, {
          animation: false, //动画控制,默认true
          baseLayerPicker: false, //地图切换控件(底图以及地形图)是否显示,默认显示true
          scene3DOnly: true, //设置为true,则所有几何图形以3D模式绘制以节约GPU资源
          fullscreenButton: false, //全屏按钮,默认显示true

          geocoder: false, //地名查找,默认true
          timeline: true, //时间线,默认true
          vrButton: false, //双屏模式,默认不显示false
          homeButton: false, //主页按钮,默认true
          infoBox: false, //点击要素之后显示的信息,默认true
          selectionIndicator: false, //选中元素显示,默认true
          navigationHelpButton: false, //导航帮助说明,默认true
          navigationInstructionsInitiallyVisible: false,
          automaticallyTrackDataSourceClocks: false, //自动追踪最近添加的数据源的时钟设置,默认true
          terrainExaggeration: 2.0,
          sceneModePicker: false, //是否显示地图2D2.5D3D模式
          useDefaultRenderLoop: false,
          allowTextureFilterAnisotropic: false,

          contextOptions: {
            webgl: {
              alpha: false,
              antialias: true,
              preserveDrawingBuffer: true,
              failIfMajorPerformanceCaveat: false,
              depth: true,
              stencil: false,
              anialias: false,
            },
          },

          targetFrameRate: 60,
          resolutionScale: 0.1,
          orderIndependentTranslucency: true,
          //加载底图
          imageryProvider: new Cesium.ArcGisMapServerImageryProvider({
            url:
              "https://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer",
          }),
          // creditContainer : "hidecredit", //注意:这里需要注释掉,否则会报找不到容器的问题
          dataSources: null,
          clock: null,
          terrainShadows: Cesium.ShadowMode.DISABLED,
        });

        //去掉银河,月亮,太阳,大气层
    cesium.viewer.scene.moon.show = false;
    cesium.viewer.scene.fog.enabled = false;
    cesium.viewer.scene.sun.show = true;
    cesium.viewer.scene.skyBox.show = false


        let center = Cesium.Cartesian3.fromDegrees(
          (minWGS84[0] + maxWGS84[0]) / 2,
          (minWGS84[1] + maxWGS84[1]) / 2 - 1,
          200000
        );
        cesium.viewer.camera.flyTo({
          destination: center,
          orientation: {
            heading: Cesium.Math.toRadians(0),
            pitch: Cesium.Math.toRadians(-60),
            roll: Cesium.Math.toRadians(0),
          },
          duration: 3,
        });
      }

      //初始化three
      function initThree() {
        let fov = 45;
        let width = window.innerWidth;
        let height = window.innerHeight;
        let aspect = width / height;
        let near = 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({ alpha: true });
        let Amlight = new THREE.AmbientLight(0xffffff, 2);
        three.scene.add(Amlight);
        // 注意这里,直接把three容器(canvas 添加到 cesium中,在cesium的canvas之下),
        // 这样的话,两个canvas才会重叠起来。
        cesium.viewer.cesiumWidget.canvas.parentElement.appendChild(
          three.renderer.domElement
        );
        // ThreeContainer.appendChild(three.renderer.domElement);
        var ThreeContainer = document.getElementById("ThreeContainer");
        ThreeContainer.appendChild(three.renderer.domElement);
      }

      //创建 cesium 图形,跟three无关
      function createPolygon() {
        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.BLUE.withAlpha(0.4),
          },
        };
        let Polygon = cesium.viewer.entities.add(entity);
      }

      // 加载three模型 (多面球体创建)
      function getModel(geometry) {
        // 创建多面体
        geometry = new THREE.DodecahedronGeometry();
        // 材质
        const material = new THREE.MeshBasicMaterial({ color: 0xf1f2f3 });
        // 创建物体
        let dodecahedronMesh = new THREE.Mesh(geometry, material);
        // let dodecahedronMesh = new THREE.Mesh(geometry, new THREE.MeshNormalMaterial());
        // 大小缩放倍率
        dodecahedronMesh.scale.set(5000, 5000, 5000); // 缩放相关参数 scale object to be visible at planet scale
        // z方向移动一段距离
        dodecahedronMesh.position.z += 25000.0; // 在Three.js空间中平移“向上”,使网格的“底部”为句柄
        // x方向旋转版权
        dodecahedronMesh.rotation.x = Math.PI / 2; // rotate mesh for Cesium's Y-up system

        // 创建一个组
        let dodecahedronMeshYup = new THREE.Group();
        dodecahedronMeshYup.add(dodecahedronMesh);
        // 添加场景当中
        three.scene.add(dodecahedronMeshYup); // don’t forget to add it to the Three.js scene manually
        //Assign Three.js object mesh to our object array
        // 设置模型位置
        let _3DOB = new _3DObject();
        // 存储模型
        _3DOB.threeMesh = dodecahedronMeshYup;
        // 存储模型位置坐标
        _3DOB.minWGS84 = minWGS84;
        _3DOB.maxWGS84 = maxWGS84;
        _3Dobjects.push(_3DOB);
      }

      function getDBModel(url) {
        // let url = './model/1.glb'
        // 外部模型加载
        const loader = new GLTFLoader();
        loader.load(url, (gltf) => {
          const modelss = gltf.scene;
          console.log(modelss, "加载到模型");
          modelss.scale.set(10, 10, 10); // 缩放相关参数 scale object to be visible at planet scale
          // z方向移动一段距离
          // modelss.position.z += 10.0; // 在Three.js空间中平移“向上”,使网格的“底部”为句
          modelss.rotation.x = Math.PI / 2; // rotate mesh for Cesium's Y-up system
          let dodecahedronMeshYup = new THREE.Group();
          dodecahedronMeshYup.add(modelss);
          // 添加场景当中
          three.scene.add(dodecahedronMeshYup); // don’t forget to add it to the Three.js scene manually

          let _3DOB = new _3DObject();
          // 存储模型
          _3DOB.threeMesh = dodecahedronMeshYup;
          // 存储模型位置坐标
          _3DOB.minWGS84 = minWGS84;
          _3DOB.maxWGS84 = maxWGS84;
          _3Dobjects.push(_3DOB);
        });
      }
      // 创建天空(不透明)
      function getSky() {
        // sun = new THREE.Vector3();
        var sun = new THREE.Vector3();
        // 创建天空
        const sky = new Sky();
        sky.scale.setScalar(100000);
        // 添加到场景当中
        three.scene.add(sky);

        const skyUniforms = sky.material.uniforms;
        skyUniforms["turbidity"].value = 10;
        skyUniforms["rayleigh"].value = 2;
        skyUniforms["mieCoefficient"].value = 0.005;
        skyUniforms["mieDirectionalG"].value = 0.8;
        const parameters = { elevation: 17, azimuth: 180 };

        const pmremGenerator = new THREE.PMREMGenerator(three.renderer);
        let renderTarget;

        function updateSun() {
          const phi = THREE.MathUtils.degToRad(90 - parameters.elevation);
          const theta = THREE.MathUtils.degToRad(parameters.azimuth);

          sun.setFromSphericalCoords(1, phi, theta);

          sky.material.uniforms["sunPosition"].value.copy(sun);

          if (renderTarget !== undefined) renderTarget.dispose();

          renderTarget = pmremGenerator.fromScene(sky);

          three.scene.environment = renderTarget.texture;
        }

        updateSun();
        let _3DOB = new _3DObject();
        // 存储模型
        _3DOB.mysky = true;
        _3DOB.threeMesh = sky;
        // 存储模型位置坐标
        _3DOB.minWGS84 = minWGS84;
        _3DOB.maxWGS84 = maxWGS84;
        _3Dobjects.push(_3DOB);
        // 控制天空光照
      }
      // 创建发光源
      function createLight() {}
      // 种树
      function getTree() {
        console.log("开始加载树木");
        // 加载树纹理贴图
        var textureTree = new THREE.TextureLoader().load("./textures/shu.jpg");
        console.log("创建组", textureTree);
        let dodecahedronMeshYup = new THREE.Group();

        // 批量创建表示一个树的精灵模型
        for (let i = 0; i < 10; i++) {
          var spriteMaterial = new THREE.SpriteMaterial({
            map: textureTree, //设置精灵纹理贴图
          });
          // 创建精灵模型对象
          var sprite = new THREE.Sprite(spriteMaterial);

          // 添加场景当中

          var k1 = Math.random() - 0.5;
          var k2 = Math.random() - 0.5;
          // 位置
          sprite.scale.set(1000, 1000, 1000); // 只需要设置x、y两个分量就可以
          sprite.position.set(1000 * k1, 50, 1000 * k2);
          sprite.position.z += 1000.0; // 在Three.js空间中平移“向上”,使网格的“底部”为句
          sprite.rotation.x = Math.PI / 2; // rotate mesh for Cesium's Y-up system

          // 添加到组中
          dodecahedronMeshYup.add(sprite);
        }
        // 加载树
        console.log("加载树");
        three.scene.add(dodecahedronMeshYup); // don’t forget to add it to the Three.js scene manually

        let _3DOB = new _3DObject();
        // 存储模型
        _3DOB.threeMesh = dodecahedronMeshYup;
        // 存储模型位置坐标
        _3DOB.minWGS84 = minWGS84;
        _3DOB.maxWGS84 = maxWGS84;
        _3Dobjects.push(_3DOB);
      }
      // 设置说
      function setWater() {
        // 创建一个平面
        const waterGeometry = new THREE.PlaneGeometry(300, 300);
        // 平面添加材质
        const water = new Water(waterGeometry, {
          textureWidth: 512,
          textureHeight: 512,
          // 纹理图片
          waterNormals: new THREE.TextureLoader().load(
            "textures/waternormals.jpg",
            function(texture) {
              texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
            }
          ),
          sunDirection: new THREE.Vector3(),
          sunColor: 0xffffff,
          waterColor: 0x001e0f,
          distortionScale: 8,
          fog: three.scene.fog !== undefined,
        });
        // water.rotation.x = -Math.PI / 2;
        // scene.add(water);

        // 设置位置
        water.scale.set(100, 100, 100);
        water.position.y += 100.0; // 在Three.js空间中平移“向上”,使网格的“底部”为句
        water.rotation.x = Math.PI; // rotate mesh for Cesium's Y-up system
        // let dodecahedronMeshYup = new THREE.Group();
        // dodecahedronMeshYup.add(water);
        // 添加场景当中
        three.scene.add(water); // don’t forget to add it to the Three.js scene manually

        let _3DOB = new _3DObject();
        // 存储模型
        _3DOB.myType = true;
        _3DOB.threeMesh = water;
        // 存储模型位置坐标
        _3DOB.minWGS84 = minWGS84;
        _3DOB.maxWGS84 = maxWGS84;
        _3Dobjects.push(_3DOB);
      }
      // 加载three立方体模型
      function cube() {
        const geometry = new THREE.BoxGeometry();
        const bulbLight = new THREE.PointLight(0xffee88, 1, 100, 2); // 点光源
        const material = new THREE.MeshBasicMaterial({
          emissive: 0xffffee, // 材质的放射(光)颜色,基本上是不受其他光照影响的固有颜色
          emissiveIntensity: bulbLight.intensity / Math.pow(0.02, 2.0), // 放射光强度,将光源表面的强度转换为辐照度
          color: 0x000000,
        });

        const dodecahedronMesh = new THREE.Mesh(geometry, material);
        dodecahedronMesh.scale.set(1500, 1500, 1500); //scale object to be visible at planet scale
        dodecahedronMesh.position.z += 10000.0; // translate "up" in Three.js space so the "bottom" of the mesh is the handle
        dodecahedronMesh.rotation.x = Math.PI / 2; // rotate mesh for Cesium's Y-up system

        // 加点光
        let dodecahedronMeshYup = new THREE.Group();
        dodecahedronMeshYup.add(dodecahedronMesh);
        three.scene.add(dodecahedronMeshYup); // don’t forget to add it to the Three.js scene manually
        //Assign Three.js object mesh to our object array
        let _3DOB = new _3DObject();
        _3DOB.threeMesh = dodecahedronMeshYup;
        _3DOB.minWGS84 = minWGS84;
        _3DOB.maxWGS84 = maxWGS84;
        _3Dobjects.push(_3DOB);
      }
      // 创建一个大光源

      // 创建three 对象
      function createThreeObject() {
        // getSky()
        // getModel();
        getDBModel("./model/1.glb");
        getDBModel("./model/2.glb");
        getDBModel("./model/3.glb");
        getDBModel("./model/4.glb");
        cube();
        setWater();
        // 加载树
        getTree();
      }

      // 初始化模型
      function init3DObject() {
        //Cesium entity
        // createPolygon();
        //Three.js Objects
        createThreeObject();
      }

      // cesium 渲染
      function renderCesium() {
        cesium.viewer.render();
      }

      // three.js每次刷新的操作
      function renderThreeObj() {
        // register Three.js scene with Cesium
        // 镜头同步
        three.camera.fov = Cesium.Math.toDegrees(
          cesium.viewer.camera.frustum.fovy
        ); // ThreeJS FOV is vertical
        //three.camera.updateProjectionMatrix();
        let cartToVec = function(cart) {
          return new THREE.Vector3(cart.x, cart.y, cart.z);
        };

        // 控制下水流变化 water.position.y += 100.0;
        // water.material.uniforms["time"].value += 1.0 / 60.0;

        // Configure Three.js meshes to stand against globe center position up direction
        // 将Three.js网格配置为相对于地球仪中心位置向上
        for (let id in _3Dobjects) {
          // 水流动一动
          if (_3Dobjects[id].myType)
            _3Dobjects[id].threeMesh.material.uniforms["time"].value +=
              1.0 / 60.0;

          minWGS84 = _3Dobjects[id].minWGS84;
          maxWGS84 = _3Dobjects[id].maxWGS84;
          // convert lat/long center position to Cartesian3
          let center = Cesium.Cartesian3.fromDegrees(
            (minWGS84[0] + maxWGS84[0]) / 2,
            (minWGS84[1] + maxWGS84[1]) / 2
          );
          // get forward direction for orienting model
          let centerHigh = Cesium.Cartesian3.fromDegrees(
            (minWGS84[0] + maxWGS84[0]) / 2,
            (minWGS84[1] + maxWGS84[1]) / 2,
            1
          );
          // use direction from bottom left to top left as up-vector
          let bottomLeft = cartToVec(
            Cesium.Cartesian3.fromDegrees(minWGS84[0], minWGS84[1])
          );
          let topLeft = cartToVec(
            Cesium.Cartesian3.fromDegrees(minWGS84[0], maxWGS84[1])
          );
          let latDir = new THREE.Vector3()
            .subVectors(bottomLeft, topLeft)
            .normalize();
          // configure entity position and orientation
          // 配置实体位置和方向
          _3Dobjects[id].threeMesh.position.copy(center); // 控制更新后的模型位置
          // if(_3Dobjects[id].myType) _3Dobjects[id].threeMesh.position.y += 10.0;

          _3Dobjects[id].threeMesh.lookAt(
            centerHigh.x,
            centerHigh.y,
            centerHigh.z
          );
          _3Dobjects[id].threeMesh.up.copy(latDir);
        }
        // Clone Cesium Camera projection position so the
        // Three.js Object will appear to be at the same place as above the Cesium Globe
        // 克隆铯相机的投影位置,使Three.js对象看起来与铯地球仪上方的位置相同
        three.camera.matrixAutoUpdate = false;
        let cvm = cesium.viewer.camera.viewMatrix;
        let civm = cesium.viewer.camera.inverseViewMatrix;

        // 注意这里,经大神博客得知,three高版本这行代码需要放在 three.camera.matrixWorld 之前
        three.camera.lookAt(0, 0, 0);

        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]
        );

        // 设置three宽高
        let width = cesiumContainer.clientWidth;
        let height = cesiumContainer.clientHeight;

        let aspect = width / height;
        three.camera.aspect = aspect;
        three.camera.updateProjectionMatrix();
        three.renderer.setSize(width, height);
        three.renderer.clear();
        three.renderer.render(three.scene, three.camera);
      }

      // 同步
      function loop() {
        requestAnimationFrame(loop);
        renderCesium();
        renderThreeObj();
      }

      initCesium(); // Initialize Cesium renderer
      initThree(); // Initialize Three.js renderer
      init3DObject(); // Initialize Three.js object mesh with Cesium Cartesian coordinate system
      loop(); // Looping renderer
    }

    pageload();
  },
};
</script>

<style scoped>
/*设置cesium和three的画布位置*/
.container-integrate canvas {
  position: absolute;
  top: 0;
}
/*three画布禁止鼠标操作*/
.container-integrate canvas:nth-child(3) {
  pointer-events: none;
}

#ThreeContainer {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  margin: 0;
  overflow: hidden;
  padding: 0;
  font-family: sans-serif;
  /* 禁止鼠标操做 */
  pointer-events: none;
}
</style>

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值