三维地球可视化从入门到进阶 - Viewer、Scene、Camera、加载第三方影像、地形服务

5,091 阅读8分钟

本文为稀土掘金技术社区首发签约文章,14 天内禁止转载,14 天后未获授权禁止转载,侵权必究!

哈喽,大家好 我是 xy👨🏻‍💻。这篇文章是 Cesium 三维地球可视化从入门到进阶专栏的第二篇,这篇文章主要对 Cesium 中基础核心类 Viewer、Scene 属性的一些使用讲解以及如何架加载第三方影像、地形服务。

Cesium 中有几个比较核心的类,也是开发过程中比较常用的:

  • Viewer: Cesium 的核心类,对应着 Cesium 展示三维要素内容的主窗口
  • Scene: 是在 Viewer 或 CesiumWidget 内部隐式创建的
  • Camera: Camera主要用来描述和操作场景的视角

视图 Viewer

ViewerCesium 的核心类,对应着 Cesium 展示三维要素内容的主窗口。

它不仅仅是包含三维地球的视窗,还包含一些基础控件,所以在定义 Viewer 对象的同时需要设定基础部件图层等的初始化状态。

Cesium 开发的大部分工作在 Scene 场景中执行,包括调用图层3D Tiles 数据加载、场景交互等。

另外 Cesium 提供了 EntityDataSource 等封装好的数据加载方式,降低了三维开发难度。

Viewer对象主要包括如下属性:

  • camera:相机属性,主要用于控制视角;
  • widgets(非属性):widgets并非Viewer对象的属性,在这里特指所有控件:
    • animation:动画控件;
    • baseLayerPicker:影像图层选择器;
    • fullscreenButton:全屏按钮;
    • geocoder:查找位置;
    • homeButton:返回视角到初始位置;
    • navigationHelpButton:帮助按钮;
    • timeline:时间轴;
    • vrButton:VR 按钮。
  • imageryLayers:影像图层集合;
  • terrainProvider:地形提供者;
  • entities:实体集合;
  • dataSources:矢量数据集合;
  • Event(非属性):Event并非Viewer对象的属性,在这里特指所有事件:
    • screenSpaceEventHandler:屏幕操作事件;
    • selectedEntityChanged:选取实体事件;
    • trackedEntityChanged:追踪实体事件。
  • scene:场景,sceneViewer对象的属性,但它也是 Cesium 中的一个关键的对象,用于添加图形(primitive)、添加场景特效和添加场景事件

在上一篇文章中,我们已经对 Viewer 的一些基本的配置做了详解, 如果还不会配置的小伙伴可以直接点击链接去学习: 三维地球可视化从入门到进阶 - 基础详解

场景 Scene

Scene 是构建场景的类, 是 Cesium 中非常重要的类。Cesium 开发大多基于 Scene 类,其主要包含四部分内容:

  • 基础地理环境设置,如地球参数(globe)、光照(light)、(fog)、大气(skyAtmosphere)
  • 基础图层设置,包含地图图层地形图层
  • 场景数据,Cesium 底层空间数据绘制方式是依赖 Primitive。Primitive API 功能强大而且非常灵活.为程序员绘制高级图形提供很大自由度、开发者可根据图形学原理自定义高级图形。技术难度较大,对于初学者较为困难,相比较面言Entity封装程度高,构造简单,使用便捷,目前不支持自定义。3D Tiles是 Primitive 的非常重要部分,可以实现大数据量加载
  • 场景交互函数,如pick(鼠标事件)、camera(相机事件)

相机 Camera

Cesium 通过相机来控制场景中的视域旋转缩放平移等操作都可控制相机移动,使用相机Camera操作场景分为如下几类:

  • 飞行 fly:flyHomeflyToflyToBoundingSphere,与 fly 有关的方法的特点就是在改变相机视角时会伴随飞行动画;这类方法一定会改变相机的位置,但是不一定会改变相机的朝向;
  • 缩放 zoom:zoomInzoomOut,与 zoom 有关的方法类似于使用鼠标滚轮来操作场景进行缩小或放大;这类方法不会改变相机的朝向,只会改变相机的位置;
  • 移动 move :moveBackwardmoveDownmoveForwardmoveLeftmoveRightmoveUp,与 move 有关的方法就是在前后左右上下这六个方向上移动相机,这类方法不会改变相机的朝向,只会改变相机的位置;
  • 视角 look :lookDownlookLeftlookRightlookUp,与 look 有关的方法就是在相机位置不变的情况下,调整相机镜头的上下左右四个方向朝向,这类方法不会改变相机的位置,只会改变相机的朝向;
  • 扭转 twist :twistLefttwistRight,与 twist 有关的方法就是在相机位置不变的情况下,调整相机视角向左(逆时针)或向右(顺时针)扭转,这类方法不会改变相机的位置,只会改变相机的朝向;
  • 旋转 rotate :rotateDownrotateLeftrotateRightrotateUp,与 rotate 有关的方法会根据提供的角度参数旋转相机视角,这类方法会改变相机的位置,也会改变相机的朝向;
  • 其他操作相机的方法:
    • setView直接将相机视角定位到某个位置;
    • lookAt直接将相机视角定位到某个位置,但是会锁定相机视角。

viewer Scene Camera 常用配置

下面给大家总结一些 viewer Scene Camera 常用的一些配置

  1. 去除 Cesium 版权图标
viewer.cesiumWidget.creditContainer.style.display = "none";
  1. 显示帧数
viewer.scene.debugShowFramesPerSecond = true;
  1. 增加太阳光照
viewer.scene.globe.enableLighting = true;
  1. 大气层显示
viewer.scene.skyAtmosphere.show = true;
  1. 开启地形深度检测
viewer.scene.globe.depthTestAgainstTerrain = true;
  1. 禁止相机进入地下
viewer.scene.globe.depthTestAgainstTerrain = true;
  1. 右键拖拽场景倾斜
viewer.scene.screenSpaceCameraController.tiltEventTypes = Cesium.CameraEventType.RIGHT_DRAG
  1. 关闭抗锯齿
viewer.scene.fxaa = false;
viewer.scene.postProcessStages.fxaa.enabled = false;
  1. 鼠标操作惯性控制
//关闭旋转惯性
viewer.scene.screenSpaceCameraController.inertiaSpin = 0
//关闭平移惯性
viewer.scene.screenSpaceCameraController.inertiaTranslate = 0
//关闭缩放惯性
viewer.scene.screenSpaceCameraController.inertiaZoom = 0
  1. 自动调整分辨率
var supportsImageRenderingPixelated =
    viewer.cesiumWidget._supportsImageRenderingPixelated;
  if (supportsImageRenderingPixelated) {
    var vtxf_dpr = window.devicePixelRatio;
    while (vtxf_dpr >= 2.0) {
      vtxf_dpr /= 2.0;
    }
    viewer.resolutionScale = vtxf_dpr;
  }
  1. 默认定位到中国
Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees(
  75.0, // 东
  0.0, // 南
  140.0, // 西
  60.0 // 北
);
  1. 修改天空背景盒样式
var scene=viewer.scene;
scene.skyBox = new Cesium.SkyBox({
    sources : {
        positiveX: "/skyBox/00h+00.jpg",
        negativeX: "skyBox/12h+00.jpg",
        positiveY: "/skyBox/06h+00.jpg",
        negativeY: "/skyBox/18h+00.jpg",
        positiveZ: "/skyBox/06h+90.jpg",
        negativeZ: "/skyBox/06h-90.jpg",
    }
});

加载第三方影像

Cesium 加载的虚拟地球默认是有影像的,如图:

默认使用的影像为“Bing Maps Aerial”:

除了图中列出的一些影像,开发者可以通过 Cesium 中的ImageryProvider影像提供者类创建所需的影像,截止 2022 年 4 月 12 日,在 Cesium 1.92 版本中共有 14 种ImageryProvider影像提供者类(不包括抽象类ImageryProvider):

  • ArcGisMapServerImageryProvider:由 ArcGIS Map Server 提供的影像服务;
  • BingMapsImageryProvider:由 Bing Map 提供的影像服务;
  • GoogleEarthEnterpriseImageryProvider:由 Google Earth Enterprise 提供的影像服务;
  • GridImageryProvider:网格影像服务;
  • IonImageryProvider:由 Cesium Ion 提供的影像服务,Cesium Ion 为 Cesium 官方提供的云服务。
  • MapboxImageryProvider:由 Mapbox 提供的影像服务;
  • MapboxStyleImageryProvider:由 Mapbox 提供的可以修改样式 style 的影像服务;
  • SingleTileImageryProvider:由单个图像提供的影像服务;
  • TileCoordinatesImageryProvider:切片 tile 坐标影像服务,可以在每一个切片周围绘制一个框并标识其 X,Y 坐标和 Level 层级,主要用于调试;
  • TileMapServiceImageryProvider:由 MapTiler 提供的影像服务;
  • UrlTemplateImageryProvider:通过指定的 URL 加载影像服务;
  • WebMapServiceImageryProvider:由 Web Map Service(WMS)提供的影像服务;
  • WebMapTileServiceImageryProvider:由 Web Map Tile Service(WMTS)提供的影像服务。

加载影像图层的方法共有两种:

  1. 在初始化 viewer 的时候添加;

在初始化Viewer时可在第二个参数Viewer.ConstructorOptions 中添加imageryProvider属性并提供一个ImageryProvider对象:

const viewer = new Cesium.Viewer("cesiumContainer", {
  imageryProvider: new Cesium.ArcGisMapServerImageryProvider({
    url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
  })
})
  1. 通过 viewer.imageryLayers.addImageryProvider 方法添加。

使用viewer.imageryLayers.addImageryProvider方法,顾名思义需要传入一个ImageryProvider对象:

const ArcGisImagery = viewer.imageryLayers.addImageryProvider(new Cesium.ArcGisMapServerImageryProvider({
  url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
}))

ArcGisMapServerImageryProvider

加载 Arcgis 影像服务的代码如下:

const viewer = new Cesium.Viewer("cesiumContainer", {
  imageryProvider: new Cesium.ArcGisMapServerImageryProvider({
    url : 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
  })
})

加载后的效果如下:

BingMapsImageryProvider

加载 Bing 影像服务的代码如下,其中key属性需要在Bing maps Dev Center中申请,按照页面提示申请即可:

viewer.imageryProvider = new Cesium.BingMapsImageryProvider({
  url: 'https://dev.virtualearth.net',
  key: 'xxx',
  mapStyle : Cesium.BingMapsStyle.AERIAL
})

加载后的效果如下:

GridImageryProvider

加载 Grid 网格影像服务的代码如下:

const viewer = new Cesium.Viewer("cesiumContainer", {
  imageryProvider: new Cesium.GridImageryProvider()
})

加载后的效果如下:

SingleTileImageryProvider

加载本地图片如下:

加载的代码如下:

viewer.imageryLayers.addImageryProvider(new Cesium.SingleTileImageryProvider({
  url: './data/worldimage.jpg'
}))

加载后的效果如下:

TileCoordinatesImageryProvider

加载 TileCoordinates 影像服务的代码如下:

const viewer = new Cesium.Viewer("cesiumContainer", {
  imageryProvider: new Cesium.TileCoordinatesImageryProvider()
})

UrlTemplateImageryProvider

Google 影像服务

众所周知的原因,谷歌地图影像在中国大陆无法使用

加载谷歌地图影像的代码如下:

const viewer = new Cesium.Viewer("cesiumContainer", {
  imageryProvider: new Cesium.UrlTemplateImageryProvider({
    url: "http://mt1.google.cn/vt/lyrs=s&hl=zh-CN&x={x}&y={y}&z={z}&s=Gali"
  })
})

加载后的效果如下:

高德影像服务

加载高德地图影像的代码如下:

const viewer = new Cesium.Viewer("cesiumContainer", {
  imageryProvider: new Cesium.UrlTemplateImageryProvider({
    url: "https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}"
  })
})

加载后的效果如下:

WebMapTileServiceImageryProvider

天地图影像(推荐)

在使用天地图之前请注册一个天地图账号以及一个天地图 Token,创建完成后可以在 Cesium 中加载天地图服务:

const tdtToken = '' // 填入你创建的天地图Token
const viewer = new Cesium.Viewer("cesiumContainer", {
  imageryProvider: 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=${tdtToken}`,
    layer: "tdtBasicLayer",
    style: "default",
    format: "image/jpeg",
    tileMatrixSetID: "GoogleMapsCompatible",
  })
})

加载后的效果如下:

加载地形服务

Cesium 默认是不包含地形的,准确的说应该是 Cesium 默认加载的地形是没有起伏效果的,,如下图所示,在包含山脉的地区是看不到地形起伏的:

默认使用的地形为”WGS84 Ellipsoid“:

地形提供者 TerrainProvider

与影像类似,在 Cesium 中提供了一些地形提供者TerrainProvider用于加载各类地形,截止 2022 年 4 月 12 日,在 Cesium 1.92 版本中共有 6 种TerrainProvider地形提供者(不包括接口TerrainProvider):

  • ArcGISTiledElevationTerrainProvider:由 ArcGIS 提供的地形服务;
  • CesiumTerrainProvider:由 Cesium 官方提供的地形服务;
  • CustomHeightmapTerrainProvider:自定义高程的地形服务;
  • EllipsoidTerrainProvider:高程为 0 的地形服务,如果初始化时不指定地形,那么默认的就是使用的该地形服务;
  • GoogleEarthEnterpriseTerrainProvider:由 GoogleEarth 提供的地形服务;
  • VRTheWorldTerrainProvider:由 VRTheWorld 提供的地形服务。

与加载影像图层十分类似,加载地形的方法也有两种:

  1. 在初始化viewer的时候添加;

注意,一个视图下地形只能加载一个,而影像图层是可以加载多个的,同一视图下可以叠加多个影像。另外,从语义上来说,每一个影像图层都是一个imageryLayer,所有的imageryLayer通过ImageryLayerCollection来管理,添加一个影像图层用的方法是addImageryProvider,注意动词“add”,而对于地形来说,是直接给viewer.terrainProvider赋值的。

在初始化Viewer时可在第二个参数Viewer.ConstructorOptions中添加terrainProvider属性并提供一个TerrainProvider对象:

const viewer = new Cesium.Viewer("cesiumContainer", {
  terrainProvider: new Cesium.ArcGISTiledElevationTerrainProvider({
    url: 'https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer',
  })
})
  1. 通过viewer.terrainProvider赋值添加。

通过viewer.terrainProvider赋值添加的代码如下:

const viewer = new Cesium.Viewer("cesiumContainer")
const ArcGisTerrainProvider = new Cesium.ArcGISTiledElevationTerrainProvider({
  url: 'https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer',
})
viewer.terrainProvider = ArcGisTerrainProvider

ArcGISTiledElevationTerrainProvider

加载 Arcgis 地形服务的代码如下:

const viewer = new Cesium.Viewer("cesiumContainer")
const terrainProvider = new Cesium.ArcGISTiledElevationTerrainProvider({
  url: 'https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer',
})
viewer.terrainProvider = terrainProvider

加载后的效果如下:

CesiumTerrainProvider

CesiumTerrainProvider用于加载自行发布的地形服务或其他地形服务商发布的地形服务。

自行发布的地形服务

加载自行发布的地形服务代码如下,其中https://localhost/terrain为地形服务的URL

const viewer = new Cesium.Viewer("cesiumContainer")
viewer.terrainProvider = new Cesium.CesiumTerrainProvider({
  url: "https://localhost/terrain"
})

火星科技地形

参考Mars3D 地形,版权归火星科技所有,在此仅作为学习交流所用,侵删。

加载火星科技地形的代码如下:

const viewer = new Cesium.Viewer("cesiumContainer")
viewer.terrainProvider = new Cesium.CesiumTerrainProvider({
  url: 'http://data.marsgis.cn/terrain',
})

加载后的效果如下:

maptiler 地形

加载 maptiler 地形的伪代码如下,需要访问maptiler 官网注册账号获取key

const viewer = new Cesium.Viewer("cesiumContainer")
const maptilerKey = 'xxx'
viewer.terrainProvider = new Cesium.CesiumTerrainProvider({
  url: `https://api.maptiler.com/tiles/terrain-quantized-mesh/?key=${maptilerKey}`,
  requestVertexNormals: true
})

加载后的效果如下:

CustomHeightmapTerrainProvider

加载自定义高程地形的代码如下,通过回调函数callback获取高程,这个TerrainProvider用的很少:

const viewer = new Cesium.Viewer("cesiumContainer")
let width = 64
let height = 64
viewer.terrainProvider = new Cesium.CustomHeightmapTerrainProvider({
  callback: (x, y, level) => {
    let buffer = new Float32Array(width * height)
    for (let yy = 0; yy < height; yy++) {
      for (let xx = 0; xx < width; xx++) {
        let v = (y + yy / (height - 1)) / Math.pow(2, level)
        let heightValue = 8000 * (Math.sin(4000 * v) * 0.5 + 0.5)
        let index = yy * width + xx
        buffer[index] = heightValue
      }
    }
    return buffer
  },
  width: width,
  height: height,
})

EllipsoidTerrainProvider

EllipsoidTerrainProvider是高程为 0 的地形,若初始化时不指定地形,那么默认的就是使用的该地形服务,加载EllipsoidTerrainProvider的代码如下:

const viewer = new Cesium.Viewer("cesiumContainer")
viewer.terrainProvider = new Cesium.EllipsoidTerrainProvider()

🎯 这篇文章是 Cesium 三维地球可视化从入门到进阶 专栏的第二篇文章,主要是对 Cesium 中基础核心类 Viewer、Scene 属性的一些使用讲解以及如何架加载第三方影像、地形服务。

🎯 在后续的文章中, 将会分享更多实践案例,如果你也对 三维可视化比较感兴趣的话,欢迎关注我一起学习

🎯 Github 仓库地址:https://github.com/xushanpei/Cesium_Study_Cases

写在最后

公众号前端开发爱好者 专注分享 web 前端相关技术文章视频教程资源、热点资讯等,如果喜欢我的分享,给 🐟🐟 点一个 👍 或者 ➕关注 都是对我最大的支持。

大家好,我 xy,是一名前端 🤫 爱好:瞎折腾

如果你也是一名瞎折腾的前端欢迎加我微信交流哦...

🤫 一定要点我