CesiumJS
进阶
记录使用
Cesium
的实体(Entity
)API
绘制空间数据、使用Viewer
为操作entities
提供出来的功能函数、使用Primitive
API
的几何图形和外观系统、创建使用ParticleSystem
粒子系统等。
空间数据可视化(Entity
)
Cesium 具有丰富的用于空间数据的 API,可以分为两类:面向图形开发人员的低级API(通常称为原始(
Primitive
)API
)和用于数据驱动的可视化的高级API(称为实体(Entity
)API
)。
Primitive API
原始API
的主要目标是暴露手头执行任务所需的最小抽象量。它希望我们像图形程序员一样思考,并使用图形术语。它的结构是为给定的可视化类型提供最有性能和灵活性的实现,而不是为了 API
的一致性。
原始API
功能强大且灵活,但大多数应用程序都提供比 Primitive API
的抽象级别更高的服务接口。Primitive API
更面向于底层图形开发。
Entity API
实体API
的目的是公开一组设计一致的高级对象,这些对象将相关的可视化和信息聚合到一个统一的数据结构中,我们称之为实体。它让我们专注于展示我们的数据,而不是担心可视化的潜在机制。它还提供了易于构建复杂的、时间动态可视化的构造,这种可视化方式与静态数据自然相适应。
虽然 实体API
实际上在背后使用了 Primitive API
,但我们几乎永远不必关注实现细节。通过将各种启发式应用到我们提供的数据,实体API
能够提供灵活的、高性能的可视化,同时公开一致的、易于学习和易于使用的接口。
管理 Entities
每一个
Entity
对象都被添加到EntityCollection
对象集合中,其中每个实体都有唯一的ID
。
- 添加
1 | let entity = new Entity({ |
所有实体实例都有一个唯一的 id
,在创建或修改时可用于从集合中检索实体。我们可以为实体指定一个 ID
,否则将自动生成一个 ID
。
- 隐藏
1 | entity.show = false; |
- 获取
使用
getByiId
检索实体。如果不存在具有提供的ID
的实体,则返回undefined
。
1 | let entity = this.$viewer.entities.getById('uniqueId') |
要获取实体或创建新实体(如果不存在),可以使用 getOrCreateEntity
。
1 | let entity = this.$viewer.entities.getOrCreateEntity('uniqueId'); |
- 修改
可以在获取实体后对其属性进行修改。
1 | let entity = this.$viewer.entities.getById('uniqueId') |
- 移除
1 | // 先查后删 |
点、图标和标签(Points, billboards, and labels)
可以通过设置
position
、point
和label
来创建图形点或标签。
- 点 && 文字标签
1 | let point = this.$viewer.entities.add({ |
- 图标
1 | let logo = this.$viewer.entities.add({ |
Picking
拾取
在
Cesium
中,想获取不同的对象,需要通过pick
方法来进行拾取。
然而其中又有多种 pick
的方法,包括 scene
中的 pick
、pickPosition
和 drillPick
;camera
中的 getPickRay
和 pickEllipsoid
;以及 Globe
中的 pick
。
- 屏幕坐标
通过
movement.position
获取。
1 | // 屏幕坐标 |
- 世界坐标
通过
viewer.scene.camera.pickEllipsoid(movement.position, ellipsoid)
获取,可以获取当前点击视线与椭球面相交处的坐标,其中ellipsoid
是当前地球使用的椭球对象。
1 | // 世界坐标 |
- 场景坐标
通过
viewer.scene.pickPosition(movement.position)
获取,根据窗口坐标,从场景的深度缓冲区中拾取相应的位置,返回笛卡尔坐标。
1 | // 场景坐标 |
- 地标坐标
通过
viewer.scene.globe.pick(ray, scene)
获取,可以获取点击处地球表面的世界坐标,不包括模型、倾斜摄影表面。其中ray=viewer.camera.getPickRay(movement.position)
。
1 | // 地标坐标 |
形状和体积(Shapes and Volumes)
之前介绍过,Cesium
加载点线面矢量数据可以通过 Entity API
或 Primitive API
实现,而无论我们怎样定义实体实例和几何,所有形状和物体都有一组共同的属性来控制它们的外观。
fill
布尔型,用于指定目标形状是否被填充,默认为
true
outline
布尔型,用于指定是否绘制形状的边缘, 默认为
false
material
如果
fill
为true
,该属性可以控制填充的材质类型,默认为Color.WHITE
具体形状的外观属性可通过官方 API
中的 Cesium.Entity
下各形状的 Type
属性查看。
材质
例如我们创建一个蓝色半透明的椭圆实例,默认 fill
为 true
:
1 | let entity = this.$viewer.entities.add({ |
然后改变其材质属性,实现各种不同的效果:
1 | // 棋盘材质 |
1 | // 条纹材质 |
1 | // 网格材质 |
轮廓
outline
没有相应的材料,而是依赖于两个独立的 outlineColor
和 outlineWidth
属性。
1 | entity.ellipse.fill = false; |
高度与挤压
覆盖在地球上的所有形状,当前是圆、椭圆、多边形和矩形,也可以放置在海拔高度或挤压成一个物体。在这两种情况下,形状或物体仍然符合其下方的地球曲率。
当需要物体距离地面一定高度的时候,可以在相应的图形对象上设置高度属性:
1 | let polygon = this.$viewer.entities.add({ |
在侧面可以看到物体就有了高度。
将形状挤压成物体同样,需要设置 extrudedHeight
属性:
1 | let polygon = this.$viewer.entities.add({ |
注意:意外发现当同时使用 height
与 extrudedHeight
并且值相同时,extrudedHeight
不会生效。
三维图形(3D models)
CesiumJS
支持通过glTF
(运行时asset format
)创建3D
模型。
1 | let position = Cesium.Cartesian3.fromDegrees( |
1 | let position = Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706, 0); |
默认情况下,模型竖直放置、并且面向东面。可以指定四元组(Quaternion
)给 Entity.orientation
属性,以改变放置的方向。
1 | //位置 |
heading
(yaw
)、pitch
、roll
对应了绕 Z
(垂直轴)、Y
(维度方向)、X
(经度方向)进行旋转,正数表示顺时针旋转(由于相对运动,在浏览器上看起来是地球在逆时针旋转),可以参考下图理解(人面向北面,摇头heading
、点头pitch
、歪头roll
)。
Entity API
Entity 实例将多种形式的可视化聚集到单个高级对象中。通过手动创建并将实体添加到
Viewer.entities
中。
Name | Type | Description |
---|---|---|
box | BoxGraphics | 与该实体关联的框。 |
corridor | CorridorGraphics | 与该实体关联的走廊。 |
cylinder | CylinderGraphics | 与该实体关联的圆柱体。 |
ellipse | EllipseGraphics | 与该实体关联的椭圆。 |
ellipsoid | EllipsoidGraphics | 与该实体关联的椭球。 |
label | LabelGraphics | 与该实体关联的options.label。 |
model | ModelGraphics | 与该实体关联的模型。 |
path | PathGraphics | 与该实体关联的路径。 |
plane | PlaneGraphics | 与该实体关联的平面。 |
point | PointGraphics | 与该实体关联的点。 |
polygon | PolygonGraphics | 与该实体关联的多边形。 |
polyline | PolylineGraphics | 与该实体关联的折线。 |
rectangle | RectangleGraphics | 与该实体关联的矩形。 |
wall | WallGraphics | 与该实体关联的墙。 |
Box
1 | // 立方体 |
Ellipse
1 | // 椭圆 |
Corridor
走廊。
1 | // Corridor |
Cylinder
1 | //圆柱和圆锥 Cylinder Cones |
Polygon
1 | // 多边形 Polygon |
Polylines
1 | // Polylines |
Polyline Volumes
1 | // Polyline Volumes |
Rectangle
1 | // rectangle |
Sphere Ellipsoid
1 | // Sphere Ellipsoid |
Wall
1 | // wall |
几何图形和外观(Geometry and Appearances
)
我们可以通过 Primitive
API
来操控几何图形及其外观,或者绘制各种特殊的形状。需要先得到 Scene
对象,然后在其上添加 Primitive
对象:
Primitive
由两个部分组成:
几何形状(
Geometry
):定义了Primitive
的结构,例如三角形、线条、点等外观(
Appearance
):定义Primitive
的着色(Sharding),包括GLSL(OpenGL着色语言,OpenGL Shading Language)顶点着色器和片段着色器( vertex and fragment shaders),以及渲染状态(render state)
Primitive API
Cesium支持以下几何图形:
几何图形 | 说明 |
---|---|
BoxGeometry | 立方体 |
BoxOutlineGeometry | 仅有轮廓的立方体 |
CircleGeometry | 圆形或者拉伸的圆形 |
CircleOutlineGeometry | 只有轮廓的圆形 |
CorridorGeometry | 走廊:沿着地表的多段线,且具有一定的宽度,可以拉伸到一定的高度 |
CorridorOutlineGeometry | 只有轮廓的走廊 |
CylinderGeometry | 圆柱、圆锥或者截断的圆锥 |
CylinderOutlineGeometry | 只有轮廓的圆柱、圆锥或者截断的圆锥 |
EllipseGeometry | 椭圆或者拉伸的椭圆 |
EllipseOutlineGeometry | 只有轮廓的椭圆或者拉伸的椭圆 |
EllipsoidGeometry | 椭球体 |
EllipsoidOutlineGeometry | 只有轮廓的椭球体 |
RectangleGeometry | 矩形或者拉伸的矩形 |
RectangleOutlineGeometry | 只有轮廓的矩形或者拉伸的矩形 |
PolygonGeometry | 多边形,可以具有空洞或者拉伸一定的高度 |
PolygonOutlineGeometry | 只有轮廓的多边形 |
PolylineGeometry | 多段线,可以具有一定的宽度 |
SimplePolylineGeometry | 简单的多段线 |
PolylineVolumeGeometry | 多段线柱体 |
PolylineVolumeOutlineGeometry | 只有轮廓的多段线柱体 |
SphereGeometry | 球体 |
SphereOutlineGeometry | 只有轮廓的球体 |
WallGeometry | 墙 |
WallOutlineGeometry | 只有轮廓的墙 |
1 | // 盒子 box |
Appearance
primitive
的属性除了Geometry
,还有另一个关键属性就是Appearance
。它定义了primitive
的纹理,即单个像素的颜色。primitive
可以有多个几何实例,但只能有一个外观。
外观定义了绘制 Primitive
时在GPU上执行的完整 GLSL
顶点和面片着色器。外观还定义了完整的渲染状态,它控制绘制 primitvie
时 GPU
的状态。
我们可以直接定义渲染状态,也可以使用更高级的属性,如“闭合(closed
)”和“半透明(translucent
)”,外观将转换为渲染状态。
1 | let appearance = new Cesium.PerInstanceColorAppearance({ |
粒子系统(Introduction to Particle Systems
)
粒子系统是一种图形技术,可以模拟复杂的物理效果。粒子系统是小图像的集合,当它们一起观看时,会形成一个更复杂的“模糊”物体,如火、烟、天气或烟花等效果。
通过使用诸如初始位置、速度和生命周期等属性指定单个粒子的行为,可以控制这些复杂的效果。
Particle system basics
基础粒子效果
1 | let model = this.$viewer.entities.getById("Truck"); |
ParticleSystem
一个参数化的对象,用于控制单个粒子对象 Particle
随时间的外观和行为。粒子由粒子发射器产生,有一个位置和类型,存活一段时间,然后消亡。
其中一些属性是动态的,如 startScale
和 endScale
、startColor
和endColor
。
对于具有最大和最小输入的每个变量,粒子上该变量的实际值将随机分配到最大和最小输入之间,并在粒子的整个生命周期内静态保持该值。允许像这样更改的属性包括imageSize
、speed
、life
和particleLife
。
Emitters
发射器
当粒子诞生时,其初始位置和速度矢量由
ParticleEmitter
控制。发射器将每秒生成一些粒子,由emissionRate
参数指定,根据发射器类型用随机速度初始化。
BoxEmitter
盒形发射器
BoxEmitter
在一个盒子内随机取样的位置初始化粒子,并将它们从六个盒子表面中的一个引导出来。它接收Cartesian3
参数,该参数指定框的宽度、高度和深度尺寸。
1 | let particleSystem = scene.primitives.add(new Cesium.ParticleSystem({ |
CircleEmitter
圆形发射器
CircleEmitter
在发射器上轴线方向上的圆形内的随机采样位置初始化粒子。它接收一个指定圆半径的浮点参数。
1 | let particleSystem = scene.primitives.add(new Cesium.ParticleSystem({ |
ConeEmitter
锥形发射器
ConeEmitter
在圆锥体的顶端初始化粒子,并以随机的角度引导它们离开圆锥体。它使用一个指定圆锥体角度的浮点参数。圆锥体沿发射器的上轴定向。
1 | let particleSystem = scene.primitives.add(new Cesium.ParticleSystem({ |
SphereEmitter
球形发射器
SphereEmitter
在球体内随机取样的位置初始化粒子,并将它们从球体中心向外引导。它使用一个指定球体半径的浮点参数。
1 | let particleSystem = scene.primitives.add(new Cesium.ParticleSystem({ |
配置粒子系统
粒子发射率
emissionRate
控制每秒发射多少粒子,可以改变系统中粒子的密度。
指定一组 burst
在一定时间发射粒子,可以增加粒子系统的多样性或爆炸性。
1 | bursts : [ |
在给定的时间,粒子会在最大和最小之间进行发射。
粒子生命周期和系统生命周期
默认情况下,粒子系统将永远运行。要使粒子系统以设定的持续时间运行,可以使用 lifetime
以秒为单位指定持续时间,并将 loop
设置为 false
。
1 | lifetime : 16.0, |
要随机化每个粒子的输出,可以使用变量 minimumParticleLife
和 maximumArticleLife
。
1 | minimumParticleLife: 5.0, |
样式化粒子
- 颜色
粒子的样式是使用 image
和 color
指定的纹理,这些纹理可以在粒子的生命周期中更改以创建动态效果。 下面的代码使烟雾粒子从绿色过渡到白色。
1 | startColor : Cesium.Color.LIGHTSEAGREEN.withAlpha(0.7), |
- 大小
粒子的大小由 imageSize
控制。要随机化大小,可以使用minimumImageSize.x
和maximumImageSize.x
控制宽度(以像素为单位),并使用minimumImageSize.y
和maximumImageSize.y
控制高度(以像素为单位)。
1 | minimumImageSize : new Cesium.Cartesian2(30.0, 30.0), |
粒子的大小可以通过 startScale
和 endscale
属性在其生命周期中进行调整,以使粒子随时间增长或收缩。
1 | startScale: 1.0, |
- 速度
速度由 speed
或 minimumSpeed
和 maximumSpeed
控制。
1 | minimumSpeed: 5.0, |
UpdateCallback
更新回调
通过应用更新函数,可以进一步自定义粒子系统。对于重力、风或颜色更改等效果,它可以动态更新每个粒子。
一个让粒子对重力作出反应的例子:
1 | let gravityVector = new Cesium.Cartesian3(); |
该函数计算重力矢量,并使用重力加速度来改变粒子的速度。 将重力设置为粒子系统的 updateFunction
:
1 | updateCallback : applyGravity |
Positioning
定位
使用两个Matrix4变换矩阵定位粒子系统:
modelMatrix
将粒子系统从模型转换为世界坐标。
emitterModelMatrix
在粒子系统的局部坐标系中变换粒子系统发射器。
1 | let model = this.$viewer.entities.getById("Truck"); |
高级粒子系统效应(Advanced Particle System Effects
)
一些动态更新粒子的行为,需要在
updateParticle
函数中定义粒子的移动行为和其他动态元素。
以雪粒子更新函数为例:
1 | let snowUpdate = function (particle, dt) { |
更新函数用于定义粒子的移动、排列和可视化。修改粒子的color颜色、imageSize图像大小和particleLife粒子生命周期。我们甚至可以基于到相机距离定义粒子、导入模型或到地球本身的距离等。
- 额外的天气效应
使用雾和大气效果来增强可视化效果,并匹配我们试图复制的天气类型。
hueshift沿着颜色光谱改变颜色,saturationShift改变了视觉实际需要的颜色与黑白的对比程度,brightnessShift改变了颜色的生动程度。
雾密度改变了地球上覆盖物与雾的颜色之间的不透明程度。雾的minimumBrightness用来使雾变暗。
1 | // snow |
Snow
1 | // 雪景 |
Rain
1 | // 雨景 |