Cesium for UE4 解决抖动的问题

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)