cesium 模拟地球自转

cesium 模拟地球自转

cesium 现有的机制分析

默认情况下,cesium 的球体其实在三维中是保持静止的状态。

而随着时间的流逝,默认的背景星空,是在不断地旋转的。

这个逻辑,我们从 cesium 的源码中就能发现端倪。

cesium 的 Scene 对象中,有个 render 方法,每一帧都被会调用,用于场景的重绘。


image.png
image.png

而 render 中有个 updateEnvironment 方法,用于更新环境


image.png
image.png

updateEnvironment 方法中,有一段更新 skyBox 的代码,同时我们能看到的是,同样有一段更新太阳和月亮位置的代码。


image.png
image.png

所以说,默认情况下,cesium 框架内部,通过计算,以地球为中心,真实的模拟了地球的运动。

产生地球自转可行的方案

如果现在有需求,想要实现地球自转的效果,那么我们该如何处理呢?

变与不变,其实是相对的。

所以其实我们有两种方案解决地球转动的问题:

  1. 地球球体自身旋转
  2. 旋转相机,产生自转的效果

旋转地球自身

先来讨论下我们的第一种方案。

如果我们想旋转地球自身的话,其实并不现实。

整个球包含着一套地理信息,而地理信息与实际的三维坐标信息并不一致,是有一套专门的转换机制的。

如果地球一转,就会使得三维坐标发生了变化,但是地理坐标却保持不变,如果还想使坐标对应,就必须要更新这套转换机制了。

这种做法显然是比较麻烦的。

我们知道,cesium 默认内置了一些三维对象的实现,比如 cylinder、plane、wall 等。

但是这些对象一般不是直接添加到 3d 场景中去的,而是通过附着在一个 entity 上添加的。

而且 entity 想要进行旋转操作,其实蛮复杂,必须要改变 orientation 值。

说实话,这个对新手及其不友好。

尤其是之前用过类似于 three.js 类似框架的童鞋,习惯了用 rotate 方式去旋转,但是用上 cesium 就犯难了,很难轻易的弄清楚,这个 orientation 该如何去写。

如果你查文档的话,文档也只会告诉你,这个 orientation 接受一个 Property 属性或者 undefined。


image.png
image.png

作为一枚新手,会让你一头雾水,特别是 cesium 里面的这个 Property,你刚接触还并不太好理解。

image.png
image.png

我们看看定义,大概能明白这是个什么东西。

其能够包含一组随着时间变化的值,也就是不同的时间,可以应用上不同的值。

这个功能很强大,特别是,当你了解到,原来 cesium 里面内置了一套系统时间的时候,就能感受到这么设计的魅力所在了。

咳咳,有点扯远了。

总之这种方案,如果放在其他的系统上,着实是可行的,就是麻烦了点。

但是放在地球上,显然是不太能行得通的方案。

旋转相机,产生自转的错觉

既然经过我们的分析,感觉第一种方案,过于复杂,那么现在我们来分析一下第二种方案,是否能够很好的满足我们的需求。

我们大家都知道的一个物理的基本原理是,运动是相对的。

所以,假使地球不转,我们视角(也就是这里的相机)反向转动,是能够模拟出地球转动的效果的。

那么相机,到底好不好进行旋转操作呢?

或者说的具体点,cesium 里面有没有提供这种 api,方便我们进行操作呢?

答案是肯定的!

不然,我也不会写这篇文章了。

需要注意的是,由于 cesium 支持多种视图模式,所以我们还需要针对不同的情况分别进行讨论,不能一概而论。

1. 哥伦布视图

对于哥伦布视图,讨论旋转好像没啥意义。

本身就是个 2.5 D 的视图,我很难想象,什么应用场景下,需要用到该视图模拟地球的旋转。

所以,这一块我们就先不考虑了。

2. 2d视图

由于 cesium 的球处于2d模式的时候,也是能够进行旋转缩放操作的。

因此,为了合理性,我们只考虑我们的 2d 视图一直处于类似初始化时候的平铺状态。


image.png
image.png

由于,cesium 2d 平面状态的时候,做了连续向左向右无限滚动的处理,因此,我们完全是可以采用平移镜头,而模拟地球自转的效果的。

刚好,cesium 的 camera 也提供了 moveLeftmoveRight api, 可以刚好用在这个地方,起到平移镜头的作用。

为了模拟真实的自转,有几点是我们需要考虑的问题。

  • 系统时间,每过24小时,我们的地球需要转一圈
  • 为了模拟地球真实的转速,当相机高度不同时,相机左移或者右移的幅度也是不一样的
  • 只在 2d 模式,且需要转的时候,才提供转动支持
  • 为了效果平滑,我们每一帧都需要通过计算得出平移距离,然后根据这个距离来平移相机

基于以上几点考量,第一步,我们需要构建一个每一帧都需要被调用的方法。

我们知道,在 cesium 系统中,我们可以通过 viewer.clock.onTick.addEventListener 的方式添加。

// 每一帧都会调用的方法
const icrf = () => {

};
viewer.clock.onTick.addEventListener(icrf);

接下来,就是填充这个方法内部的逻辑了。

首先我们过滤掉关于一些不执行平移操作的情况:

// 地球自转关键代码
const icrf = () => {
  if (viewer.scene.mode !== SceneMode.SCENE2D || !viewer.isRotation) {
    return;
  }

  const { camera, clock, scene } = viewer;
  if (!clock.shouldAnimate) return;
};

然后我们加上一段计算逻辑,算出我们相隔的时间,相机需要平移的距离:

// 地球自转关键代码

// 存储上一帧需要移动的距离
let prev = viewer.clock.currentTime;
const icrf = () => {
  const { camera, clock, scene } = this;
  if (
    this.scene.mode !== SceneMode.SCENE2D
     || !this.isRotation
     || this.mod !== 0
     || !clock.shouldAnimate
  ) {
    prev = clock.currentTime;
    return;
  }

  // 获取相机高度
  const { height } = scene.globe.ellipsoid.cartesianToCartographic(
    camera.position,
  );
  // 根据高度、地球半径等参数,计算出每秒钟相机需要平移的值
  const a = (465.2 / (6371 * 1000)) * (height + 6371 * 1000);
  const { currentTime } = clock;
  // 算出前后两次的时间间隔
  const interval = JulianDate.toDate(currentTime) - JulianDate.toDate(prev);
  prev = currentTime;
  // 调用api平移镜头
  camera.moveLeft((interval * a) / 1000);
};

最后这段代码应用上以后,是这样的效果:


2d-rotation-min.gif
2d-rotation-min.gif

这样的效果,放在球面上,可能不太能看得出来有啥作用,但是如果该平面是满铺在一个球体上,就能够看出来,刚好是我们想要的地球自转的效果了。

3. 3d视图

对于 cesium 的 3d 模式,旋转起来,好像更为简单点。

cesium 的 camera 对象,提供了 rotate api, 可以直接绕 z 轴(即地球上任意一点的法线方向)对相机进行旋转,就能产生地球自转的效果了。

话不多说,直接放代码吧。

// 地球自转关键代码
let prev = this.clock.currentTime;
const icrf = () => {
  const { camera, clock, scene } = this;
  if (
    this.scene.mode !== SceneMode.SCENE3D
    || !this.isRotation
    || this.mod !== 0
    || !clock.shouldAnimate
  ) {
    prev = clock.currentTime;
    return;
  }

  const { height } = scene.globe.ellipsoid.cartesianToCartographic(
    camera.position,
  );
  const a = (465.2 / (6371 * 1000)) * (height + 6371 * 1000);
  const { currentTime } = this.clock;
  const interval = JulianDate.toDate(currentTime) - JulianDate.toDate(prev);
  prev = currentTime;

  camera.rotate(
    Cartesian3.UNIT_Z,
    (Math.PI / (24 * 60 * 60)) * (interval / 1000),
  );
};
this.clock.onTick.addEventListener(icrf);

可以看到,与我们的 2d 视图下的自转基本类似,唯一有差别的地方是,我们旋转的时候,传入的是旋转角度,而不再像之前一样是距离了。

应用上这段代码以后,我们的 3d 模式下,球的旋转效果最终就形成了。


3d-rotation-min.gif
3d-rotation-min.gif
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355