maplibre-gl-js
maplibre-gl-js copied to clipboard
Flickering when zooming with terrain enabled
Hi all,
great work on the 3d feature, looks beautiful!
When testing your branch I noticed some flickering when zooming in (using the toursprung/terrain.json style) which is quite noticeable. It seems like semi-transparent polygons are rendered twice and for a few frames and the opacity is therefore doubled. Edit: It only seems to happen on the first zooming of an area, when all tiles are cached it doesn't happen.
Tile in the southeast with wrong opacity:
200ms later the tile is rendered correctly:
This doesn't happen with terrain disabled, I think it's related to some networking activity (new DEM or image tiles available).
Any idea where that may come from? Happy to help if you can point me in the right direction.
Regards, Florian
Original issue in prozessor13's repo: https://github.com/prozessor13/maplibre-gl-js/issues/60
I made some further tests. My initial suspicion was, it's because of the hillshade raster layer, but that's not the case. Confirmed this on another machine as well. Is this reproducible for anybody else?
Zooming in:
First artifacts appear:
Then some more:
When everything is loaded, everything is allright:
Any pointers appreciated!
Hi, i think the issue is only with raster-tiles, and in the terrain-testpage the hillshading is done via raster-tiles.
In 2d mode the raster-tiles are rendered on every frame, and between zoomleves and loading the rastertiles will fade into each other, but 3d works completele different. The vectortiles will render into a texture and this texture will draped over the terrain-mesh. During zooming maplibre resample existing rastertiles while loading the new tiles in background. But because of performance the rendert-to-texture is done only when tile-data changes, so in between this time you see the artefacts (e.g. when resampled and already loaded tiles are mixed). Maybe this logic can be optimized, but currently i have no time to focus on that.
hi, thanks for your reply. My first suspicion also were raster tiles, but take a look at the screenshots from yesterday, this also happens if the raster hillshade layer is switched off and no raster tiles are used.
so in between this time you see the artefacts (e.g. when resampled and already loaded tiles are mixed) Could you point me to the location in the source?
OK, you are right, i will have a look onto it!
After some more digging, I can reproduce the overlapping rendering now in a simple example with only the natural-wood layer. It seems, that in higher zoom levels there are overlapping tiles. Like here, it seems there is one parent 13 zoom tile, covered with two child tiles in 14, but not all four:
Ok thanks, i think i know now where to start!
I think the problem is when filling _coordsDescendingInv. May after the fill process (below line 56) we have to check that there are no parent-tiles in the to-render-tiles array.
You're right, there are loads of parents in the to-render array. That's clear, because some low zoom tiles (f.ex. 5) are always visible. But the problem really only shows on zooming in, until all requested tiles are loaded. A simple hacky fix reduces the flickering to almost not noticeable. I added this in the inner loop in _init
let parentPresent = false
for (const parentTileID of tileIDs) {
if (parentTileID != tileID && tileID.isChildOf(parentTileID) && (tileID.overscaledZ - parentTileID.overscaledZ) < 3) {
parentPresent = true
}
}
if (parentPresent) {
continue;
}
The tile isn't rendered, if there is a parent or grandparent tile visible. I don't get why the double rendering isn't notice all the time, because tiles are rendered on top of another all the time?
Thx for your effort! I shortly digged into this but currently i do not have a correct answer for the issue. As soon as i have time, i will look again onto this issue.
@prozessor13 I finally found the issue, took me a couple of hours. Based on your #1651 PR:
The problem is in the renderLayer methode in render_to_texture.ts. When a zoom event occurs and not all tiles are completely loaded, the coords array in the layer loop contains overlapping tiles. Link to source: https://github.com/prozessor13/maplibre-gl-js/blob/c82c13eac651bf96f23e44985bbf62326fc93ad2/src/render/render_to_texture.ts#L247
My suggested fix would be, remove the overlapping tiles for fill layers. Lower zoom levels should be always rendered first, because of ordering in coords array.
for (let l = 0; l < layers.length; l++) {
const layer = painter.style._layers[layers[l]];
let coords = layer.source ? this._coordsDescendingInv[layer.source][tile.tileID.key] : [tile.tileID];
if (coords && coords.length > 1 && layer.type == "fill") {
let parentPresent = false
let nonOverlappingCoords = []
for (const tileID of coords) {
for (const parentTileID of coords) {
if (parentTileID != tileID && tileID.isChildOf(parentTileID)) {
parentPresent = true
break;
}
}
if (!parentPresent) {
nonOverlappingCoords[nonOverlappingCoords.length] = tileID
}
}
coords = nonOverlappingCoords
}
painter._renderTileClippingMasks(layer, coords);
painter.renderLayer(painter, painter.style.sourceCaches[layer.source], layer, coords);
if (layer.source) tile.rttCoords[layer.source] = this._coordsDescendingInvStr[layer.source][tile.tileID.key];
}
What do you think?
Hi, thanks very much for digging into this. Your approach is one way, but i think the problem can be solved otherwise. In 2d Maplibre has a stencil logic to render on a pixel only the data of the tile with the highest zoomlevel. But currently in terrain-mode the stencil is not handled correct, and so this artefacts accours.
I played also with a solution you did, but run into problems when sources has no global coverage.
So i created another branch for this issue https://github.com/prozessor13/maplibre-gl-js/tree/rtt_stencil . Hopefully with this logic your problem dissapears as well, because currently i only tested with raster and hillshade layers. It would be great, if you can test this branch.
I tested your rtt_stencil branch an it works great! It also fixes another related issue where tile borders (overdraw) where overlapping when zooming out. Really fantastic!
@florianf I've assigned a bounty for adding tests to the #1672 in #1540. Feel free to add some test so the PR can be merged.
No need to assign a bounty here since it was assigned in #1540. Link to parent Bounty: https://github.com/maplibre/maplibre/issues/189