Controls how global geospatial coordinates are mapped to coordinates in the Unreal Engine level.
Internally, Cesium uses a global Earth-centered,Earth-fixed (ECEF) ellipsoid-centered coordinate system, where the ellipsoid
is usually the World Geodetic System 1984 (WGS84) ellipsoid.
This is a right-handed system centered at the Earth’s center of mass, where +X is in the direction of the intersection of the Equator and the Prime Meridian (zero degrees longitude), +Y is in the direction of the intersection of the Equator and +90 degrees longitude, and +Z is through the North Pole.
This Actor is used by other Cesium Actors to control how this coordinate system is mapped into an Unreal Engine world and level.
CesiumGeoreference的用途主要是控制如何将地理空间坐标转换到虚幻引擎关卡中的坐标。
Cesium 使用的地心空间直角坐标系(ECEF),这里通常使用WGS84椭球体。
地心空间直角坐标系(ECEF)是一个以地球质心为中心的右手系,其中 +X
:地球质心指向赤道与本初子午线的交点方向(0经度),+Y
:地球质心指向赤道和+90度的子午线的交点方向,+Z
:地球质心指向北极方向。
其他 Cesium Actor
使用CesiumGeoreference Actor
将地心空间直角坐标系(ECEF)映射到虚幻引擎世界和关卡的坐标系当中。
XYZ: ECEF 地球地心空间直角坐标系。
BLH:地理坐标,经纬度,λ和φ。
ENU: 东北天坐标系,也叫站心坐标系以用户所在位置(Pawn)为坐标原点。坐标系定义为: X轴:指向东边;Y轴:指向北边 ;Z轴:指向天顶。ENU局部坐标系采用三维直角坐标系来描述地球表面,实际应用较为困难,因此一般使用简化后的二维投影坐标系来描述。
UE4的空间直角坐标系,前(x)+右(y)+上(z)。
转换关系如下图所示:
BLH<->ECEF:
地理坐标到地心空间直角坐标的转换(geodetic to geocenter)。
//BLH->ECEF
glm::dvec3
cartographicToCartesian(const Cartographic& cartographic) const noexcept;
//ECEF->BLH
std::optional<Cartographic>
cartesianToCartographic(const glm::dvec3& cartesian) const noexcept;
glm::dvec3 center(0.0, 0.0, 0.0);
const CesiumGeospatial::Ellipsoid& ellipsoid =
CesiumGeospatial::Ellipsoid::WGS84;
center = ellipsoid.cartographicToCartesian(
CesiumGeospatial::Cartographic::fromDegrees(
this->OriginLongitude,
this->OriginLatitude,
this->OriginHeight));
NEU<->ECEF:
两个空间直角坐标系的转换,只要计算出转换矩阵即可。
//ENU 到 ECEF 的转换矩阵
this->_georeferencedToEcef =
CesiumGeospatial::Transforms::eastNorthUpToFixedFrame(center);
// 求逆,即ECEF->ENU
this->_ecefToGeoreferenced = glm::affineInverse(this->_georeferencedToEcef);
设置NEU的坐标原点的位置。
UENUM(BlueprintType)
enum class EOriginPlacement : uint8 {
/**
* Use the tileset's true origin as the Actor's origin. For georeferenced
* tilesets, this usually means the Actor's origin will be at the center
* of the Earth.
*/
TrueOrigin UMETA(DisplayName = "True origin"),
/*
* Use the center of the tileset's bounding volume as the Actor's origin. This
* option preserves precision by keeping all tileset vertices as close to the
* Actor's origin as possible.
*/
BoundingVolumeOrigin UMETA(DisplayName = "Bounding volume center"),
/**
* Use a custom position within the tileset as the Actor's origin. The
* position is expressed as a longitude, latitude, and height, and that
* position within the tileset will be at coordinate (0,0,0) in the Actor's
* coordinate system.
*/
CartographicOrigin UMETA(DisplayName = "Longitude / latitude / height")
};
True origin
设置NEU和ECEF一致。将Actor的原点设置为地球质心。
Bounding volume center
使用包围盒的中心作为Actor的原点。这种设置使所有tileset的顶点尽可能的靠近Actor的原点,保证其精度。
Longitude / latitude / height
使用tileset中自定义的位置来作为Actor的原点。可以使用过经纬度和高程来表示其位置,这样的话在Actor的坐标系统中tileset的位置将会变成(0,0,0)。
// ENU转ECEF (ENU->XYZ)
glm::dmat4 _georeferencedToEcef;
// ECEF转ENU (XYZ->ENU)
glm::dmat4 _ecefToGeoreferenced;
// UE4 绝对坐标转 ECEF
glm::dmat4 _ueAbsToEcef;
// ECEF 转 UE4 绝对坐标
glm::dmat4 _ecefToUeAbs;
构造函数中四个矩阵初始化为单位矩阵:
ACesiumGeoreference::ACesiumGeoreference()
: _georeferencedToEcef(1.0),
_ecefToGeoreferenced(1.0),
_ueAbsToEcef(1.0),
_ecefToUeAbs(1.0),
_insideSublevel(false) {
PrimaryActorTick.bCanEverTick = true;
}
四个矩阵的更新:
void ACesiumGeoreference::UpdateGeoreference() {
// update georeferenced -> ECEF
// 将Actor的原点放置在ECEF的中心,即 NEU和ECEF重合。
if (this->OriginPlacement == EOriginPlacement::TrueOrigin) {
this->_georeferencedToEcef = glm::dmat4(1.0);
}
else
{
glm::dvec3 center(0.0, 0.0, 0.0);
//将Actor的原点放置在tileset的包围盒中心
if (this->OriginPlacement == EOriginPlacement::BoundingVolumeOrigin)
{
// TODO: it'd be better to compute the union of the bounding volumes and
// then use the union's center,
// rather than averaging the centers.
// 遍历每一个地理参考的对象,合并计算它们包围盒的中心
size_t numberOfPositions = 0;
for (const TWeakInterfacePtr<ICesiumGeoreferenceable> pObject : this->_georeferencedObjects)
{
if(pObject.IsValid() && pObject->IsBoundingVolumeReady())
{
std::optional<Cesium3DTiles::BoundingVolume> bv = pObject->GetBoundingVolume();
if (bv)
{
center += Cesium3DTiles::getBoundingVolumeCenter(*bv);
++numberOfPositions;
}
}
}
if (numberOfPositions > 0)
{
center /= numberOfPositions;
}
}
//使用自定义经纬度高程设置原点位置
else if (this->OriginPlacement == EOriginPlacement::CartographicOrigin)
{
const CesiumGeospatial::Ellipsoid& ellipsoid = CesiumGeospatial::Ellipsoid::WGS84;
//由经纬度高程换算到ECEF中的位置,并将其作为原点
center = ellipsoid.cartographicToCartesian(CesiumGeospatial::Cartographic::fromDegrees(
this->OriginLongitude,
this->OriginLatitude,
this->OriginHeight));
}
// 上述两种center设置是ENU原点在ECEF当中的坐标,ENU->ECEF的计算。
this->_georeferencedToEcef =
CesiumGeospatial::Transforms::eastNorthUpToFixedFrame(center);
}
// update ECEF -> georeferenced
// 求逆矩阵
this->_ecefToGeoreferenced = glm::affineInverse(this->_georeferencedToEcef);
// update UE -> ECEF
//
this->_ueAbsToEcef = this->_georeferencedToEcef *
CesiumTransforms::scaleToCesium *
CesiumTransforms::unrealToOrFromCesium;
// update ECEF -> UE
this->_ecefToUeAbs = CesiumTransforms::unrealToOrFromCesium *
CesiumTransforms::scaleToUnrealWorld *
this->_ecefToGeoreferenced;
//通知所有Object参考系的变化
for (TWeakInterfacePtr<ICesiumGeoreferenceable> pObject :
this->_georeferencedObjects) {
if (pObject.IsValid()) {
pObject->NotifyGeoreferenceUpdated();
}
}
this->_setSunSky(this->OriginLongitude, this->OriginLatitude);
}