Globe.pick gives wrong results when the camera is tilted, and terrain is on

I'm using Cesium for quite a long time and recently I've encountered the following problem:

When I use the method in this guide-
http://analyticalgraphicsinc.github.io/cesium-google-earth-examples/examples/groundAlt.html
and the camera is tilted, such that the camera faces the horizon, sometimes the position and ground height reported by Globe.pick differs largely from the real position/height.

After some investigation it seems that the reason is the sort method of intersecting bounding spheres used in the pick method.
Since the sort function is based on the distance, tiles of low levels are sometimes prioritized over more detailed tiles, triangles from the wrong tile are selected and therefore the wrong result is returned.

So I've rewritten the code:

1)
Instead of pushing the tileData here
https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Source/Scene/Globe.js#L395
I'm pushing the tile itself(https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Source/Scene/Globe.js#L378) to the array,
So the new line is:
sphereIntersections.push(tile)

2)
My sort function:

function createComparePickTileFunction(rayOrigin){
    return function(a,b){
        // see http://stackoverflow.com/questions/4576714/sort-by-two-values-prioritizing-on-one-of-them
        var n = b.level-a.level;
        if (n!==0){
            return n;
        }
        var aDist = BoundingSphere.distanceSquaredTo(a.data.pickBoundingSphere, rayOrigin);
        var bDist = BoundingSphere.distanceSquaredTo(b.data.pickBoundingSphere, rayOrigin);
        
        return aDist - bDist;
    }
}

Note: we can't sort only by level because it's possible that two tiles along the ray would have the same level.

This gives more accurate results but after further reading I have some other questions, before I could rely on this solution.
I'm quite new at this, so forgive me if these are noobie questions.

A) The tile level is computed using the SSE function which in turn uses the distance to tile.
I have noticed that the camera height is used in the distance calculation
(https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Source/Scene/GlobeSurfaceTileProvider.js#L585)
which (correct me if I'm wrong) is (almost) accurate only if the camera is above the tile, but this is not the case with tilted views. So the distance might be not accurate when the tiles are on the horizon.
If I'm right - can I be sure that tiles on a ray would have the same or lower level as we move away from the origin?

B) The previous question can be asked again given the comment in


(page 37, or 373 in the full book)
"Technically, this equation [for SSE] is only accurate for objects in the center of
the viewport. For an object at the sides, it slightly underestimates the
true screen-space error."

So, given this comment- can I be sure that tiles on a ray would have the same or lower level as we move away from the origin?

C) I can imagine a situation when two (or more) triangles in a tile (with a front face in the opposite direction of the ray) would intersect the ray.
However, currently, only the first one is returned.
(https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Source/Scene/GlobeSurfaceTile.js#L224)
Is this the right behavior?

Please let me know if there is an already open issue for this case.
Thanks a lot...

Hi,

Thanks for looking into this!

re: 1 and 2… I agree, the existing sort function is not always going to be reliable. Just because the camera is close to a tile’s bounding sphere does not mean it is close to the tile itself. Lower-level tiles have much bigger bounding spheres than higher-level tiles. This could be the cause of bugs like this one:

https://github.com/AnalyticalGraphicsInc/cesium/issues/3037

Your function looks better, though I wonder why we don’t just use the tile.distance property instead of computing the distance to the bounding sphere? tile.distance is approximate as well, but it should be a better estimate than the distance to the bounding sphere.

re: A and B… the SSE computation does use the camera height, but only as one factor. It also considers the distance to four planes bounding the tile. I think the overall metric should be a reasonable (and cheap - that’s important too) approximation for the distance to the tile.

There’s no guarantee that tiles become less detailed as we move along a ray, though of course it’s generally true. In Cesium, the geometric error is assumed to be a constant for each level, so SSE is really just a function of tile level and distance. But distance is approximate, so I can imagine a pathological case where a tile that is “really” farther away than another tile is considered closer in the approximation. I think. I haven’t completely thought through it, because for rendering it doesn’t really matter.

For an early exit from picking it does, though. I guess we need decide how likely this is and whether accounting for it is worth the substantial extra cost of computing the intersection.

re: C… Yes, I agree, this seems wrong. We should be finding all the front-face intersections and returning the closest one, not just returning the first.

Kevin

Thanks Kevin for your replay.

I'm not fully understood the tile handling in cesium yet, so correct me if I'm wrong in the following:
After more thinking, and after playing with the great Cesium Inspector tool, I think my function is going to work only after all tiles are downloaded and processed.
So in case of camera movement, when some of the tiles are still belonging to the old camera position, my function would give wrong results.

So I will investigate it a bit more, using your replay...

About the distance- What I meant is that the height is above the ellipsoid, not above a plane, so it's accurate only when it above the tile (correct me if I'm wrong).

BTW, I suggest changing of the `distanceToTile` function name to `estimatedDistanceToTile` or something like this, or just put a comment about the estimation.

Thanks again.

Hi,

I have tried two more solutions:

  1. Using orientedBoundingBox (which is more accurate than boundingSphere) and sorting by distance to it.

I have modified the distance function slightly - just making some of the expensive calculations in the class constructor (only once).

This works fine for most of the cases but there are cases when a lower level tile is actually closer than others.

I guess this happens because of the way cesium handle tiles - you get higher level only if all of the four children meet the SSE requirements (correct me if I’m wrong).

I’m attaching a print screen. The red point is the position of the mouse cursor. The tile to the right (level 12) is closer than the tile with the red point (level 13), so it is prioritized over it, and the wrong triangle is returned.

  1. Getting all of the triangles (from intersecting tiles) that are intersecting with the ray and sort the intersections by squared distance.

This is not very efficient but it seems to work.

If you have any recommendations on how to do it more efficiently please let me know…

Thanks for your help…

BTW- Why don’t you use orientedBoundingBox as tile boundaries?