地形切换存在闪烁问题,如从平面地形切换到3D地形球面会重新构建出现空球的闪烁问题,

如下如所示。

 

为什么在切换过程中会导致球面瓦片没有渲染?

GlobeSurfaceTileProvider的terrainProvider更改的时候,假设初始化球时,没有设置地形,那么默认的terrainProvider是EllipsoidTerrainProvider,即简单的平面地形。然后通过交互的方式添加地形,添加地形代码如下:

viewer.terrainLayers.addTerrainProvider(

            new Cesium.CesiumTerrainProvider({

              url: "../data/terrain/",

            })

          )

 

此时的terrainProvider的ready是false,在源代码中有一句注释

“Nothing to do until the provider is ready”

这就导致地形数据源发生变化的时候,有那么1-2秒的时间球是没有渲染的。因为根本就没有需要渲染的地形瓦片绘制指令。

 

如果想要优化此不友好的体验效果,应该如何优化?

解决方案:

在每一次释放所有瓦片的时候,先判断当前的terrainProvider的ready值是否为true,是则保持原来的执行流程;否就拷贝另存上一帧渲染的所有的瓦片。

然后在渲染的时候,同样判断terrainProvider的ready值是否为true,是则保持原来的执行流程;否就渲染拷贝的瓦片。

详细代码实现如下:

在QuadtreePrimitive.js文件中

第一步:在构造方法中添加如下四个属性

//上一帧0级瓦片

  this._levelZeroTilesOld = [];

//存储上一帧渲染的瓦片

  this._tilesToRenderOld = [];

  //是否释放所有备份瓦片

  this._freeAllBakTiles = false;

  //0级瓦片是否准备

  this._zeroTileReady = false;

 

第二步:在invalidateAllTiles方法中,如果primitive._zeroTileReadyfalse,不释放瓦片,并且拷贝primitive._tilesToRender到primitive._tilesToRenderOld中。

 

const levelZeroTiles = primitive._levelZeroTiles;

  if (defined(levelZeroTiles)) {

    if (primitive._zeroTileReady) {//added

      primitive._tilesToRenderOld.length = 0;//added

 

      for (let i = 0; i < levelZeroTiles.length; ++i) {

        const tile = levelZeroTiles[i];

        const customData = tile.customData;

        const customDataLength = customData.length;

 

        for (let j = 0; j < customDataLength; ++j) {

          const data = customData[j];

          data.level = 0;

          primitive._addHeightCallbacks.push(data);

        }

 

        levelZeroTiles[i].freeResources();

      }

    } else {//added

      primitive._tilesToRenderOld = primitive._tilesToRender.slice(0);

      primitive._levelZeroTilesOld = primitive._levelZeroTiles.slice(0);

 

    }

  }

 

 

 

第三步:在QuadtreePrimitive.prototype.invalidateAllTile方法中将_zeroTileReady设为false,每次更改terrainProvider会执行此方法。

QuadtreePrimitive.prototype.invalidateAllTiles = function () {

  this._tilesInvalidated = true;

  this._zeroTileReady = false;//geoway added

};

 

 

 

第四步:在QuadtreePrimitive.prototype.beginFrame方法中,释放_levelZeroTilesOld

 

if (this._freeAllBakTiles) {

    if (this._zeroTileReady) {

      this._tilesToRenderOld.length = 0;

      let levelZeroTiles = this._levelZeroTilesOld;

      for (let i = 0; i < levelZeroTiles.length; ++i) {

        let tile = levelZeroTiles[i];

        let customData = tile.customData;

        for (let j = 0; j < customData.length; ++j) {

          let data = customData[j];

          data.level = 0;

          this._addHeightCallbacks.push(data);

        }

        tile.freeResources();

      }

      this._levelZeroTilesOld.length = 0;

    }

    this._freeAllBakTiles = false;

  }

 

 

第五步,在方法中设置_lastSelectionFrameNumber之前,设置_zeroTileReady属性和_freeAllBakTiles属性。当上一帧渲染tile的长度大于0时且当前帧应该被渲染的tile长度不大于2时还是渲染上一帧瓦片。

if (!primitive._zeroTileReady) {

    if (primitive._tilesToRenderOld.length > 0) {

      if (tilesToRender.length > 2) {

        primitive._zeroTileReady = true;

        primitive._freeAllBakTiles = true;

      }

    } else {

      primitive._zeroTileReady = true

    }

  }

 

 

最后:在createRenderCommandsForSelectedTiles方法里,修改tilesToRender

变量的值,如果0级准备完成就渲染当前帧瓦片,否则就渲染上一帧瓦片。

const tilesToRender = primitive._zeroTileReady

    ? primitive._tilesToRender

    : primitive._tilesToRenderOld;