CesiumGS / 3d-tiles

Specification for streaming massive heterogeneous 3D geospatial datasets :earth_americas:
2.14k stars 469 forks source link

Clarifying question on 3D Tiles Spec REPLACE and Cesium's Implementation #776

Closed gkjohnson closed 2 months ago

gkjohnson commented 2 months ago

Hello! I wanted to clarify some intended behavior around the REPLACE refinement and what the spec implies vs what Cesium implements. For context this is a question related to the three.js 3D Tiles Renderer project.

From the spec:

If the tile has replacement refinement, the children tiles are rendered in place of the parent, that is, the parent tile is no longer rendered.

Perhaps there are some more eccentric uses but to me this implies that the replacement refinement is used for improving the detail of the parent tile (ie reducing the error) while representing the same objects - useful for things like terrain vs using ADD refinement for adding buildings or trees on top of an existing terrain.

To that end for visual consistency as the camera moves it makes sense to only replace the parent with the child tiles once all the children have loaded so we don't get popping or tile gaps when shifting the camera. This is what the NASA AMMOS project does and Cesium is doing, as well.

Cesium, however, also implements additional logic to only wait (and trigger loads) for child tiles only if that tile has content - effectively causing it to work like ADD refinement only when tiles are empty. In practice this only effects empty tiles near the root before there are content tiles. My impression is that this means you could wind up at the boundary of one of these closest-to-root content tiles and not have even the highest level tile loaded (ie at the root octant boundary of one of the Google earth tiles). Depending on the precision of the frustum bounds checks this may or may not be apparent when moving the camera but it's a possibility and something I've run in to. This is what I'm seeing in when replicating the behavior from Cesium in my project, at least. Perhaps there's more that I'm missing.

Confusingly, Cesium also gates this behavior behind an undocumented (or perhaps incorrectly documented) loadSiblings setting that defaults to false so the above issue will be present by default.

The Questions

Given what I've described above the reasonable default behavior should be that of loadSiblings = true so we can avoid cases where the camera shifts and there's a flash of no tile. This was originally the behavior in the NASA-AMMOS project. I'm asking because a tile set has been provided by a user that is effectively a flat hierarchy of tens of thousands of buildings with 0 geometric error and "REPLACE" used everywhere. With the loadSiblings === true this will cause thousands and thousands of tiles to load rather than just those in the frustum if ADD were used appropriately. However due to Cesium's implementation quirk this tile set seems to load just fine.

For reference the problem and tile set are linked and described in this issue: https://github.com/iTowns/itowns/issues/2335#issue-2348255580. When loading the tile set in cesium with loadSiblings = false it will take forever to load - and in fact likely will not load due to cache limits.

I'm generally curious as to the thoughts on the undocumented loadSiblings setting and the above tile set. My inclination is to say that this is a poorly formed tile set and should be regenerated or at least adjusted since the loadSiblings === false behavior isn't necessarily implied by the spec or a requirement and is more of an implementation detail by Cesium (which is not noted as an optimization and may cause unintended artifacts). I'm trying to avoid adding new settings and flags for cases like this as much as possible to avoid maintenance burden and requiring users to map specific settings to tile sets just to avoid load error and artifacts.

Thanks as always! And sorry for the long post.

lilleyse commented 2 months ago

I think it depends on your tolerance for holes. If the goal is to forbid holes entirely, then yes, all the children, even those offscreen, would need to be loaded before the parent refines. If you allow for holes, then this sort of optimization seems valid. The main condition is that parent and children tiles aren't rendered simultaneously.

Probably what CesiumJS should do is disable this optimization unless skipLevelOfDetail is true.

Also, I agree that the root tile in this tileset should be ADD instead of REPLACE.

gkjohnson commented 2 months ago

Thanks - I agree that these things are more stylistic choices since 3d tiles spec doesn't prescribe a rendering approach. I mostly wanted to make sure that this somehow wasn't required behavior during traversal so a wider range of tile sets could be loaded if the one above was considered well-formed.

Probably what CesiumJS should do is disable this optimization unless skipLevelOfDetail is true.

In the interest of providing a more pure representation of tiles rendering traversal it does seem like this load siblings behavior should default to "true" in this case.

Also, I agree that the root tile in this tileset should be ADD instead of REPLACE.

Great, I'll note this in the other issue and remove the behavior from the NASA-AMMOS project.

Thank you again!