• 一、Cesium前置知识:
  • 二、Cesium开始开发:
  • 三、Cesium开发基础:
  • 四、Cesium开发进阶:

15 - Cesium视频总结

一、Cesium前置知识:

最新使用cesium的1.93版本。

1,Cesium支持的数据格式:

2,Cesium在项目中的定位:

3,Cesium学习路线:

Viewer类 ——> Camera类 ——> ImageryLayer类 ——> TerrainProvider类 ——> Entity类 ——> Cesium3DTileset类 ——> Primitive类 ——> Fabric类 ——> ParticleSystem类

4,Cesium进阶方向:

二、Cesium开始开发:

1,Vue3 + Cesium配置方案:

(1)安装依赖:

"dependencies": {
    "cesium": "^1.93.0",
    "sass": "^1.70.0",
    "vue": "^3.3.11"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^4.5.2",
    "vite": "^5.0.8",
    "vite-plugin-cesium": "^1.2.22",
    "vue-router": "^4.2.5"
  }

(2)在vite.config.js中添加插件:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import cesium from 'vite-plugin-cesium'
import path from 'path'

export default defineConfig({
  plugins: [vue(), cesium()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src')
    }
  }
})

(3)添加Home.vue,并引入Cesium,同时去掉Cesium默认组件:

<script setup>
import * as Cesium from 'cesium'
import { onMounted } from 'vue'

onMounted(() => {
  const viewer = new Cesium.Viewer('cesiumContainer', {
    animation: false,   // 动画小组件
    baseLayerPicker: false,  // 底图组件,选择三维数字地球的底图
    fullscreenButton: false,  // 全屏组件
    vrButton: false,    // VR模式
    geocoder: false,    // 地理编码搜索组件
    homeButton: false,  // 首页,点击之后将视图跳转到默认视角
    infoBox: false,     // 信息框
    sceneModePicker: false,  // 场景模式,切换2D、3D和Columbus Views(CV)模式
    selectionIndicator: false,  // 是否显示选取指示器组件
    timeline: false,    // 时间轴
    navigationHelpButton: false,    // 帮助提示,如何操作数字地球
    navigationInstructionsInitiallyVisible: false,
    // 添加地形效果
    // terrain: new Cesium.Terrain.fromWorldTerrain({
    //   url: Cesium.IonResource.fromAssetId(3956),
    //   requestWaterMask: true,    // 请求水体效果所需要的海水波浪数据
    //   requestVertexNormals: true   // 请求地形照明数据
    // })
    // 添加Cesium官方图层
    baseLayer: Cesium.ImageryLayer.fromProviderAsync(
      Cesium.TileMapServiceImageryProvider.fromUrl(
        // 给定 Cesium 基本 URL 下的相对 URL,返回绝对 URL。
        // http://localhost:5173/cesium/Assets/Textures/NaturalEarthII
     Cesium.buildModuleUrl("Assets/Textures/NaturalEarthII"),
      )),
  })
})
</script>

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

<style lang="scss">
#cesiumContainer {
  width: 100vw;
  height: 100vh;
    .cesium-viewer-bottom {
    visibility: hidden;
  }
}
</style>

2,Cesium源码目录:

(1)前端打包方式整理:

1,gulp:Cesium源码打包方式,也用到了rollup。
2,webpack和vite:单页面应用程序(SPA)的打包方式,比如Vue、React工程打包。
3,rollup:SDK库文件打包方式,比如Vue、React库文件打包。

(2)Cesium源码目录:

(3)Cesium导航首页:

3,Cesium初始化:

把Cesium初始页面除了地球之外所有控件删除。Cesium初始化

<script setup>
import * as Cesium from 'cesium'
import { onMounted } from 'vue'

onMounted(() => {
  const viewer = new Cesium.Viewer('cesiumContainer', {
    animation: false,   // 动画小组件
    baseLayerPicker: false,  // 底图组件,选择三维数字地球的底图
    fullscreenButton: false,  // 全屏组件
    vrButton: false,    // VR模式
    geocoder: false,    // 地理编码搜索组件
    homeButton: false,  // 首页,点击之后将视图跳转到默认视角
    infoBox: false,     // 信息框
    sceneModePicker: false,  // 场景模式,切换2D、3D和Columbus Views(CV)模式
    selectionIndicator: false,  // 是否显示选取指示器组件
    timeline: false,    // 时间轴
    navigationHelpButton: false,    // 帮助提示,如何操作数字地球
    navigationInstructionsInitiallyVisible: false
  })
})
</script>

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

<style lang="scss">
#cesiumContainer {
  width: 100vw;
  height: 100vh;

  .cesium-viewer-bottom {
    visibility: hidden;
  }
}
</style>

4,API目录:

API整理

5,Cesium添加天地图和MapBox底图:

以下这个小方法可以拿到所有默认图层的集合:

// 拿到所有默认图层配置的集合
console.log(Cesium.createDefaultImageryProviderViewModels())

Cesium通过WMTS或者XYZ的方式都可以添加天地图,其中,WMTS需要使用Cesium.WebMapTileServiceImageryProvider类,TMS需要使用Cesium.UrlTemplateImageryProvider类。

(1)通过WMTS方式添加天地图矢量图层:

OGC各项服务介绍:

viewer.imageryLayers.addImageryProvider(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=" + tianditutk,
    layer: "tdtVecBasicLayer",
    style: "default",
    format: "image/jpeg",
    tileMatrixSetID: "GoogleMapsCompatible",
    show: true
  }))

或者是:

const viewer = new Cesium.Viewer('cesiumContainer', {
      animation: false,   // 动画小组件
      baseLayerPicker: false,  // 底图组件,选择三维数字地球的底图
      fullscreenButton: false,  // 全屏组件
      vrButton: false,    // VR模式
      geocoder: false,    // 地理编码搜索组件
      homeButton: false,  // 首页,点击之后将视图跳转到默认视角
      infoBox: false,     // 信息框
      sceneModePicker: false,  // 场景模式,切换2D、3D和Columbus Views(CV)模式
      selectionIndicator: false,  // 是否显示选取指示器组件
      timeline: false,    // 时间轴
      navigationHelpButton: false,    // 帮助提示,如何操作数字地球
      navigationInstructionsInitiallyVisible: false,
      mapProjection: new Cesium.WebMercatorProjection(),
      baseLayer: new Cesium.ImageryLayer(new Cesium.WebMapTileServiceImageryProvider({
        url: new Cesium.Resource({
          url: `${tdtUrl}vec_w/wmts?tk=${token}`
        }),
        layer: "vec",
        style: "default",
        format: "tile",
        tileMatrixSetID: "w",
        show: true
      }))
    })

(2)通过TMS方式添加天地图影像图层:

viewer.imageryLayers.addImageryProvider(new Cesium.UrlTemplateImageryProvider({
    url: 'http://t0.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=590447cdbdf97775b57588a69ebb903d',
  })

或者是:

const viewer = new Cesium.Viewer('cesiumContainer', {
      animation: false,   // 动画小组件
      baseLayerPicker: false,  // 底图组件,选择三维数字地球的底图
      fullscreenButton: false,  // 全屏组件
      vrButton: false,    // VR模式
      geocoder: false,    // 地理编码搜索组件
      homeButton: false,  // 首页,点击之后将视图跳转到默认视角
      infoBox: false,     // 信息框
      sceneModePicker: false,  // 场景模式,切换2D、3D和Columbus Views(CV)模式
      selectionIndicator: false,  // 是否显示选取指示器组件
      timeline: false,    // 时间轴
      navigationHelpButton: false,    // 帮助提示,如何操作数字地球
      navigationInstructionsInitiallyVisible: false,
      mapProjection: new Cesium.WebMercatorProjection(),
    // 添加影像底图
      baseLayer: new Cesium.ImageryLayer(new Cesium.UrlTemplateImageryProvider({
        url: 'http://t0.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=590447cdbdf97775b57588a69ebb903d',
      }))
    })

(3)添加MapBox底图:

// 添加mapbox地图,此token过期了
viewer.imageryLayers.addImageryProvider(
    new Cesium.MapboxImageryProvider({
      mapId: ''mapbox.satellite'',
      accessToken: 'pk.eyJ1IjoiY2hlbmdiZW5jaGFvIiwiYSI6ImNsODU3aGRiODA0Y2UzcHBzZmFlcmdqZ2sifQ.8k59W_pB_Riwe6o-MneRlA'
    })
  )

三、Cesium开发基础:

1,Cesium坐标系:

屏幕坐标、地理坐标(弧度)、地理坐标(经纬度)和世界坐标之间的转换。

(1)屏幕坐标(经纬度):new Cesium.Cartesian2(x, y)
(2)笛卡尔空间直角坐标系(世界坐标):new Cesium.Cartesian3(x, y, z),通常用于平移、旋转和缩放
(3)地理坐标(弧度):new Cesium.Cartographic(lng, lat, height)
(4)经纬度坐标(经纬度):Cesium默认是WGS84地理坐标系
// 弧度转经纬度
Cesium.Math.toDegrees(degrees)
// 经纬度转弧度 
Cesium.Math.toRadians(radians)   
//1, 经纬度转笛卡尔坐标(世界坐标), cartesian是笛卡尔的意思
const catesian1 = Cesium.Cartesian3.fromDegrees(110, 20, 20)
const catesianArr = Cesium.Cartesian3.fromDegreesArray([
      120, 20,
      121, 23
    ])
console.log(catesian1)
// 弧度制转笛卡尔坐标(世界坐标)
const b = Cesium.Cartesian3.fromRadians(a.longitude, a.latitude, a.height)
console.log(b)
// 2,世界坐标转经纬度坐标
// 笛卡尔坐标转弧度地理坐标
const a = Cesium.Cartographic.fromCartesian(catesian1)
// const alat = 180 / Math.PI * a.latitude
// const alng = 180 / Math.PI * a.longitude
// 弧度地理坐标转角度坐标
  console.log(b)
const alng = Cesium.Math.toDegrees(a.longitude)
const alat = Cesium.Math.toDegrees(a.latitude)
console.log(alng)
console.log(alat)
// 3,二维屏幕坐标转三维笛卡尔空间直角坐标系(世界坐标)
const cartesian3 = viewer.scene.globe.pick(
	viewer.camera.getPickRay(windowPosition),
        scene
)
// 4,三维笛卡尔空间直角坐标转为二维屏幕坐标,结果是Cartesian2对象,取出X,Y即为屏幕坐标。
const windowPosition = Cesium.SceneTransforms.wgs84ToWindowCoordinates(scene, cartesian3);
// 或者是:
const canvasPosition = viewer.scene.cartesianToCanvasCoordinates(cartesian1)

2,Cesium坐标转换工具:

3,Cesium加载影像数据:

主要接口:ImageryLayerCollection ——> ImageryLayer ——> ImageryProvider

其中,ImageryProvider分为九个大类,十一种类型,分别是:

ImageryProvider类2

(1)加载天地图矢量图层:

// viewer.scene.imageryLayers和viewer.imageryLayers是同一对象
console.log(viewer.scene.imageryLayers === viewer.imageryLayers)               // 返回true
// 1,加载天地图图层
const layer = new Cesium.UrlTemplateImageryProvider({
    url: 'http://t1.tianditu.com/vec_w/wmts?layer=vec&tilematrixset=w&Service=WMTS&Request=GetTile&Version=1.0.0&TileMatrix={z}&TileCol={x}&TileRow={y}&tk=590447cdbdf97775b57588a69ebb903d'
  })
  // 添加图层,并设置图层透明度
  viewer.imageryLayers.addImageryProvider(layer).alpha = 0.5

(2)加载mapbox图层:

// 2,加载mapbox图层,下面加载的mapbox图层中,除了第一个卫星图之外都请求不到资源了,报410的状态码
  const mapIds = [
    'mapbox.satellite',
    'mapbox.streets',
    'mapbox.streets-basic',
    'mapbox.light',
    'mapbox.streets-satellite',
    'mapbox.wheatpaste',
    'mapbox.comic',
    'mapbox.outdoors',
    'mapbox.run-bike-hike',
    'mapbox.pencil',
    'mapbox.pirates',
    'mapbox.emerald',
    'mapbox.high-contrast'
  ]
  viewer.imageryLayers.addImageryProvider(
    new Cesium.MapboxImageryProvider({
      mapId: mapIds[0],
      accessToken: 'pk.eyJ1IjoiY2hlbmdiZW5jaGFvIiwiYSI6ImNsODU3aGRiODA0Y2UzcHBzZmFlcmdqZ2sifQ.8k59W_pB_Riwe6o-MneRlA'
    })
  )

4,Cesium加载地形数据:

主要接口:Cesium.CesiumProvider()

Cesium各类地形数据的加载

// cesium版本1.107.0之前的加载地形方式
viewer.terrainProvider = new Cesium.CesiumTerrainProvider({
    url: 'https://assets.agi.com/stk-terrain/world' 
});

cesium版本更新之后,删除了 viewer.terrainProvider 接口,其加载地形方式发生了改变:

第一种方式:调用Cesium.createWorldTerrainAsync()

// cesium版本1.107.0之后的加载地形方式
const addWorldTerrainAsync = async (viewer) => {
    try {
      const terrainProvider = await Cesium.createWorldTerrainAsync({
        requestWaterMask: true,
        requestVertexNormals: true,
      });
      viewer.terrainProvider = terrainProvider;
    } catch (error) {
      console.log(`Failed to add world imagery: ${error}`);
    }
  }
  addWorldTerrainAsync(viewer)
  viewer.scene.globe.enableLighting = true   // 对大气和雾启用实时动态照明效果
  // 启用深度测试,让地形后面的东西消失。为true实现高程遮挡(在没有地形时候也会有高程遮挡效果),为false则绘制与添加的图元均在地球顶部不会出现遮挡问题,按实际应用情况设置
  viewer.scene.globe.depthTestAgainstTerrain = true
  console.log(viewer.scene.imageryLayers === viewer.imageryLayers)
})

第二种方式:在创建Viewer时直接添加地形:

const viewer = new Cesium.Viewer('cesiumContainer', {
     terrain: new Cesium.Terrain.fromWorldTerrain({
     url: Cesium.IonResource.fromAssetId(3956),
     requestWaterMask: true,    // 请求水体效果所需要的海水波浪数据
     requestVertexNormals: true   // 请求地形照明数据
     })
  })

5,Cesium加载矢量数据:

(1)Cesium支持的矢量数据格式有:geojson、topojson、kml、具有时间特性的czml。

DataSourceCollection有:

CustomDataSource、CzmlDataSource、GeoJsonDataSource、KmlDataSource

Cesium添加CustomDataSource,是Cesium自定义的数据类型,比如entity:

const dataSource = new Cesium.CustomDataSource('myData');

const entity = dataSource.entities.add({
   position : Cesium.Cartesian3.fromDegrees(1, 2, 0)
});

viewer.dataSources.add(dataSource);

(2)Topjson介绍:

在线生成TopoJSON和GeoJSON的网站:https://geojson.io/

topojson和geojson面数据示例:

{"type":"Topology","objects":{"collection":{"type":"GeometryCollection","geometries":[{"type":"Polygon","arcs":[[0]]}]}},"arcs":[[[115.22446921011499,33.607866983978],[115.22446921011499,29.852609554190323],[117.75358850351438,29.852609554190323],[117.75358850351438,33.607866983978],[115.22446921011499,33.607866983978]]],"bbox":[115.22446921011499,29.852609554190323,117.75358850351438,33.607866983978]}
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "coordinates": [
          [
            [
              115.22446921011499,
              33.607866983978
            ],
            [
              115.22446921011499,
              29.852609554190323
            ],
            [
              117.75358850351438,
              29.852609554190323
            ],
            [
              117.75358850351438,
              33.607866983978
            ],
            [
              115.22446921011499,
              33.607866983978
            ]
          ]
        ],
        "type": "Polygon"
      }
    }
  ]
}

添加TopJSON或GeoJSON数据:

// 1,添加GeoJSON数据源,返回一个Promise
  const dataSource = Cesium.GeoJsonDataSource.load('/polygon.topojson', {
    stroke: Cesium.Color.RED,
    strokeWidth: 3,
    fill: Cesium.Color.PINK.withAlpha(0.5)
  })
  // 2,把数据源添加到viewer中
  dataSource.then(res => {
    viewer.dataSources.add(res)
    viewer.flyTo(res)
  })

(3)KML介绍:

(4)CZML介绍:

CZML格式的数据是一种时态数据,可以做Cesium轨迹动画。

CZML Structure · AnalyticalGraphicsInc/czml-writer Wiki (github.com)open in new window

CZML添加一个点简单示例:

const czml = [
  {
    id: "document",
    name: "CZML Point",
    version: "1.0",
  },
  {
    id: "point 1",
    name: "point",
    position: {
      cartographicDegrees: [-111.0, 40.0, 0],
    },
    point: {
      color: {
        rgba: [255, 255, 255, 255],
      },
      outlineColor: {
        rgba: [255, 0, 0, 255],
      },
      outlineWidth: 4,
      pixelSize: 20,
    },
  },
];

const viewer = new Cesium.Viewer("cesiumContainer");
const dataSourcePromise = Cesium.CzmlDataSource.load(czml);
viewer.dataSources.add(dataSourcePromise);
viewer.zoomTo(dataSourcePromise);

6,Cesium加载3D Tiles数据:

(1)3D Tiles数据介绍:

带有LOD(层次细节模型)的glTF,专门为流式传输和渲染海量3D地理空间数据设计,例如倾斜摄影、3D建模、BIM/CAD、实例化要素集的点云。

![](.\cesium基础知识\3D Tiles数据介绍.jpg)

![](.\cesium基础知识\3D Tiles数据结构介绍.jpg)

![](.\cesium基础知识\3D Tiles数据结构.jpg)

3D Tiles相关类介绍:

cesium更新版本后将Cesium3DTileFeature类的getPropertyNames()换成getPropertyIds()。

![](.\cesium基础知识\3D Tiles相关类.jpg)

(2)添加3D Tiles数据代码:

// 添加3D Tiles的数据源,需要使用异步函数加载
  async function add3DTile() {
    const tileset = await Cesium.Cesium3DTileset.fromUrl(
      "/Tileset/tileset.json"       // 瓦片索引
    )
    // 1,添加tileset
    viewer.scene.primitives.add(tileset)
    // 视图定位
    viewer.zoomTo(tileset, new Cesium.HeadingPitchRange(
      0.0, -0.5, tileset.boundingSphere.radius * 2.0
    ))
    const boundingSphere = tileset.boundingSphere
    console.log(boundingSphere.center)
    // 2,设置样式
    const properties = tileset.properties
    if (Cesium.defined(properties) && Cesium.defined(properties.Height)) {
      tileset.style = new Cesium.Cesium3DTileStyle({
        color: {
          conditions: [
            ["${Height} >= 83", "color('purple', 0.5)"],
            ["${Height} >= 80", "color('red')"],
            ["${Height} >= 70", "color('orange')"],
            ["${Height} >= 12", "color('yellow')"],
            ["${Height} >= 7", "color('lime')"],
            ["${Height} >= 1", "color('cyan')"],
            ["true", "color('blue')"]
          ]
        }
      })
    }
    // 3,添加鼠标拾取要素
    const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas)
    // 绑定鼠标左点击事件
    handler.setInputAction((e) => {
      console.log(e)
      let windowPosition = e.position   // 屏幕坐标
      // 屏幕坐标转世界坐标
      const ray = viewer.camera.getPickRay(windowPosition)
      const catesian = viewer.scene.globe.pick(ray, viewer.scene)
      // 世界坐标转经纬度坐标
      const lat = Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(catesian).latitude)
      const lng = Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(catesian).longitude)
      console.log('经度坐标是:' + lng + ';纬度坐标是:' + lat)
      // 获取要素
      const feature = viewer.scene.pick(windowPosition)
      if (Cesium.defined(feature) && feature instanceof Cesium.Cesium3DTileFeature) {
        const propertyIds = feature.getPropertyIds()
        console.log(propertyIds)
        const length = propertyIds.length
        for (let i = 0; i < length; ++i) {
          let propertyId = propertyIds[i]
          console.log(propertyId + ': ' + feature.getProperty(propertyId))
        }
      }

    }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
  }
  add3DTile()

(3)3D Tiles制作平移动画:

![](.\cesium基础知识\3D Tiles平移动画.png)

// 4,3DTiles制作向上平移的动画
    const adjustTilesetHeight = (tileset, height = 10) => {
      // 计算出模型模型包围球的中心点,转弧度制
      const cartography = Cesium.Cartographic.fromCartesian(tileset.boundingSphere.center)
      // 计算与模型包围球中心点经纬度相同的地表点位
      const surface = Cesium.Cartesian3.fromRadians(
        cartography.longitude, cartography.latitude,
        0
      )
      // 计算调整高度后的模型包围球的中心点
      const offset = Cesium.Cartesian3.fromRadians(
        cartography.longitude, cartography.latitude,
        height
      )
      // 计算差向量
      const diff = Cesium.Cartesian3.subtract(
        offset, surface, new Cesium.Cartesian3()
      )
      console.log(diff);
      // 修改模型的变换矩阵
      tileset.modelMatrix = Cesium.Matrix4.fromTranslation(diff)
    }
    let h = 10
    setInterval(() => {
      adjustTilesetHeight(tileset, h++)
    }, 1000)

7,Cesium加载GLTF数据:

(1)GLTF数据介绍:

1,scene: glTF格式的场景结构描述条目。它通过引用node来定义场景图;
2,node: 场景图层次中的一个节点。它可以包含一个变换(比如旋转或平移),并且可以引用其他(子)节点 。此外,它可以引用网格和相机,以及描述网格变换的蒙皮;
3,camera: 定义了用于渲染场景的视锥体配置;
4,mesh: 描述了出现在场景中几何对象实际的几何数据。它是指accessor用于访问实际几何数据material的对象,并且是指在渲染对象时定义其外观的 ;
5,skin: 定义了用于蒙皮的参数,参数的值通过一个accessor对象获得。
6,animation: 描述了一些结点如何随时间进行变换(比如旋转或平移);
7,accessor: 一个访问任意数据的抽象数据源。被mesh、skin和animation元素使用来提供几何数据、蒙皮参数和基于时间的动画值。它通过引用一个bufferView对象,来引用实际的二进制数据;
8,material: 包含了定义3D对象外观的参数。它通常引用了用于3D对象渲染的texture对象;
9,texture: 定义了一个sampler对象和一个image对象。sampler对象定义了image对象在3D对象上的张贴方式。

(2)添加GLTF数据代码:

GLTF加载可以使用两种方式:一种添加entity,一种添加primitive。

// 添加GLTF数据源
  async function addGLTF() {
    viewer.entities.removeAll()
    const position = Cesium.Cartesian3.fromDegrees(
      120, 45
    )
    const heading = Cesium.Math.toRadians(0)
    const pitch = 0
    const roll = 0
    const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll)
    const orientation = Cesium.Transforms.headingPitchRollQuaternion(
      position,
      hpr
    )
    // 第一种方式:通过enitity方式
    const entity = viewer.entities.add({
      name: "ferrari.glb",
      position: position,
      orientation: orientation,
      model: {
        uri: "ferrari.glb",
        minimumPixelSize: 128,    // 最小像素大小
        maximumScale: 20000,   // 模型的最大比例尺大小
        scale: 1,   // 缩放比例
        incrementallyLoadTextures: true,   // 加载模型后的纹理是否可以继续流入
        runAnimations: true,     // 指定是否应运行 glTF 动画
        clampAnimations: true,    // 指定 glTF 动画是否应在不带关键帧的持续时间内保持最后一个姿势
        shadows: Cesium.ShadowMode.ENABLED,   // 指定模型是否 投射或接收来自光源的阴影
        heightReference: Cesium.HeightReference.NONE  // 表示相对于地形的位置。NONE表示绝对位置 
      }
    })
    viewer.trackedEntity = entity
    // 第二种方式:通过primitive方式
    // 加载调试primitive和地形的工具
    viewer.extend(Cesium.viewerCesiumInspectorMixin)
    const origin = Cesium.Cartesian3.fromDegrees(120, 46)
    const modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin)
    const model = await Cesium.Model.fromGltfAsync({
      url: 'ferrari.gltf',
      modelMatrix,     // primitive只能通过模型矩阵方式加载位置
      scale: 1,       // 缩放比例
      minimumPixelSize: 128,    // 最小像素大小
      maximumScale: 20000,   // 模型的最大比例尺大小
      incrementallyLoadTextures: true,   // 加载模型后的纹理是否可以继续流入
      clampAnimations: true,    // 指定 glTF 动画是否应在不带关键帧的持续时间内保持最后一个姿势
      shadows: Cesium.ShadowMode.ENABLED,   // 指定模型是否 投射或接收来自光源的阴影
      heightReference: Cesium.HeightReference.NONE  // 表示相对于地形的位置。NONE表示绝对位置
    })
    viewer.scene.primitives.add(model)
  }
  addGLTF()

(3)添加有颜色的GLTF数据代码:

color:模型颜色。

colorBlendMode:颜色混合模式。HIGHLIGHT、MIX、REPLACE

colorBlendAmount:控制颜色混入的程度。

silhouetteColor:线框颜色。

silhouetteSize:模型边框大小。

// 添加有颜色的GLTF数据代码
const position = Cesium.Cartesian3.fromDegrees(
      120, 45
    )
    const heading = Cesium.Math.toRadians(0)
    const pitch = 0
    const roll = 0
    const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll)
    const orientation = Cesium.Transforms.headingPitchRollQuaternion(
      position,
      hpr
    )
    // 第一种方式:通过enitity方式
    const entity = viewer.entities.add({
      name: "ferrari.glb",
      position: position,
      orientation: orientation,
      model: {
        uri: "ferrari.glb",
        minimumPixelSize: 128,
        maximumScale: 20000,
        scale: 1,   // 缩放比例
        incrementallyLoadTextures: true,   // 加载模型后的纹理是否可以继续流入
        runAnimations: true,     // 指定是否应运行 glTF 动画
        clampAnimations: true,    // 指定 glTF 动画是否应在不带关键帧的持续时间内保持最后一个姿势
        shadows: Cesium.ShadowMode.ENABLED,   // 指定模型是否 投射或接收来自光源的阴影
        heightReference: Cesium.HeightReference.NONE,  // 表示相对于地形的位置。NONE表示绝对位置 
        color: Cesium.Color.RED,  // 与模型的渲染颜色混合的颜色
        colorBlendMode: Cesium.ColorBlendMode.MIX, // 颜色混合模式,HIGHLIGHT、MIX、REPLACE
        colorBlendAmount: 0.3,  // colorBlendMode是MIX时有效,控制颜色混入的程度
        silhouetteColor: Cesium.Color.WHITE.withAlpha(0.5),  // 白色线框,模型边框的颜色
        silhouetteSize: 5    // 模型边框大小
      }
    })
    viewer.trackedEntity = entity

8,Cesium添加Primitive:

(1)Entity和Primitive区别:

(2)Cesium添加Primitive并做定位效果:

Primitive不是Enitity,不能通过viewer.trackedEntity = entity定位到entity上,只能使用viewer.camera.flyTo(primitive)或viewer.zoomTo(primitive)的方式制作定位效果。

// 第二种方式:通过primitive方式
    // 加载调试primitive和地形的工具
    viewer.extend(Cesium.viewerCesiumInspectorMixin)
    const origin = Cesium.Cartesian3.fromDegrees(120, 46)
    const modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin)
    const model = Cesium.Model.fromGltfAsync({
      url: 'ferrari.gltf',
      modelMatrix,     // primitive只能通过模型矩阵方式加载位置
      scale: 1,       // 缩放比例
      minimumPixelSize: 128,    // 最小像素大小
      maximumScale: 20000,   // 模型的最大比例尺大小
      incrementallyLoadTextures: true,   // 加载模型后的纹理是否可以继续流入
      clampAnimations: true,    // 指定 glTF 动画是否应在不带关键帧的持续时间内保持最后一个姿势
      shadows: Cesium.ShadowMode.ENABLED,   // 指定模型是否 投射或接收来自光源的阴影
      heightReference: Cesium.HeightReference.NONE  // 表示相对于地形的位置。NONE表示绝对位置
    })
    model.then(res => {
      viewer.scene.primitives.add(res)
      // 定位到primitive
      viewer.camera.flyTo({
        destination: Cesium.Cartesian3.fromDegrees(120, 46, 100),
        orientation: {//设置相机的Heading,Pitch,Roll,参照上图
          heading: Cesium.Math.toRadians(90.0),
          pitch: Cesium.Math.toRadians(-90),
          roll: 0.0
        },
        duration: 3
      })
    })

(3)Cesium开启深度测试和不开启深度测试的效果:

首先,假设Cesium是添加了地形图层,因为这样才能看出添加3D Tiles在不在地下。

开启深度测试效果:

viewer.scene.globe.depthTestAgainstTerrain = true

关闭深度测试效果:

可见,关闭深度测试效果的时候,模型会陷到地面下去,造成模型显示不真实的效果。

viewer.scene.globe.depthTestAgainstTerrain = false

9,Cesium小工具:

(1)显示帧速度:

// 显示帧速度
  viewer.scene.debugShowFramesPerSecond = true

(2)开启深度测试,控制视角不转到地下:

让地形后面的东西消失。为true实现高程遮挡(在没有地形时候也会有高程遮挡效果),为false则绘制与添加的图元均在地球顶部不会出现遮挡问题,按实际应用情况设置

viewer.scene.globe.depthTestAgainstTerrain = true

(3)开启阴影并添加光照:

viewer.shadows = true
viewer.scene.light = new Cesium.DirectionLight({
    direction: window.Cesium.Cartesian3.fromElements(-0.2, -0.5, -0.8),
    intensity: 1
})

(4)关闭光照:

viewer.scene.globe.enableLighting = false  // 开启光照

(5)开启Primitive辅助工具:

viewer.extend(Cesium.viewerCesiumInspectorMixin)

(6)开启Cesium动画效果:

viewer.shouldAnimate = true

(7)显示星空、地球、太阳、月亮和背景颜色:

viewer.scene.skyBox.show = true; //是否显示星空
viewer.scene.sun.show = true; //是否显示太阳
viewer.scene.moon.show = true; //是否显示月亮
viewer.scene.globe.show = true; //是否显示地球
viewer.scene.backgroundColor = Cesium.Color.BLUE;//地球背景颜色

四、Cesium开发进阶:

Entity配置Graphic和Material,Primitive配置Geometry和Apparence。

1,Cesium开启雾和大气效果:

使用到的接口有全部是viewer.scene下的

Fog、Globe、skyAtmosphere、highDynamicRange

viewer.scene.globe.enableLighting = true  // 开启光照
// 雾
viewer.scene.fog.enabled = true  // 开启雾效果
viewer.scene.fog.minimumBrightness = 0.1   // 雾效果最小亮度
viewer.scene.fog.density = 0.0003  // 浓度
// 地表大气效果
viewer.scene.globe.showGroundAtmosphere = true  // 开启地表大气效果
viewer.scene.globe.atmosphereLightIntensity = 10   // 设置地表大气亮度
// 天空大气效果
viewer.scene.skyAtmosphere.show = true
viewer.scene.skyAtmosphere.atmosphereLightIntensity = 50  // 天空大气效果亮度
// HDR效果
viewer.scene.highDynamicRange = true

2,Cesium卷帘分析:

思路:首先在scene中添加两个图层,设置其中一个图层或两个图层的分割方向,然后设置scene的分割距离。下面是核心代码:

// 添加图层方向
      this.earthAtNightClip.splitDirection = Cesium.SplitDirection.LEFT
// 添加图层分割位置
      this.scene.splitPosition = 0.5

// 添加卷帘分析
function addRollerShutterClip(RollerShutterClipChecked) {
    const layers = this.imageryLayers
    let moveAction = false
    const move = (movement) => {
      if (!moveAction) return
      const relativeOffset = movement.endPosition.x
      const splitPosition = (this.slider.offsetLeft + relativeOffset) / this.slider.parentElement.offsetWidth
      this.slider.style.left = `${100.0 * splitPosition}%`
      // 改变图层分割位置
      this.scene.splitPosition = splitPosition
    }
    if (this.earthAtNightClip && this.slider && RollerShutterClipChecked) {
      layers.remove(this.earthAtNightClip)
      this.earthAtNightClip = null
      document.getElementsByClassName('cesium-main')[0].removeChild(this.slider)
      this.slider = null
    } else {
      this.earthAtNightClip = layers.addImageryProvider(new Cesium.UrlTemplateImageryProvider({
        url: 'http://t1.tianditu.com/img_w/wmts?layer=img&tilematrixset=w&Service=WMTS&Request=GetTile&Version=1.0.0&TileMatrix={z}&TileCol={x}&TileRow={y}&tk=590447cdbdf97775b57588a69ebb903d'
      }))
      // 添加图层方向
      this.earthAtNightClip.splitDirection = Cesium.SplitDirection.LEFT
      // 添加图层分割位置
      this.scene.splitPosition = 0.5
      // 添加滑动控件slider
      this.slider = document.createElement('div')
      this.slider.style.width = 10 + 'px'
      this.slider.style.height = 50 + 'px'
      this.slider.style.position = 'absolute'
      this.slider.style.top = '50%'
      this.slider.style.left = '50%'
      this.slider.style.backgroundColor = 'brown'
      this.slider.style.cursor = 'pointer'
      document.getElementsByClassName('cesium-main')[0].appendChild(this.slider)
      const handler = new Cesium.ScreenSpaceEventHandler(this.slider)
      handler.setInputAction(() => {
        moveAction = true
      }, Cesium.ScreenSpaceEventType.LEFT_DOWN)
      handler.setInputAction(move, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
      handler.setInputAction(() => {
        moveAction = false
      }, Cesium.ScreenSpaceEventType.LEFT_UP)
    }
  }

3,Cesium的四种pick的区别:

viewer.scene.pick(windowPosition):适用于3D Tiles、entity、primitive的选取。

viewer.scene.globe.pick(ray, viewer.scene):适用于拾取有地形高度的点的坐标,需要开启深度检测。

viewer.scene.camera.pickEllipsoid(movement.position, viewer.scene.globe.ellipsoid):适用于拾取没有地形高度的点的坐标。

viewer.scene.pickPosition(movement.position):适用于获取地球和各个实体点的坐标,需要开启深度检测。

cesium中的四种拾取pick - 一梦、 - 博客园 (cnblogs.com)open in new window

Cesium的拾取问题总结 - 知乎 (zhihu.com)open in new window

4,CZML制作动画:

(1)CZML制作动态点的动画:

注意:如果想要制作CZML动画,需要打开Cesium动画效果,viewer.shouldAnimate = true

// 添加一个czml动态点
const addDynamicPoint = (viewer) => {
  const czml = [
    {
      id: 'document',
      name: 'CZML Point',
      version: '1.0',
      clock: {
        interval: '2012-03-15T10:00:00Z/2012-03-15T10:05:00Z',
        currentTime: '2012-03-15T10:00:00Z',
        // multiplier: 10
      }
    },
    {
      id: 'point',        // 定义一个packet
      position: {
        epoch: '2012-03-15T10:00:00Z',     // 定义初始时间
        cartographicDegrees: [
          // 时间,经度,纬度,高度
          0, 100, 25, 150000,
          100, 105, 30, 150000,
          200, 110, 35, 150000,
          300, 115, 40, 150000
        ],            // 定义插值时间
        interpolationAlgorithm: "LAGRANGE",    // 拉格朗日插值
        interpolationDegree: 5         // 插值多项式次数
      },
      point: {
        color: {
          rgba: [255, 255, 255, 128]
        },
        outlineColor: {
          rgba: [255, 0, 0, 128]
        },
        outlineWidth: 3,
        pixelSize: 15
      }
    }
  ]
  Cesium.CzmlDataSource.load(czml).then(res => {
    console.log(res)
    viewer.dataSources.add(res)
    // viewer.zoomTo(res)
    console.log(Cesium.JulianDate.toDate(viewer.clock.tick()))    // 输出当前时间
  })
}

(2)CZML添加动态自定义属性:

czml可以使用两种方式制作自定义属性,一种是sample插值方式,另一种是interval方式。以下是它们的区别:

// CZML添加动态自定义属性
const addDynamicPoint = (viewer) => {
  const czml = [
    {
      id: 'document',
      name: 'CZML Point',
      version: '1.0',
      clock: {
        interval: '2012-03-15T10:00:00Z/2012-03-15T10:05:00Z',
        currentTime: '2012-03-15T10:00:00Z',
        // multiplier: 10
      }
    },
    {
      id: 'point',        // 定义一个packet
      position: {
        epoch: '2012-03-15T10:00:00Z',     // 定义初始时间
        cartographicDegrees: [
          // 时间,经度,纬度,高度
          0, 100, 25, 150000,
          100, 105, 30, 150000,
          200, 110, 35, 150000,
          300, 115, 40, 150000
        ],            // 定义插值时间
        interpolationAlgorithm: "LAGRANGE",    // 拉格朗日插值
        interpolationDegree: 5         // 插值多项式次数
      },
      point: {
        color: {
          rgba: [255, 255, 255, 128]
        },
        outlineColor: {
          rgba: [255, 0, 0, 128]
        },
        outlineWidth: 3,
        pixelSize: 15
      },
      properties:{
        // sample插值方式
        height: {
          number: [
          '2012-03-15T10:00:00Z', 10,
          '2012-03-15T10:05:00Z', 50
          ]
        },
        // interval分段多项式方式
        testDGB: [
          {
            interval: '2012-03-15T10:00:00Z/2012-03-15T10:02:00Z',
            rgba: [255, 255, 255, 255]
          },
          {
            interval: '2012-03-15T10:02:00Z/2012-03-15T10:05:00Z',
            rgba: [255, 255, 255, 254]
          }
        ]
      }
    }
  ]
  // 修改当前时间
  // viewer.clock.currentTime = Cesium.JulianDate.fromIso8601('2012-03-15T10:00:00Z')
  Cesium.CzmlDataSource.load(czml).then(res => {
    console.log(res)
    viewer.dataSources.add(res)
    // viewer.zoomTo(res)
    console.log(Cesium.JulianDate.toDate(viewer.clock.tick()))    // 输出当前时间
    let properties = res.entities.getById('point').properties
    console.log(properties)
    console.log(properties.height)
    // 循环打印自定义属性
    setInterval(() => { 												  	                                                                                console.log(res.entities.getById('point').properties.height.getValue(viewer.clock.tick()))                                                                console.log(res.entities.getById('point').properties.testDGB.getValue(viewer.clock.tick()))
    }, 1000)
  })
}
Last Updated:
Contributors: zly