# cesium 介绍
- 使用 3d tiles 格式流式加载各种不同的 3d 数据,包含倾斜摄影模型、三维建筑物、CAD 和 BIM 的外部和内部,点云数据。并支持样式配置和用户交互操作。
- 全球高精度地形数据可视化,支持地形夸张效果、以及可编程实现的等高线和坡度分析效果。
- 支持多种资源的图像图层,包括 WMS,TMS,WMTS 以及时序图像。图像支持透明度叠加、亮度、对比度、GAMMA、色调、饱和度都可以动态调整。支持图像的卷帘对比。
- 支持标准的矢量格式 KML、GeoJSON、TopoJSON, 以及矢量的贴地效果。
- 三维模型支持 gltf2.0 标准的 PBR 材质、动画、蒙皮和 变形效果;贴地以及高亮效果。
- 使用 CZML 支持动态时序数据的展示。
- 支持各种几何体:点、线、面、标注、公告牌、立方体、球体、椭球体、圆柱体、走廊(corridors)、管径、墙体。
- 可视化效果包括:基于太阳位置的阴影、自身阴影、柔和阴影。
- 大气、雾、太阳、阳光、月亮、星星、水面。
- 粒子特效:烟、火、火花。
- 地形、模型、3d tiles 模型的面裁剪。
- 对象点选和地形点选。
- 支持鼠标和触摸操作的缩放、渲染、惯性平移、飞行、任意视角、地形碰撞检测。
- 支持 3d 地球、2d 地图、2.5d 哥伦布模式。3d 视图可以使用透视和正视两种投影方式。
- 支持点、标注、公告牌的聚集效果。
# 学习路线
- Viewer 类学习 -- 一切API的入口
- Camera 类学习 -- 想去哪里去哪里( Cartesian3和Cartographic)
- ImageryLayer 类学习 -- 地球原来如此多姿( ImageryProvider 类)
- TerrainProvider 类学习 – 这才是“真”三维( sampleTerrain)
- EntityAPI – 与地球交互起来( DataSource, Scene.pick、 Property)
- Cesium3DTileset 类 -- 让场景更细致真实点( Cesium3DTileStyle 类)
- PrimitiveAPI – 性能提升第一步( GeometryInstance 类、 Geometry 类)
- Fabric – 玩点高级的(Appearance 类, Material 类)
- ParticleSystem -- 锦上添点“花”( Particle 类, ParticleEmitter 类)
# cesium 功能点
对于 Cesium 的学习,可以看官网示例,以及修改运行了解 API 功能,然后实际进行开发查阅 API 文档;Cesium 包含以下功能点
- Viewer
- 小控件
- 场景 Scene
- 影像图层 ImageryLayer
- 地形图层 TerrainProvider
- 坐标系以及坐标变换
- 相机 Camera
- 交互性(鼠标动作处理器、事件)
- 后期处理 PostProcessStage
- Entity 矢量数据
- Primitive 矢量数据
- Property 机制
- 材质(Material、Fabric)
- glTF 小模型
- 3D Tiles 三维模型 Cesium3DTileset
- CZML 数据格式
- 粒子系统 ParticleSystem
# viewer
new Viewer 构造的就是一个地球对象,常见的属性如下
const viewer = new Cesium.Viewer('cesiumContainer', {
/* 1. 参数 */
// 初始化场景模式
sceneMode : Cesium.SceneMode.COLUMBUS_VIEW,
// 3D 场景模式
scene3DOnly: false,
// 是否显示渲染出错窗
showRenderLoopErrors: true,
// 地形夸张系数
terrainExaggeration: 1,
// 全屏时渲染的 div 元素 id,或者元素
fullscreenElement: document.body,
/* 2. 控件显示隐藏 */
// 视角复位按钮
homeButton: true,
// 二三维切换按钮
sceneModePicker: true,
// 帮助按钮
navigationHelpButton: true,
// 左下角动画部件按钮
animation: true,
// 下侧时间轴
timeline: true,
// 全屏按钮
fullscreenButton: true,
// 自动播放
shouldAnimate: false,
// vr 模式按钮
vrButton: false,
// 查询按钮
geocoder: true,
// 信息框
infoBox: true,
// 点击选中提示框
selectionIndicator: true,
/* 3. 图层 */
// 地形地图
terrainProvider : Cesium.createWorldTerrain(),
// 底图图层控件显隐
baseLayerPicker : false,
// 底图设置
imageryProvider : new Cesium.OpenStreetMapImageryProvider({
url : 'https://a.tile.openstreetmap.org/'
}),
// 可选底图组
imageryProviderViewModel: getImageryProviderViewModel(),
// 可选地形组
terrainProviderViewModel: getTerrainProviderViewModel()
})
# 控件
Cesium 内置了一些小控件,可以在 new Cesium.Viewer 构造函数中传入参数进行控制。
const viewer = new Cesium.Viewer('cesiumContainer', {
// 视角复位按钮
homeButton: true,
// 二三维切换按钮
sceneModePicker: true,
// 帮助按钮
navigationHelpButton: true,
// 左下角动画部件按钮
animation: true,
// 下侧时间轴
timeline: true,
// 全屏按钮
fullscreenButton: true,
// 自动播放
shouldAnimate: false,
// vr 模式按钮
vrButton: false,
// 查询按钮
geocoder: true,
// 信息框
infoBox: true,
// 点击选中提示框
selectionIndicator: true,
// 底图图层控件显隐
baseLayerPicker : false,
})
# 影像图层 ImageryLayer
Cesium 可以加载多种服务来源的高精度影像数据,图层支持排序和透明混合,每个图层的亮度(brightness),对比度(contrast),等,都可以动态修改。
const viewer = new Cesium.Viewer('cesiumContainer', {
// 底图图层控件显隐
baseLayerPicker : false,
// 地形地图
terrainProvider : Cesium.createWorldTerrain(),
// 底图设置
imageryProvider : new Cesium.OpenStreetMapImageryProvider({
url : 'https://a.tile.openstreetmap.org/'
}),
// 可选底图组
imageryProviderViewModel: getImageryProviderViewModel(),
// 可选地形组
terrainProviderViewModel: getTerrainProviderViewModel()
})
在地球构造后可以通过 viewer.scene.imageryLayers (ImageryLayerCollection 类) 来控制图层.
参考资料:https://www.jianshu.com/p/98d4c0b2c618
# 地形图层 TerrainProvider
对于地形图层只需要修改 viewer.terrainProvider 属性:
// 1. 使用 cesium 在线 Ion 地形
viewer.terrainProvider = Cesium.createWorldTerrain()
// 2. 使用自行发布的服务
viewer.terrainProvider = new Cesium.CesiumTerrainProvider({
// 加载 cesium 服务地形
// url : Cesium.IonResource.fromAssetId(3956),
url: 'http://localhost/terrain'
requestVertexNormals : true
})
// 3. 不使用地形
viewer.terrainProvider = new Cesium.EllipsoidTerrainProvider()
# cesium 中常用坐标系以及坐标之间的转换
- 屏幕坐标(像素值)
- 笛卡尔平面坐标:new Cesium.Cartesian2(x, y)
- 笛卡尔空间直角坐标:new Cesium.Cartesian3(x, y, z)
- 地理坐标(默认为弧度制):new Cesium.Cartographic(longitude, latitude, height)
# 获取鼠标点击后再屏幕中的坐标
const hander = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
// 绑定鼠标左点击事件
hander.setInputAction(event => {
// 获取鼠标点的屏幕坐标
const windowPosition = event.position
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
# 屏幕坐标转换为笛卡尔空间直角坐标
// 三维模式下
const ray = viewer.camera.getPickRay(windowPosition)
const cartesian = viewer.scene.globe.pick(ray, viewer.scene)
// 二维模式下
const cartesian = viewer.camera.pickEllipsoid(position, scene.globe.ellipsoid)
# 笛卡尔空间直角坐标转换为屏幕坐标
const pick = Cesium.SceneTransforms.wgs84ToWindowCoordinates(viewer.scene, cartesian)
# 笛卡尔空间直角坐标转换为地理坐标(弧度制)
const cartographic = Cesium.Cartographic.fromCartesian(cartesian)
# 地理坐标(弧度制)转换为笛卡尔空间直角坐标
const position = Cesium.Cartesian3.fromRadians(lng, lat, height)
# 笛卡尔空间直角坐标转换为地理坐标(经纬度)
const cartographic = Cesium.Cartographic.fromCartesian(cartesian)
const lat = Cesium.Math.toDegrees(cartographic.latidude)
const lng = Cesium.Math.toDegrees(cartographic.longitude)
const height = cartographic.height
# 度数与弧度互转
Cesium.Math.toDegrees(radians)
Cesium.Math.toRadians(degrees)
# 地理坐标经纬度转换为笛卡尔空间直角坐标
const position = Cesium.Cartesian3.fromDegrees(longitude, latitude, height)
# 相机 Camera
相机控制了场景的观察视角。有很多相机操控方法,比如旋转、缩放、平移以及飞行定位。其内部默认已支持使用鼠标和触摸事件控制相机。
Camera 类(相机)描述了相机的当前状态,包括:位置(position),朝向(orientation), 参考空间(reference frame), 视锥体(view frustum),可以调用move*、zoom*、look* 、twist* 、rotate* 、setView、flyTo 等方法进行控制相机操作
ScreenSpaceCameraController 类(屏幕控件相机控制器)把屏幕空间的用户输入(鼠标拖拽点击或者触摸事件)转换为三维世界的相机移动。它包含一些属性,可以启用/禁用某种用户输入,修改惯性、最小最大缩放距离等。
相机相关事件:
- viewer.camera.moveStart() 相机开始移动时将引发的事件
- viewer.camera.moveEnd() 相机停止时将引发的事件
- viewer.camera.changed() 相机更改后将引发的事件
可以通过 addEventListener 和 removeEventListener 进行绑定和解绑
// 相机位置
const camera = viewer.camera
camera.flyTo({
destination: camera.position,
orientation: {
heading: 0,
pitch: camera.pitch,
roll: camera.roll
}
})
// 另一种飞行
viewer.zoomTo(
tileset,
new Cesium.HeadingPitchRange(
0.0,
-0.5,
tileset.boundingSphere.radius * 2.0
)
);
# 相机操作的基本方法
- move* 和 zoom* 方法:沿着相机方向或者某个给定向量来平移相机的位置。相机朝向不变。
- look* 和 twist* 方法:旋转相机朝向,向前向量(direction),向下向量(up),向右向量(right)都会改变。相机位置保持不变。
- rotate* 方法:相对一个给定的向量,旋转相机的位置和朝向。
https://www.jianshu.com/p/1a31aa57a84b
# Cesium 的交互事件(鼠标动作处理器、事件)
ScreenSpaceEventHandler 类处理用户输入事件,可以添加自定义函数,以便在用户输入时对其执行:
- setInputAction(action, type, modifier) 设置事件
- getInputAction(type, modifier) 得到交互事件
- removeInputAction(type, modifier) 移除事件
- destroy() 销毁 handle
- isDestroy() 判断是否销毁
ScreenSpaceEventType 类为事件类型,包含鼠标单击、双击、按下、抬起、滚轮、右击等:
const hander = new Cesium.ScreenSpaceEventHandle(viewer.scene.canvas)
// 绑定事件
hander.setInputAction(event => {
}, Cesium.ScreenSpaceEvent.Type.LEFT_CLICK)
# 后期处理 PostProcessStage
Cesium 支持对整个场景的后期处理(Post Processing)功能,包括模型描边、黑白图、明亮 度调整、夜视效果、环境光遮蔽等。 后期处理的过程有点类似于照片的 PS。
- PostProcessStage:对应于某个具体的后期处理效果,它的输入为场景渲染图或者上一个后期处理的结果图,输 出结果是一张处理后的图片。
- PostProcessStageComposite: 一个集合对象,存储类型为 PostProcessSrage 或者 PostProcessStageComposite 的元素
- PostProcessStageLibrary:负责创建具体的后期处理效果,包括 Silhouette、Bloom、AmbientOcclusion 等, 创建返回的结果是 PostProcessStageComposite 或者 PostProcessStage 类型。
- PostProcessStageCollection:是一个集合类型的类,负责管理和维护放到集合中的元素 ,元素的类型是 PostProcessStage 或者 PostProcessStageComposite。
https://www.cnblogs.com/webgl-angela/p/9272810.html
# Primitive 与 Entity
Cesium 加载点线面矢量数据分为两部分:
- Primitive API 面向三维图形开发者,更底层一些,灵活、性能高、使用复杂。
- Entity API 是数据驱动更高级一些,性能略低,接口一致、容易使用。
Primitive API 的主要目的是为了完成(可视化)任务的最少的抽象需求。它很强大又很灵活,要求我们以一个图形开发者的方式去思考,并且使用了一些图形学术语。它是为了最高效最灵活的实现可视化效果,忽略了 API 的一致性。他们每种都有自己的独特的性能提升方式,也需要遵守不同的优化原则。
Entity API 的主要目的是定义一组高级对象,它们把可视化和信息存储到统一的数据结果中,这个对象叫 Entity。 它让我们更加关注我们的数据展示而不是底层的可视化机制。它提供了很方便的创建复杂的与静态数据相匹配的随时间变化的可视化效果。Entity 内部也是使用了 Primitive,它的实现细节,我们无需关心, Entity 暴露一些一致性的、容易去学习和使用的接口。
# Entity 矢量数据
通常使用 viewer.entities.add 方法进行添加 Entity 矢量数据,或者使用 CustomDataSource 对象进行管理,支持的类型有:
# Primitive 矢量数据
Primitive 方式更接近渲染引擎底层,Primitive由两个部分组成:
- 几何形状(Geometry):定义了 Primitive 的结构,例如三角形、线、面等。
- 外观(Appearance):定义 Primitive 的着色(Sharding),包括 GLSL(OpenGL 着色语言)顶点着色器和片段着色器(vertex and fragment shaders),以及渲染状态(render state)
使用 Geometry 和 Appearance 具有以下优势:
- 性能:绘制大量 Primitive 时,可以将其合并为单个 Geometry 以减轻 CPU 负担,更好的使用 GPU,合并 Primitive 由 Web worker 线程执行,UI 保持响应性。
- 灵活性:Geometry 与 Appearance 解耦,两者可以分别进行修改
- 低级别访问:易于编写 GLSL 顶点、片段着色器、使用自定义的渲染状态同时,具有以下劣势:
- 需要编写更多的代码
- 需要对图形编程有更多的理解,特别是 OpenGL 的知识
https://www.cnblogs.com/fuckgiser/p/5706842.html https://www.jianshu.com/p/5a74c607a591 http://cesium.marsgis.cn/go.html?id=12 https://www.jianshu.com/p/fc0252e63204
# Property 机制
Cesium 宣称自己是数据驱动和 time-dynamic visualization,这些可都是仰仗 Property 系统来实现的。 Property 最大的特点是和时间相互关联,在不同的时间可以动态地返回不同的属性值。而 Entity则可以感知这些 Property 的变化,在不同的时间驱动物体进行动态展示。
详细的讲解: https://www.jianshu.com/p/f0b47997224c
# 材质(Material、Fabric)
Fabric 是 Cesium 中基于 JSON 格式来描述 Material 的机制。材质描述多边形、折线、椭球等对象的外观特征。材质可以简单的是覆盖一张图片,或者是条纹或者棋盘图案。使用 Fabric 和 GLSL,可以从零开始写脚本新建材质,也可以从现有的材质中派生。比如潮湿碎裂的砖块可以使用程序生成的纹理、凹凸贴图和反射贴图来组合。对象通过 material 属性来支持材质效果。
资料:https://www.cnblogs.com/fuckgiser/p/6171245.html https://www.jianshu.com/p/f8fee864379a
# glTF 小模型
glTF 小模型只是 Primitive 或 Entity 的其中一种类型数据,代码加载方式是一致的;这里主要说明.gltf 数据特性。规范:https://github.com/KhronosGroup/glTF
Cesium 支持包含关键帧(key-frame)动画、骨骼(skinning)动画的 glTF 格式的三维模型,并且支持模型节点(node)的拾取。 glTF 是 Khronos Group 定义的一个基于 web 上的新兴三维模型格式行业标准。Khronos Group 是 WebGL 和 COLLADA 的背后财团。
格式转换:https://cesium.com/blog/2019/09/17/ion-gltf/ 其他资源:官方示例中 3D models,官方文档 Model 类和 ModelAnimationCollection 类
模型加载以及介绍:https://www.jianshu.com/p/47cd185b58c7
# 3D tiles 三维模型 Cesium3DTileset
Cesium 与开源社区合作开发了 3D Tiles,这是一个开放的规范,用于传输海量的异构三维地理空间数据集。使用概念上类似于 terrain 和 imagery 的瓦片流技术,3D Tiles 使得建筑物数据集、BIM 模型、点云和摄影测量模型等大模型比较流畅的在 Web 端进行浏览展示。3D Tiles 还允许我们使用 3D Tiles styling 语言 来调整我们的样式。3D Tiles 样式定义了用于评估颜色(RGB 和透明度)的表达式,并显示了 Cesium3DTileFeature 特征的属性,这是 tileset 的一部分。样式是用 JSON 定义的,而表达式是在 JavaScript 的小子集中编写的,用于样式化。此外,样式语言提供了一组内置函数来支持常见的数学运算。
数据规范:https://github.com/AnalyticalGraphicsInc/3d-tiles 更多示例:the 3D Tiles sandcastle demos:https://sandcastle.cesium.com/
const tilest = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({
url: 'http://localhost/3dTiles/tileset.json',
maximumScreenSpaceError: 1
}))
tilest.readyPromise.then(tileset => {
const boundingSphere = tileset.boundingSphere
const hpr = new Cesium.HeadingPitchRange(0.0, -0.5, boundingSphere.radius * 2)
// 视角定位到模型
viewer.camera.flyToBoundingSphere(boundingSphere, hpr)
// 设置样式
tilest.style = new Cesium.Cesium3DTileStyle({
color: {
conditions: [
["${Height} >= 300", "rgba(45, 0, 75, 0.5)"],
["${Height} >= 200", "rgb(102, 71, 151)"],
["${Height} >= 100", "rgb(170, 162, 204)"],
["${Height} >= 50", "rgb(224, 226, 238)"],
["${Height} >= 25", "rgb(252, 230, 200)"],
["${Height} >= 10", "rgb(248, 176, 87)"],
["${Height} >= 5", "rgb(198, 106, 11)"],
["true", "rgb(127, 59, 8)"],
],
}
})
}).otherwise(error => {
throw(error)
})
# 鼠标点选 & 属性输出
const scene = viewer.scene
viewer.screenSpaceEventHandler.setInputAction(movement => {
const fature = scene.pick(movement.position)
if (feature instanceof Cesium.Cesium3DTileFeature) {
const propertyNames = feature.getPropertyNames()
const length = propertyNames.length
for (let i = 0; i < length; i ++) {
const propertyName = propertyNames[i]
console.log(propertyName + ':' + feature.getProperty(propertyName))
}
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
# 鼠标交互 & 自定义气泡
const info = document.getElementById('info')
function showInfo(entity) {
info.innerHTML = entity.name + '<br>' + entity.description
info.style.display = 'black'
}
function hideInfo() {
info.style.display = 'none'
}
const scene = viewer.scene
let pickPosition
viewer.sceenSpaceEventHandler.setInputAction(function onLeftClick(movement) {
const pick = scene.pick(movement.position)
if (pick) {
if (pick.id === model) {
pickPosition = scene.pickPosition(movement.position)
showInfo(model)
} else {
hideInfo()
}
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
const removechanged = scene.postRender.addEventListener(function (percentage) {
// 转换为屏幕坐标
if (pickPosition && info.style.display === 'block') {
const winpos = scene.cartesianToCanvasCoordinates(pickPosition)
if (winpos) {
info.style.left = (winpos.x -100 / 2).toFixed(0) + 'px'
info.style.top = (winpos.y -100 / 2).toFixed(0) + 'px'
}
}
})
# CZML 数据格式
CZML 是一种用来描述动态场景的 JSON 架构的语言,主要用于 Cesium 数据与程序分离, 就如同 Google Earth 和 KML 的关系。采用数据驱动的方式,不用写代码即可构建出丰富的动态场景。 一个 CZML 文档包含一个 JSON 数组,数组中每一个对象都是一个 CZM L数据包(packet),一个 packet 对应一个场景中的对象,例如一个飞机。Cesium.CzmlDataSource.load 可以加载 czml 对象或者czml 文档的 url。CZML 比较特殊的是跟时间序列相关的属性。
{
"asset": {
"version": "1.0"
},
"properties": {
"id": {
"minimum": 0,
"maximum": 9
},
"Longitude": {
"minimum": -1.3196959060375169,
"maximum": -1.3196607462778132
},
"Latitude": {
"minimum": 0.6988590050687061,
"maximum": 0.6988864387845588
},
"Height": {
"minimum": 6.1022464875131845,
"maximum": 13.410263679921627
}
},
"geometricError": 70,
"root": {
"refine": "ADD",
"boundingVolume": {
"region": [
-1.3197004795898053,
0.6988582109,
-1.3196595204101946,
0.6988897891,
0,
20
]
},
"geometricError": 0,
"content": {
"uri": "batchedColors.b3dm"
}
}
}
# 粒子系统 ParticleSystem
粒子系统是一种图形技术,可以模拟复杂的物理效果。粒子系统是小图像的集合,当它们一起观看时,会形成一个更复杂的“模糊”物体,如火、烟、天气或烟花。通过使用诸如初始位置、速度和寿命等属性指定单个粒子的行为,可以控制这些复杂的效果。粒子发射器( ParticleEmitter)控制了粒子产生时候的位置以及初始速度方向,并依据 emissionRate 来决定每秒产生多少粒子,根据发射器类型不同决定了粒子的随机速度方向。
- CircleEmitter:圆形发射器使用 CircleEmitter 类在圆形面上随机一个位置,粒子方向是发射器的向上向量。它接受一个float参数指定了圆的半径。
- BoxEmitter:在盒子里(box)里随机一个位置,沿着盒子的 6 个面的法向量向外运动。它接受一个 Cartesian3 参数,定义了盒子的长宽高。
- ConeEmitter:锥形发射器类使用 ConeEmitter 在椎体顶点产生粒子,粒子方向在椎体内随机一个角度向外。它接受一个 float 参数,制定了锥角。
- SphereEmitter:球形发射器使用 SphereEmitter 类在球体内随机产生粒子,初始速度是沿着球心向外。它接受一个 float 参数指定了球体半径。
https://www.jianshu.com/p/cb18e2c8ba72, https://www.jianshu.com/p/339c3685ba00
# CesiumWidget - Scense 结构
Cesium 通过 CesiumWidget 类管理窗口 div;通过 Scene 来管理所有的三维场景对象;三维场景对象的构建则是通过图元来创建;
CesiumWidget 内部创建的对象主要有下面几个部分:
- clock 表示时间,通过时间可以确定某一帧绘制的内容。
- canvas 就是绘制的三维窗口中的对象。
- screenSpaceEventHandle 是对 canvas 对象上各种鼠标的交互事件的封装,方便传递给三维场景;
- scene 三维窗口所有的内容:地球(global)、skybox(天空盒)、sun(太阳)、moom(月亮)等等。还有链各个用来由用户自行控制存放对象的数组 primitives 和 groundPrimitives(贴地的图元)。
- Billboards 图片(始终面向屏幕)
- ViewportQuad:类似 logo 一直存在视图
- Primitive 图元的一种,可以自定义几何体包括:Geometry 以及 Appearence(外观,怎么显示;比如材质等等)
Scene 场景渲染监听事件触发顺序:
- viewer.scene.preUpdate: 在更新或呈现场景之前将引发的事件
- viewer.scene.postUpdate: 在场景更新后以及渲染场景之前立即引发的事件
- viewer.scene.preRender: 在场景更新后以及渲染场景之前将引发的事件
- viewer.scene.postRender: 在渲染场景后立即引发的事件
addEventListener 和 removeEventListener 进行绑定事件和解绑
# Model 图元使用
# 增加一个图元
// 根据 heading、pitch、roll、height 来控制·
const modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame()
// model 返回的是一个 promise
model = scene.primitives.add(Cesium.Model.fromGltf({
url : url, // 模型的 url
modelMatrix : modelMatrix, // 控制模型的位置
minimumPixelSize : 128 // 模型最小显示的像素
}));
# camera.lookAt()
会直接定位到一定的位置。以及会锁定窗口,会使得视口围绕那个点进行旋转。可以使用 lookAtTransform 去解除锁定。
camera.lookAt(center, mew Cesium.HeadingPitchRange(heading, pitch, r * 2.0))
// 解除锁定
camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
# Cesium.ScreenSpaceEventHandler 处理鼠标事件
鼠标操作的一些事件。
var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
handler.setInputAction(function (movement) {
// 在场景中拾取一个要素
var feature = scene.pick(movement.endPosition);
if (Cesium.defined(feature) && Cesium.defined(feature.node) && Cesium.defined(feature.mesh)) {
cosole.log(feature, feature.node, feature.mesh)
}
if (feature instanceof Cesium.Cesium3DTileFeature) {
selectFeature(feature);
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
# Cesium.ModelInstanceCollection 批量渲染 model
官网 API 没有,源码中可以查到
var collection = scene.primitives.add(new Cesium.ModelInstanceCollection({
url : url,
instances : instances
}));
# Model 子节点控制
可以对一个 Model 的子节点进控制
const node = model.getNode(viewModel.nodeName)
node.matrix = Cesium.Matrix4.multiply()
# Billboard&Cesium.BillboardCollection
创建一个面朝屏幕的图片; 特点
- 始终面朝屏幕,即使旋转也面朝屏幕
- 注意创建的集群对象 Cesium.BillboardCollection
// 第一种方式
const billboards = scene.primitives.add(new Cesium.billboardCollection())
billboards.add({
image: "../images/Cesium_Logo_overlay.png",
position: Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883)
})
// 第二种方式
viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883),
billboard: {
image: logoUrl,
},
});
viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883),
billboard: {
image: images[0],
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(0.0, -facilityHeight), // 相对于屏幕做偏移
pixelOffsetScaleByDistance: new Cesium.NearFarScalar(
1.0e3,
1.0,
1.5e6,
0.0
),
translucencyByDistance: new Cesium.NearFarScalar(
1.0e3,
1.0,
1.5e6,
0.1
),
},
});
# Label&Cesium.LabelCollection
面朝屏幕的文字 特点
- 始终朝向屏幕
- 注意创建的是集群对象 Cesium.LabelCollection()
- Label 对象只能用在 LabelCollection 当中
PointPrimitive&Cesium.PointPrimitiveCollection 点、PolylineCollection 线
PolylineCollection 可以同时渲染多条折线,性能较高;其他类型都是单独渲染某个折线的,会导致多了的话,渲染性能受影响。
groundPrimitives 贴地图元。如果加线就是 GroundPolylinePrimitive 贴地线。他的 geometry 是 GroundPolylineGeometry 类型。 arctype 可以设置线是否为曲线。
# 地形影像的加载
# 地形
地形的加载:
viewer.terrainProvider = worldTerrain;
// 上面的写法相当于下面这句
// viewer.scene.globe.terrainProvider = worldTerrain
viewer.scene.globe.enableLighting = true;
# 影像
WebMapTileServiceImageryProvider 加载 wmts 影像,WebMapServiceImageryProvider 加载 wms 影像,URLTemplateImageryProvider 是自定义影像加载
影像的加载:
var viewer = new Cesium.Viewer("cesiumContainer", {
imageryProvider: Cesium.createWorldImagery({
style: Cesium.IonWorldImageryStyle.AERIAL_WITH_LABELS,
}),
baseLayerPicker: false,
});
// 加载影像,imageryLayers 是 imageryLayerCollection 的一个子集
var layers = viewer.scene.imageryLayers;
// addImageryProvider 加载数据源
var blackMarble = layers.addImageryProvider(
// ImageryProvider
new Cesium.IonImageryProvider({ assetId: 3812 })
);
blackMarble.alpha = 0.5;
blackMarble.brightness = 2.0;
layers.addImageryProvider(
new Cesium.SingleTileImageryProvider({
url: "../images/Cesium_Logo_overlay.png",
rectangle: Cesium.Rectangle.fromDegrees(-75.0, 28.0, -67.0, 29.75),
})
)
# Primitive 图元
GeometryInstance 几何体的集合、modeMatrix/id/attribute 描述位置信息;Appearance 就是物体表面。Material 是材质。renderState 就是控制绘制的一些参数,比如物体的背面是否绘制。
Cesium.Primitive 可以为一个 PrimitiveCollection 对象,PrimitiveCollection 有一个 removeall 方法移除所有的图元
var viewer = new Cesium.Viewer('cesiumContainer');
var scene = viewer.scene;
// add 方法返回加载图元的本身
// original code
//viewer.entities.add({
// rectangle : {
// coordinates : Cesium.Rectangle.fromDegrees(-100.0, 20.0, -90.0, 30.0),
// material : new Cesium.StripeMaterialProperty({
// evenColor: Cesium.Color.WHITE,
// oddColor: Cesium.Color.BLUE,
// repeat: 5
// })
// }
//});
var instance = new Cesium.GeometryInstance({
geometry : new Cesium.RectangleGeometry({
rectangle : Cesium.Rectangle.fromDegrees(-100.0, 20.0, -90.0, 30.0),
vertexFormat : Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT
})
});
scene.primitives.add(new Cesium.Primitive({
geometryInstances : instance,
appearance : new Cesium.EllipsoidSurfaceAppearance({
material : Cesium.Material.fromType('Stripe')
})
}));
// 创建一个框
scene.primitives.add(
new Cesium.Primitive({
// geometryInstances 可以是一个数组,一次加载好几个图形
geometryInstances: new Cesium.GeometryInstance({
geometry: Cesium.BoxGeometry.fromDimensions({
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
dimensions: new Cesium.Cartesian3(400000.0, 300000.0, 500000.0),
}),
modelMatrix: Cesium.Matrix4.multiplyByTranslation(
Cesium.Transforms.eastNorthUpToFixedFrame(
Cesium.Cartesian3.fromDegrees(-105.0, 45.0)
),
new Cesium.Cartesian3(0.0, 0.0, 250000),
new Cesium.Matrix4()
),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.RED.withAlpha(0.5)
),
},
}),
appearance: new Cesium.PerInstanceColorAppearance({
closed: true,
}),
})
);
# 3D tiles 数据加载
var viewer = new Cesium.Viewer("cesiumContainer", {
terrainProvider: Cesium.createWorldTerrain(),
});
var tileset = new Cesium.Cesium3DTileset({
// url 也可以自定义的地址
url: Cesium.IonResource.fromAssetId(40866),
});
viewer.scene.primitives.add(tileset);
viewer.zoomTo(tileset);
# Appearance & Material
给图元设置材质
示例:https://sandcastle.cesium.com/?src=Materials.html&label=All
// Create a color material with fromType:
polygon.material = Cesium.Material.fromType('Color');
polygon.material.uniforms.color = new Cesium.Color(1.0, 1.0, 0.0, 1.0);
// Create the default material:
polygon.material = new Cesium.Material();
// Create a color material with full Fabric notation:
polygon.material = new Cesium.Material({
fabric : {
type : 'Color',
uniforms : {
color : new Cesium.Color(1.0, 1.0, 0.0, 1.0)
}
}
});
MaterialAppearance
var primitive = new Cesium.Primitive({
geometryInstances : new Cesium.GeometryInstance({
geometry : new Cesium.WallGeometry({
materialSupport : Cesium.MaterialAppearance.MaterialSupport.BASIC.vertexFormat,
// ...
})
}),
appearance : new Cesium.MaterialAppearance({
material : Cesium.Material.fromType('Color'),
faceForward : true
})
});
# Camera 和 Scene 中的其他函数使用
1 调试类函数 debugShowFramesPerSecond/debugShowGlobeDepth/... debugShowFramesPerSecond 就是显示 fps 数
2 WebGL渲染状态 logarithmicDepthBuffer/mode
3 事件 preRender/postUpdate/postRender/preUpdate/renderError
4 环境对象 sun/moon/skyBox/...
5 camera 相机
6 后处理相关示例
调试用的函数 debugShowFramesPerSecond debugShowDepthFrustum debugShowGlobeDepth
示例:https://sandcastle.cesium.com/?src=Camera%20Tutorial.html&label=All
https://sandcastle.cesium.com/?src=Camera.html&label=All
卷帘:https://sandcastle.cesium.com/?src=Imagery%20Layers%20Split.html&label=All
在控制台可以获取相机一些参数 var camera = scene.camera; 比如相机的位置 camera.position 获取到的是 x、y、z 值;基于 lookAtTransform 的设置。camera.positionCartogrraphic 获取地理位置。camera.transform 获取单位矩阵。camers.positionWC 是空间直角坐标系的位置;
pickPosition 获取位置。
function flyToSanDiego() {
Sandcastle.declare(flyToSanDiego);
viewer.camera.flyTo({
// 相机的位置
destination: Cesium.Cartesian3.fromDegrees(-117.16, 32.71, 15000.0),
});
}
function flyToHeadingPitchRoll() {
Sandcastle.declare(flyToHeadingPitchRoll);
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(-122.22, 46.12, 5000.0),
// 相机的姿态
orientation: {
heading: Cesium.Math.toRadians(20.0),
pitch: Cesium.Math.toRadians(-35.0),
roll: 0.0,
},
});
}
// 一个矩形
function viewRectangle() {
Sandcastle.declare(viewRectangle);
var west = -77.0;
var south = 38.0;
var east = -72.0;
var north = 42.0;
var rectangle = Cesium.Rectangle.fromDegrees(
west,
south,
east,
north
);
viewer.camera.setView({
destination: rectangle,
});
// Show the rectangle. Not required; just for show.
viewer.entities.add({
rectangle: {
coordinates: rectangle,
fill: false,
outline: true,
outlineColor: Cesium.Color.WHITE,
},
});
}
function flyToRectangle() {
Sandcastle.declare(flyToRectangle);
var west = -90.0;
var south = 38.0;
var east = -87.0;
var north = 40.0;
var rectangle = Cesium.Rectangle.fromDegrees(
west,
south,
east,
north
);
// 飞到矩形框
viewer.camera.flyTo({
destination: rectangle,
});
// Show the rectangle. Not required; just for show.
viewer.entities.add({
rectangle: {
coordinates: rectangle,
fill: false,
outline: true,
outlineColor: Cesium.Color.WHITE,
},
});
}
function setReferenceFrame() {
Sandcastle.declare(setReferenceFrame);
var center = Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883);
var transform = Cesium.Transforms.eastNorthUpToFixedFrame(center);
// View in east-north-up frame
var camera = viewer.camera;
camera.constrainedAxis = Cesium.Cartesian3.UNIT_Z;
// 确定相机旋转的中心点
camera.lookAtTransform(
// transform 默认为 地球的中心点
transform,
new Cesium.Cartesian3(-120000.0, -120000.0, 120000.0)
);
// Show reference frame. Not required.
referenceFramePrimitive = scene.primitives.add(
new Cesium.DebugModelMatrixPrimitive({
modelMatrix: transform,
length: 100000.0,
})
);
}
function setHeadingPitchRoll() {
Sandcastle.declare(setHeadingPitchRoll);
var camera = viewer.camera;
camera.setView({
destination: Cesium.Cartesian3.fromDegrees(
-75.5847,
40.0397,
1000.0
),
orientation: {
heading: -Cesium.Math.PI_OVER_TWO,
pitch: -Cesium.Math.PI_OVER_FOUR,
roll: 0.0,
},
});
}
function icrf(scene, time) {
if (scene.mode !== Cesium.SceneMode.SCENE3D) {
return;
}
var icrfToFixed = Cesium.Transforms.computeIcrfToFixedMatrix(time);
if (Cesium.defined(icrfToFixed)) {
var camera = viewer.camera;
var offset = Cesium.Cartesian3.clone(camera.position);
var transform = Cesium.Matrix4.fromRotationTranslation(icrfToFixed);
camera.lookAtTransform(transform, offset);
}
}
function viewInICRF() {
Sandcastle.declare(viewInICRF);
viewer.camera.flyHome(0);
clock.multiplier = 3 * 60 * 60;
scene.postUpdate.addEventListener(icrf);
scene.globe.enableLighting = true;
}
var viewChanged = document.getElementById("viewChanged");
var removeStart;
var removeEnd;
// 移动事件,监听相机位置变化
function cameraEvents() {
Sandcastle.declare(cameraEvents);
var camera = viewer.camera;
removeStart = camera.moveStart.addEventListener(function () {
viewChanged.style.display = "block";
});
removeEnd = camera.moveEnd.addEventListener(function () {
viewChanged.style.display = "none";
});
}
function cameraChanges() {
Sandcastle.declare(cameraChanges);
var i = 0;
removeChanged = viewer.camera.changed.addEventListener(function (
percentage
) {
++i;
cameraChanged.innerText =
"Camera Changed: " + i + ", " + percentage.toFixed(6);
cameraChanged.style.display = "block";
});
}
function flyInACity() {
Sandcastle.declare(flyInACity);
var camera = scene.camera;
camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(
-73.98580932617188,
40.74843406689482,
363.34038727246224
),
complete: function () {
setTimeout(function () {
camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(
-73.98585975679403,
40.75759944127251,
186.50838555841779
),
orientation: {
heading: Cesium.Math.toRadians(200.0),
pitch: Cesium.Math.toRadians(-50.0),
},
easingFunction: Cesium.EasingFunction.LINEAR_NONE,
});
}, 1000);
},
});
}
function losAngelesToTokyo(adjustPitch) {
var camera = scene.camera;
var tokyoOptions = {
destination: Cesium.Cartesian3.fromDegrees(
139.8148,
35.7142,
20000.0
),
orientation: {
heading: Cesium.Math.toRadians(15.0),
pitch: Cesium.Math.toRadians(-60),
roll: 0.0,
},
duration: 20,
// 飞行一定要经过的点
flyOverLongitude: Cesium.Math.toRadians(60.0),
};
var laOptions = {
destination: Cesium.Cartesian3.fromDegrees(
-117.729,
34.457,
10000.0
),
duration: 5,
orientation: {
heading: Cesium.Math.toRadians(-15.0),
pitch: -Cesium.Math.PI_OVER_FOUR,
roll: 0.0,
},
};
// 飞行结束后出发的事件
laOptions.complete = function () {
setTimeout(function () {
camera.flyTo(tokyoOptions);
}, 1000);
};
// 如果相机飞行高度超过 1000,修改相机位置
if (adjustPitch) {
tokyoOptions.pitchAdjustHeight = 1000;
laOptions.pitchAdjustHeight = 1000;
}
camera.flyTo(laOptions);
}
# Viewer / Entities
var viewer = new Cesium.Viewer('cesiumContainer');
Viewer/Entities的作用:
- 方便创建直观的对象,同时做到性能优化(billboard、point 等)
- 提供一些方便使用的函数:flyTo/zoomTo
- 赋予 Entity 对象时间这个属性,对象具备动态特性 Primitive 不具备
- 提供一些 UI(homeButton/sceneModePicker/projectionPicker/baseLayerPicker)
- 大量的快捷方式,camera 等未必是好事
- Datasource 模式来加载大规模数据:Geojson
窗口随 entity 进行移动
viewer.trackedEntity = vehicleEntity = dataSource.entities.getById(
"Vehicle"
);
# Cesium 进阶之路
- Web前端方向:Cesium 与 webpack(裁剪以及压缩),Cesium 与 vue(框架设计, 嵌入复杂业务系统),Cesium 的 UI(UI 设计,定制可复用的 Cesium 交互界面)
- 图形学方向:WebGL 深入,基于 Cesium 的可视化定制(视阈、水淹,水面、热力图,流场图,飞线图,扫描图)
- 数据预处理方向:投影变换,空间索引,LOD,3dtile 生成,数据存储,数据分发 服务,解决超大空间数据如何在 Cesium 上流畅可视化的问题