前言 在二维地图中,我们只需要提供经纬度坐标即可定位到指定地点,而在三维世界中,经纬度(即二维坐标系)是无法描述一个要素的位置,所以需要使用笛卡尔坐标系来描述空间要素的位置。具体介绍这里不啰嗦,在之前的文章 中已经大致介绍过,这篇主要来讲解一下 camera 相关参数及利用camera能够做出的效果及其实现思路。
camera 我在 blender 中截了一张图模拟我们的相机对象,球体便是 cesium 的地球 🌏,个人觉得是很生动形象的。有助于我们了解相机的视角,方便后续讲解调整相机的相关参数。
官网的 api 中是这么介绍的:相机类由位置、方向和视锥体定义。 视锥体(viewing frustum)由6个(上、下、左、右、近、远)平面限定,每个平面可 由Cartesian4对象表示,其中x,y和z分量定义垂直于平面的单位向量,w分量是平面距原点/相机位置的距离。
1 2 3 4 5 6 7 8 var camera = new Cesium .Camera (scene); camera.position = new Cesium .Cartesian3 (); camera.direction = Cesium .Cartesian3 .negate (Cesium .Cartesian3 .UNIT_Z , new Cesium .Cartesian3 ()); camera.up = Cesium .Cartesian3 .clone (Cesium .Cartesian3 .UNIT_Y ); camera.frustum .fov = Cesium .Math .PI_OVER_THREE ; camera.frustum .near = 1.0 ; camera.frustum .far = 2.0 ;
说的什么鸟语,目前我们不需要了解这么复杂的自定义实现,只需要专注于它给我们提供的变换视角方法即可!
操作 camera cesium 中提供了多种方式对相机进行操作:setView
、flyTo
和lookAt
,主要介绍一下这三种方式的使用及异同
setView(options) viewer.camera.setView(options)
设置相机位置、方向和变换。
opitons<Object>
参数列表:
名称
类型
描述
destination
Cartesian3 | Rectangle
相机在 wgs84 坐标系中的最终位置,或自顶向下视图可见的矩形区域
orientation
Object
包含方位(direction),上方向(up)及方位角(heading),仰角(pitch),滚动角(roll)属性的对象
endTransform
Matrix4
相机参考坐标系的变换矩阵
convert
Boolean
Whether to convert the destination from world cool。(这参数我也不知道干嘛的……)
用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 viewer.camera .setView ({ destination : Cesium .Cartesian3 .fromDegrees (-117.16 , 32.71 , 15000.0 ) }); viewer.camera .setView ({ destination : cartesianPosition, orientation : { heading : Cesium .Math .toRadians (90.0 ), pitch : Cesium .Math .toRadians (-90 ), roll : 0.0 } }); viewer.camera .setView ({ orientation : { heading : Cesium .Math .toRadians (90.0 ), pitch : Cesium .Math .toRadians (-90 ), roll : 0.0 } }); viewer.camera .setView ({ destination : Cesium .Rectangle .fromDegrees (west, south, east, north) }); viewer.camera .setView ({ destination : Cesium .Cartesian3 .fromDegrees (-122.19 , 46.25 , 5000.0 ), orientation : { direction : new Cesium .Cartesian3 (-0.04231243104240401 , -0.20123236049443421 , -0.97862924300734 ), up : new Cesium .Cartesian3 (-0.47934589305293746 , -0.8553216253114552 , 0.1966022179118339 ) } });
flyTo(options) viewer.camera.flyTo(options)
相机从当前位置飞行到新的空间位置。
opitons<Object>
参数列表(其余参数请查阅官方文档 ):
名称
类型
描述
destination
Cartesian3 | Rectangle
相机在 wgs84 坐标系中的最终位置,或自顶向下视图可见的矩形区域
orientation
Object
包含方位(direction),上方向(up)及方位角(heading),仰角(pitch),滚动角(roll)属性的对象
duration
Number
飞行持续时间(以秒为单位)。如果省略,由飞行距离计算合理的持续时间。
complete
Camera~FlightCompleteCallback
飞行完成时执行的功能。
cancel
Camera~FlightCompleteCallback
飞行取消时执行的功能。
endTransform
Matrix4
飞行完成时相机将处于的参考系的变换矩阵。
maximumHeight
Number
飞行中的最大高度。
easingFunction
EasingFunction | EasingFunction~Callback
释放时调用功能。
……
……
……
用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 viewer.camera .flyTo ({ destination : Cesium .Cartesian3 .fromDegrees (-117.16 , 32.71 , 15000.0 ) }); viewer.camera .flyTo ({ destination : Cesium .Rectangle .fromDegrees (west, south, east, north) }); viewer.camera .flyTo ({ destination : Cesium .Cartesian3 .fromDegrees (-122.19 , 46.25 , 5000.0 ), orientation : { direction : new Cesium .Cartesian3 (-0.04231243104240401 , -0.20123236049443421 , -0.97862924300734 ), up : new Cesium .Cartesian3 (-0.47934589305293746 , -0.8553216253114552 , 0.1966022179118339 ) } }); viewer.camera .flyTo ({ destination : Cesium .Cartesian3 .fromDegrees (-122.19 , 46.25 , 5000.0 ), orientation : { heading : Cesium .Math .toRadians (175.0 ), pitch : Cesium .Math .toRadians (-35.0 ), roll : 0.0 } });
lookAt(target,offset) 通过目标(target)和偏移(offset)设置相机位置、方向。 目标(target)以世界坐标方式表示;偏移(offset)是在以目标为中心的本地“东-北-上”参考系中的笛卡尔坐标或“方位角/俯仰角/范围(heading/pitch/range)”。 如果偏移(offset)由笛卡尔坐标表示,它表示距离由变换矩阵定义的参考系中心的偏移量。 如果偏移(offset)由“heading/pitch/range”表示,方位角(heading)和俯仰角(pitch)的角度由根据变换矩阵定义的参考系确定,方位角(heading)从 y 轴开始朝向 x 轴增加,俯仰角(pitch)为从 xy 平面的旋转角度,正的俯仰角位于平面下方,负的俯仰角位于平面上方。 范围(range)为距中心点的距离。在 2D 中必须为自上而下视图,相机位于俯视目标的上方,目标上方的高度值是偏移量的大小。方位角根据偏移确定,如果不能从偏移确定方位角,则方位角为北方向。
viewer.camera.lookAt(target,offset)
设置相机位置、方向和变换。
opitons<Object>
参数列表:
名称
类型
描述
target
Cartesian3
目标在世界坐标中的空间位置。
offset
Cartesian3 | HeadingPitchRange
距以目标为中心的本地“东-北-上”参考系的偏移。
用法:
1 2 3 4 5 6 7 8 9 10 var center = Cesium .Cartesian3 .fromDegrees (-98.0 , 40.0 ); viewer.camera .lookAt (center, new Cesium .Cartesian3 (0.0 , -4790000.0 , 3930000.0 ));var center = Cesium .Cartesian3 .fromDegrees (-72.0 , 40.0 );var heading = Cesium .Math .toRadians (50.0 );var pitch = Cesium .Math .toRadians (-20.0 );var range = 5000.0 ; viewer.camera .lookAt (center, new Cesium .HeadingPitchRange (heading, pitch, range));
三者异同 首先三者基本都是通过调整相机对象的destination
和orientation
达到定位到指定位置的目的,不同点在于flyTo
是带有动画效果的飞入目的地,其余二者是直接定位到对应位置(如图)接下来我们详细说明一下参数中的方位角/俯仰角/滚动角,因为这和我们想要实现的效果息息相关。
Heading/Pitch/Roll 上文中已经对这三个角进行了中文注释
Heading:方位角
Pitch:俯仰角
Roll:滚动角
现在以你聪明的小脑袋瓜为中心,heading类似于左右环顾也就是左右方向的改变,pitch相当于低头抬头也就是上下方向的改变,而roll可以想象你在侧翻,而你的头始终朝前看,滚动一圈也就相当于roll了360°。
下图直观的展现了视角与三个角的关系:
HeadingPitchRange与HeadingPitchRoll 这两者都是用来定义一个局部使用的角对象,二者区别在HeadingPitchRoll主要用在模型变换上,而HeadingPitchRange更多用于相机、视角相关操作的时候。下边简单将二者有区别的参数进行展示。
实战案例 相机能做的效果非常多,而最经典的因该就是绕点飞行和漫游了,接下俩就两个实现效果来简单实现。
绕点飞行
我们首先创建一个点将绕点飞行的中心点可视化出来,不多介绍了就是基本的创建实体:
1 2 3 4 5 6 7 var entity = this .GLOBAL .Viewer .entities .add ({ position : Cesium .Cartesian3 .fromDegrees (116.391935 , 39.907123 ), point : { color : Cesium .Color .YELLOW , pixelSize : 10 , }, })
接下来利用zoomTo
将视角定位到我们创建的点,在then
中利用onTick
绑定一个每帧运行的方法,在方法中调整我们的heading(方位角),实现绕着点位旋转:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var heading = 0 var offset = new Cesium .HeadingPitchRange ( Cesium .Math .toRadians (heading), -Cesium .Math .toRadians (30 ), 1000 )this .GLOBAL .Viewer .zoomTo (entity, offset).then (() => { this .GLOBAL .Viewer .clock .onTick .addEventListener (() => { heading += 0.1 offset = new Cesium .HeadingPitchRange ( Cesium .Math .toRadians (heading), -Cesium .Math .toRadians (30 ), 1000 ) this .GLOBAL .Viewer .zoomTo (entity, offset) this .GLOBAL .Viewer .scene .screenSpaceCameraController .enableInputs = false }) })
第一人称视角漫游 在之前的文章【Cesium | 利用Property机制实现轨迹回放】 中我们已经实现了轨迹回放,现在我们可以利用相机和变换来实现固定视角。
在addRoamLine
方法中,我们添加固定视角方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 addRoamLine (positions, speed ) { this .viewer .trackedEntity = this .animateEntity ; if (this .isFirstPerson ) { this .clockEvent = this .viewer .clock .onTick .addEventListener ( this .firstPersonEvent () ); } }firstPersonEvent ( ) { return () => { if (!this .animateEntity ) return ; if (!this .viewer .trackedEntity ) return ; let center = this .animateEntity .position .getValue ( this .viewer .clock .currentTime ); let orientation = this .animateEntity .orientation .getValue ( this .viewer .clock .currentTime ); if (center) { let transform = Cesium .Transforms .eastNorthUpToFixedFrame (center); transform = Cesium .Matrix4 .fromRotationTranslation ( Cesium .Matrix3 .fromQuaternion (orientation), center ); this .viewer .camera .lookAtTransform ( transform, new Cesium .Cartesian3 (-100 , 0 , 50 ) ); } }; }
关于transform相关的知识等我统一梳理一下在分享吧,本来想在这篇文章里写,写着写着感觉自己的理解还不够到位……太弟弟了/(ㄒoㄒ)/~~