在3D场景下使用Cesium跟踪飞机时会出现摄像头晃动问题,导致地图背景不断晃动,影响观看。下面以最新的Cesium1.51源码为例,解析Cesium 渲染过程原理,分析跟踪实体时摄像头晃动的原因,找出可能的解决方法。
Cesium渲染过程分析
使用Cesium最简单示例代码如下:
1 | var viewer = new Cesium.Viewer('cesiumContainer'); |
Viewer是Cesium构建应用的最基础的组件。它又是其他组件的容器,包括:
- animation:控制时间前进、倒退、暂停以及前进和倒退速度的组件
- baseLayerPicker:图层选择组件
- fullscreenButton:控制是否全屏的组件
- vrButton:控制是否VR显示的组件
- geocoder:地理位置搜索组件
- homeButton:返回摄像头默认位置按钮组建
- infoBox:信息框组件
- sceneModePicker:场景模式选择组件
- selectionIndicator:选择指示组件
- timeline:时间线组件
- navigationHelpButton:导航帮助按钮,告诉使用者如何使用鼠标和触摸屏操纵虚拟地球
- CesiumWidget:虚拟地球组件
其中,虚拟地球组件CesiumWidget是Viewer包含核心组件,在Viewer中创建CesiumWidget对象时,将设置其useDefaultRenderLoop属性。设置该属性将启动渲染函数startRenderLoop。
1 | //from Source/Widgets/CesiumWidget/CesiumWidget.js |
函数startRenderLoop是Cesium渲染的开始,其代码如下:
1 | function startRenderLoop(widget) { |
CesiumWidget组件的render方法随后调用Scene的render方法。
1 | Scene.prototype.render = function(time) { |
Scene的render方法中tryAndCatchError函数将调用render函数。在该render函数中,地球的主要要素(地形&影像)的渲染,将在Globe的beginFrame和endFrame之间完成的。
1 | function render(scene) { |
其中updateAndExecuteCommands负责数据的调度,比如哪些Tile需要创建,这些Tile相关的地形数据,以及涉及到的影像数据之间的调度,都是在该函数中维护。而scene.globe.endFrame中,会对该帧所涉及的GlobeTile的下载,解析等进行处理。
Cesium跟踪实体
在Viewer组件构造函数内,Viewer订阅了场景组件Scene的渲染后事件postRender,以执行Viewer自己的_postRender函数。
1 | eventHelper.add(scene.postRender, Viewer.prototype._postRender, this); |
Viewer的_postRender函数代码如下,其中updateTrackedEntity函数将更新被跟踪实体的摄像头位置:
1 | Viewer.prototype._postRender = function() { |
updateTrackedEntity函数代码如下:
1 | function updateTrackedEntity(viewer) { |
除此之外,Viewer组件订阅了Clock组建的onTick事件,以执行其自身的_onTick事件处理函数:
1 | eventHelper.add(clock.onTick, Viewer.prototype._onTick, this); |
在Viewer组件的_onTick事件处理函数中,同样会更新被跟踪实体的摄像头位置。而Cesium摄像头跟踪飞机实体时产生晃动的根源即在此处。
1 | Viewer.prototype._onTick = function(clock) { |
可行的解决方案
在Viewer组件的_onTick函数做如下修改:
1 | Viewer.prototype._onTick = function(clock) { |
参考文献
- Cesium原理篇:1最长的一帧之渲染调度, by 法克鸡丝