【Cesium源码系列】Cesium的相机(1)

Cesium相机内部保存着相机的基本信息,主要有以下几个属性:

  • _transform:对外只读,变换矩阵
  • _invTransform:对外只读,变换矩阵的逆
  • _actualTransform:对内,实际变换矩阵
  • _actualInvTransform:对内,实际变换矩阵的逆
  • position:对外,相机的位置
  • _position:对内,相机的位置
  • _positionWC:对外只读,相机的世界坐标
  • _positionCartographic:对外只读,相机的经纬度坐标
  • direction:对外,相机朝向
  • _direction:对内,相机朝向
  • _directionWC:对外只读,相机朝向世界坐标
  • up:对外,相机头顶指向
  • _up:对内,相机头顶指向
  • _upWC:对外只读,相机头顶指向的世界坐标
  • right:对外,相机右方向
  • _right:对内,相机右方向
  • _rightWC:对外只读,相机右方向的世界坐标
  • _viewMatrix:对外只读,相机视图矩阵
  • _invViewMatrix:对外只读,相机视图矩阵的逆

Cesium的相机之所以会设置同名的两个相机设置,一个对外、一个对内,是为了更好地更新相机。

相机有一个内部方法updateMembers(camera),用于更新相机的参数,其更新过程如下:

对_position/position,_direction/direction,up/_up,_right/right进行比对,如果不相同,第一步是将对应的外部值(不带_的)拷贝到内部值(带_的),然后记录对应的属性是改变的,XXChanged = true
检查内部变量_transformChanged是否为true,判断变换矩阵_transform是否改变,如果变换矩阵改变,则将_transform拷贝到_actualTransform,并计算对应的_invTransform和_actualInvTransform
如果位置position或者transform改变了,则使用_actualTransform * _position计算_positionWC,并计算经纬度位置_positionCartographic
如果direction/up/right改变了,则要重新计算这三个值,由于任何一个值改变都会改变另外两个值,所以三个值都要进行更新(更新过程暂不赘述)
通过_actualTransform计算三个方向的世界坐标系下的向量_XXWC
更新视图矩阵(viewModelMatrix * _actualInvTransform)

可见,如果外部的position、up、right、direction改变了,实际上并不会改变内部的_transform和_actualTranform。但是首先让内部变量与对应的外部变量保持一致,然后对于position直接计算出_positionWC。但是对于direction、up和right,会首先将他们保持在相互正交的位置,然后使用_actualTransform对内部变量进行变换,得到对应的世界坐标。在保持相互正交的过程中,内部变量和外部变量可能不一致了。

如果transform改变了,那么首先会保持_transform、_actualTransform的一致,计算对应的逆,然后会用新的_transform计算_positionWC等世界坐标,但是不会改变_position等局部坐标。

但是由于这个方法是个内部方法,不用担心外部会调用,造成内外变量不一致的情况。其主要功能是在获取变量的时候更新一下数据,防止脏数据的产生。另一个功能是在内部函数_setTransform中,更改局部坐标系用的。

_setTransform方法的原理比较绕,我们使用调用它的一个get属性来解释。
在获取heading旋转角的时候,会两次使用到_setTransform方法,同时_setTransform内部会两次调用updateMembers方法。

首先,获取heading的时候,会先保存当前的内部_transform,然后通过_positionWC世界坐标(地心地固坐标)得到一个ENU2FixedFrame矩阵,这个矩阵是用来将一个地心地固坐标(ECEF)转换为东北天坐标(ENU)的。最常用的地方就是加载一个模型,模型有局部坐标,这个局部坐标实际上就是ENU坐标,但是直接放置在地球上会发现,模型的上方向并不能和地球表面垂直,整个模型与地球表面有个夹角,看起来是歪的。此时我们就需要计算模型所在位置的一个ENU2FixedFrame矩阵,使用了这个矩阵后,模型的上方向就会与地表垂直,同时X方向指向地球的东方,Y方向指向地球的北方,模型才能正确放置。这个矩阵实际上就是将XYZ坐标转为东-北-上坐标的一个转换矩阵,只是在不同的位置,这个矩阵完全不同罢了。

此处计算了相机所在世界位置的ENU2FixedFrame矩阵(以下记为ENU),然后进行_setTransform,在_setTransform中,首先保存了此时相机的世界坐标系下的状态,我们记为positionWC_o,upWC_o和directionWC_o,
然后,将传进来的ENU拷贝到内部_transform变量,并标记_transformChanged为true,然后进行updateMembers(this)。

通过上面的内容我们知道,在updateMembers中,我们没有改变position、direction等值,但是改变了_transform,因此,内部的_actualTransform会改变,同时,_positionWC等世界坐标会改变,但是_position等局部坐标不会改变,最终的结果变为_positionWC = ENU * _position,其他同理。

之后,_setTransform会重新计算外部变量position、direction、up和right。会使用新得到的_actualTransform的逆,即ENU-1,与旧的positionWC_o进行相乘,得到新的position,然后再一次进行updateMembers,其他同理。此时_transformChanged为false,因此不会改变_transform,但是由于position发生了改变,_position会拷贝position,并用新的_position去计算_positionWC。

在这个过程中,外部变量position可记为ENU-1 * positionWC_o,内部_position会进行拷贝,然后计算新的_positionWC=_transform * _position = ENU * ENU-1 * positionWC_o = positionWC_o。

也就是说,经过_setTransform之后,相机的世界位置并不会改变,但是内部的_transform和_actualTransform会改变为设置的transform,同时,_position和position也会发生改变,但是最终结果两者保持一致。

此时,相机的position、direction、right、up等值,实际上是变到了世界坐标经过transform变换之后的空间中去。更通俗一点来说,我们在写shader的时候,viewMatrix是将模型顶点从世界空间变换到相机空间,同样,_setTransform就是通过transform矩阵将模型顶点从世界空间(地心地固坐标)变换到一个类相机空间(ENU坐标空间)中去。

heading获取函数中,第一次_setTransform是将相机从世界空间转到ENU上来,然后通过球面空间中的direction和up计算heading值。然后使用旧的_transform再调用一次_setTransform,将_position和_transform进行还原即可。

因此,cesium的相机的内部函数_setTransform的作用,就是将相机变换到另外一个空间去,获取这个空间内的相机的状态。

posted @ 2024-03-26 09:09  李煎饼_GISer  阅读(36)  评论(0编辑  收藏  举报