欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Cesium for UE4 解决抖动的问题

程序员文章站 2022-06-10 23:34:25
...

Cesium for UE4 解决抖动的问题

由于单精度浮点数的精度问题,使得相机在近距离浏览离世界原点非常远的物体时会发生抖动现象。
目前的解决方案是:通过GLM库的双精度数据类型来保存原始的高精度坐标数值,适时改变UE4中世界原点的位置,重新计算在新的原点下的相对位置。

重新设置世界原点

Camera has moved too far from the origin, move the origin.

当相机距离原点很远的时候,重新设置原点。

ACesiumGeoreference:提供场景内坐标参考的类,关联所有具有地理参考的Actor。初始化世界原点,并通过判断和相机的距离重新更新世界原点。

void ACesiumGeoreference::_performOriginRebasing() {

bool isGame = this->GetWorld()->IsGameWorld();
const FIntVector& originLocation = this->GetWorld()->OriginLocation;

if (isGame && this->WorldOriginCamera) {

        const FMinimalViewInfo& pov = this->WorldOriginCamera->ViewTarget.POV;
        const FVector& cameraLocation = pov.Location;

        if (this->KeepWorldOriginNearCamera &&
            (!this->_insideSublevel || this->OriginRebaseInsideSublevels) &&
            !cameraLocation.Equals(
                FVector(0.0f, 0.0f, 0.0f),
                this->MaximumWorldOriginDistanceFromCamera)) 
        {
        // Camera has moved too far from the origin, move the origin.
        this->GetWorld()->SetNewWorldOrigin(FIntVector(
            static_cast<int32>(cameraLocation.X) +
                static_cast<int32>(originLocation.X),
            static_cast<int32>(cameraLocation.Y) +
                static_cast<int32>(originLocation.Y),
            static_cast<int32>(cameraLocation.Z) +
                static_cast<int32>(originLocation.Z)));
        }

    }
}

应用世界偏移

新的世界原点更新后,对场景的两类物体应用世界偏移(WorldOffset)。一类是地形数据的Mesh(UCesium3DTilesetRoot),另一类是放置在场景中的带地理参考的模型Mesh(UCesiumGeoreferenceComponent)。

virtual void ApplyWorldOffset(const FVector& InOffset, bool bWorldShift) override;
UCesium3DTilesetRoot::ApplyWorldOffset(const FVector& InOffset,bool bWorldShift);
UCesiumGeoreferenceComponent::ApplyWorldOffset(const FVector& InOffset,bool bWorldShift);

Cesium3DTilesetRoot 地形的世界偏移

  1. 计算获取新的世界原点。
  2. 更新ECEF到UE4新的世界原点的变换矩阵。
void UCesium3DTilesetRoot::ApplyWorldOffset(
    const FVector& InOffset,
    bool bWorldShift) {

  USceneComponent::ApplyWorldOffset(InOffset, bWorldShift);

  const FIntVector& oldOrigin = this->GetWorld()->OriginLocation;
  this->_worldOriginLocation = glm::dvec3(
      static_cast<double>(oldOrigin.X) - static_cast<double>(InOffset.X),
      static_cast<double>(oldOrigin.Y) - static_cast<double>(InOffset.Y),
      static_cast<double>(oldOrigin.Z) - static_cast<double>(InOffset.Z));

  // Do _not_ call _updateAbsoluteLocation. The absolute position doesn't change
  // with an origin rebase, and we'll lose precision if we update the absolute
  // location here.

  this->_updateTilesetToUnrealRelativeWorldTransform();
}

根据新的UE4 世界原点 ,计算物体的ECEF坐标转换到UE4相对坐标的转换矩阵。

void UCesium3DTilesetRoot::_updateTilesetToUnrealRelativeWorldTransform() {

  ACesium3DTileset* pTileset = this->GetOwner<ACesium3DTileset>();
  if (!IsValid(pTileset->Georeference)) {
    this->_tilesetToUnrealRelativeWorld = glm::dmat4(1.0);
    this->_isDirty = true;
    return;
  }

  const glm::dmat4& ellipsoidCenteredToUnrealWorld =
      pTileset->Georeference->GetEllipsoidCenteredToUnrealWorldTransform();

  glm::dvec3 relativeLocation =
      this->_absoluteLocation - this->_worldOriginLocation;

  FMatrix tilesetActorToUeLocal =
      this->GetComponentToWorld().ToMatrixWithScale();

  glm::dmat4 ueAbsoluteToUeLocal = glm::dmat4(
      glm::dvec4(
          tilesetActorToUeLocal.M[0][0],
          tilesetActorToUeLocal.M[0][1],
          tilesetActorToUeLocal.M[0][2],
          tilesetActorToUeLocal.M[0][3]),
      glm::dvec4(
          tilesetActorToUeLocal.M[1][0],
          tilesetActorToUeLocal.M[1][1],
          tilesetActorToUeLocal.M[1][2],
          tilesetActorToUeLocal.M[1][3]),
      glm::dvec4(
          tilesetActorToUeLocal.M[2][0],
          tilesetActorToUeLocal.M[2][1],
          tilesetActorToUeLocal.M[2][2],
          tilesetActorToUeLocal.M[2][3]),
      glm::dvec4(relativeLocation, 1.0));

  
  this->_tilesetToUnrealRelativeWorld =
      ueAbsoluteToUeLocal * ellipsoidCenteredToUnrealWorld;

  this->_isDirty = true;
}

最后对GltfMeshComponentRelativeTransform,以保持场景的物体的相对位置关系。

// Called every frame
void ACesium3DTileset::Tick(float DeltaTime) {
  Super::Tick(DeltaTime);

  UCesium3DTilesetRoot* pRoot = Cast<UCesium3DTilesetRoot>(this->RootComponent);
  if (!pRoot) {
    return;
  }

  if (pRoot->IsTransformChanged()) {
      // 是否更新的相对位置
    this->UpdateTransformFromCesium(
        this->GetCesiumTilesetToUnrealRelativeWorldTransform());
    pRoot->MarkTransformUnchanged();
  }
  ///
 ......
 ///
}
void UCesiumGltfPrimitiveComponent::UpdateTransformFromCesium(
    const glm::dmat4& CesiumToUnrealTransform) {
  this->SetUsingAbsoluteLocation(true);
  this->SetUsingAbsoluteRotation(true);
  this->SetUsingAbsoluteScale(true);

  const glm::dmat4x4& transform =
      CesiumToUnrealTransform * this->HighPrecisionNodeTransform;
  /**
     void ACesium3DTileset::Tick(float DeltaTime){

       ...
       ...
       ...

           if (Gltf->GetAttachParent() == nullptr) 
           {
            Gltf->AttachToComponent(
                this->RootComponent,
                FAttachmentTransformRules::KeepRelativeTransform);
          }
       ...
       ...
       ...
     }
    
  */
  // 规则为相对位置
  this->SetRelativeTransform(FTransform(FMatrix(
      FVector(transform[0].x, transform[0].y, transform[0].z),
      FVector(transform[1].x, transform[1].y, transform[1].z),
      FVector(transform[2].x, transform[2].y, transform[2].z),
      FVector(transform[3].x, transform[3].y, transform[3].z))));
}

CesiumGeoreferenceComponent 的世界偏移

  1. 获取新的世界原点。
  2. 更新相对位置偏移,计算ECEF坐标到UE4相对坐标的变换矩阵,即重新将世界坐标计算映射新的世界原点中的相对坐标。
  3. 应用变换。
void UCesiumGeoreferenceComponent::ApplyWorldOffset(
    const FVector& InOffset,
    bool bWorldShift) {
  // USceneComponent::ApplyWorldOffset will call OnUpdateTransform, we want to
  // ignore it since we don't have to recompute everything on origin rebase.
  this->_ignoreOnUpdateTransform = true;
  USceneComponent::ApplyWorldOffset(InOffset, bWorldShift);

  const FIntVector& oldOrigin = this->GetWorld()->OriginLocation;
  this->_worldOriginLocation = glm::dvec3(
      static_cast<double>(oldOrigin.X) - static_cast<double>(InOffset.X),
      static_cast<double>(oldOrigin.Y) - static_cast<double>(InOffset.Y),
      static_cast<double>(oldOrigin.Z) - static_cast<double>(InOffset.Z));

  // Do _not_ call _updateAbsoluteLocation. The absolute position doesn't change
  // with an origin rebase, and we'll lose precision if we update the absolute
  // location here.

  this->_updateRelativeLocation();
  this->_updateActorToUnrealRelativeWorldTransform();
  if (this->FixTransformOnOriginRebase) {
    this->_setTransform(this->_actorToUnrealRelativeWorld);
  }
}
//计算ECEF坐标到UE4相对坐标的变换矩阵
void UCesiumGeoreferenceComponent::
    _updateActorToUnrealRelativeWorldTransform() {
  if (!this->Georeference) {
    return;
  }
  const glm::dmat4& ecefToUnrealWorld =
      this->Georeference->GetEllipsoidCenteredToUnrealWorldTransform();
  glm::dmat4 absoluteToRelativeWorld(
      glm::dvec4(1.0, 0.0, 0.0, 0.0),
      glm::dvec4(0.0, 1.0, 0.0, 0.0),
      glm::dvec4(0.0, 0.0, 1.0, 0.0),
      glm::dvec4(-this->_worldOriginLocation, 1.0));

  this->_actorToUnrealRelativeWorld =
      absoluteToRelativeWorld * ecefToUnrealWorld * this->_actorToECEF;
}
// 应用变换
void UCesiumGeoreferenceComponent::_setTransform(const glm::dmat4& transform) {
  if (!this->GetWorld()) {
    return;
  }

  // We are about to get an OnUpdateTransform callback for this, so we
  // preemptively mark down to ignore it.
  _ignoreOnUpdateTransform = true;

  this->_ownerRoot->SetWorldTransform(
      FTransform(FMatrix(
          FVector(transform[0].x, transform[0].y, transform[0].z),
          FVector(transform[1].x, transform[1].y, transform[1].z),
          FVector(transform[2].x, transform[2].y, transform[2].z),
          FVector(transform[3].x, transform[3].y, transform[3].z))),
      false,
      nullptr,
      TeleportWhenUpdatingTransform ? ETeleportType::TeleportPhysics
                                    : ETeleportType::None);
  // TODO: try direct setting of transformation, may work for static objects on
  // origin rebase
  /*
  this->_ownerRoot->SetRelativeLocation_Direct(
      FVector(transform[3].x, transform[3].y, transform[3].z));

  this->_ownerRoot->SetRelativeRotation_Direct(FMatrix(
    FVector(transform[0].x, transform[0].y, transform[0].z),
    FVector(transform[1].x, transform[1].y, transform[1].z),
    FVector(transform[2].x, transform[2].y, transform[2].z),
    FVector(0.0, 0.0, 0.0)
  ).Rotator());

  this->_ownerRoot->SetComponentToWorld(this->_ownerRoot->GetRelativeTransform());
  */
}
相关标签: UE4 UE4 cesium