地形切换存在闪烁问题,如从平面地形切换到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._zeroTileReady是false,不释放瓦片,并且拷贝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;
浙公网安备 33010602011771号