Cesium: Investigate image quality degradation

Created on 3 Dec 2015  路  18Comments  路  Source: CesiumGS/cesium

Reported on the forum

Check out the below image. You'll notice the Cesium version is washed out and slightly blurry compared to the same image in Bing Maps (click it for full resolution). At some point in the rendering pipeline (texture filtering, AA, lighting, etc..) Cesium processing is reducing imagery quality.

I looked at the source files in the network debugger and confirmed that both engines are using the same exact imagery and the rendered Bing map version is identical to the source (since it's just doing img elements like traditional webmaps). This means that Cesium is definitely the culprit here.

image

category - terrain and imagery priority - high type - bug

Most helpful comment

I encountered this in #4304 and I later realized it's a general problem and not only GoogleEarthImageryProvider's issue (It looks much worse with Geographic Projection-'flat' in Google terminology- than with Mercator projection).

When I compare the same DB in Cesium and in Google Earth- the GE version looks much better.

In Cesium (with gamma fixed to 1.0):
cesium_sf_gamma1 0_ratio_1 0_fxaa_false

In GE (latest version):

ge_plugin_sf

At least in my case, the main reasons for this behavior are points 3 and 4 in @kring comment:

Cesium's default maximum SSE of 2 means that image texels map to between 1 and 2 screen pixels, so there's actually less detail on the screen compared to a 2D map where the SSE is effectively always exactly 1.

When I change the pixel ratio to, say, 0.5 (one level above), the image looks pretty much like in Google earth, and this is preserved while zooming.

After:
cesium_sf_gamma1 0_ratio_0 5_fxaa_false

I believe that this is exactly what Google Earth does, too. I found this when I implemented KML Region for cesium, by sniffing the network output and checking the tile's rectangle dimensions when the appropriate request is sent.
Here is an image of the tile's rectangle when its request was sent- you can see that it's about 128 pixels at the bigger edge.

tiles_rectangle_when_request_sent_ge

(you can read about GE tiling scheme and how to compute tile's rectangle here).

This has some impact on performances, impact that directly related to the ratio value, because more textures are rendered. But for my case this is the best solution and the impact is reasonable.

Of course, this is good only for Imagery, not for rasterized vector, because labels and other vector data would look much smaller than they have to. So we have to let the user choose if to apply such effect.

I think the best way to achieve this is to implement the errorRatio that's mentioned in this comment in the code,:

// The errorRatio should really be imagerySSE / terrainSSE rather than this hard-coded value.
// But first we need configurable imagery SSE and we need the rendering to be able to handle more
// images attached to a terrain tile than there are available texture units. So that's for the future.

From reading GlobeSurfaceTileProvider, it seems that handling more images than available texture units is supported now (correct me if I'm wrong), so only configurable imagerySSE per layer is needed.

I think that for most of the users, ImagerySSE says nothing - so I thought of 'pixelRatio' option in ImageryProvider constructing options (default to 1.0, lower value gives better quality).
@pjcozzi, @kring , @mramato , @hpinkos, @denverpierce, Does it seem to you? Any other suggestions?

All 18 comments

Just a hunch: see if disabling anisotropic filtering helps this view. I doubt it.

@kring any chance you want to look at this?

Just a hunch: see if disabling anisotropic filtering helps this view. I doubt it.

Setting allowTextureFilterAnisotropic to false had no discernible effect.

I have looked at this, because it's very obvious in an app like ours where you can toggle between Cesium and Leaflet. Here's what I know:

  1. Cesium applies a gamma of 1.3 to Bing Maps satellite layers. It probably made sense at one point, but IMO it doesn't anymore and we've disabled it in TerriaJS. Perhaps it should be disabled in Cesium in general. That's the cause of most of the "washed out" effect.
  2. Cesium reprojects imagery from Web Mercator to Geographic, and _then_ it unprojects it onto the globe. This extra step probably introduces some blurring compared to simply unprojecting the Web Mercator maps directly. But to do that, we need Web Mercator texture coordinates, and it gets hairy for tiles near the poles.
  3. 2D maps like Bing only allow fixed zoom levels, so image texels always map 1:1 to screen pixels. Cesium allows continuous zoom, which necessitates resampling, which is inevitably going to introduce some blurring.
  4. Cesium's default maximum SSE of 2 means that image texels map to between 1 and 2 screen pixels, so there's actually less detail on the screen compared to a 2D map where the SSE is effectively always exactly 1.

For (1), this was because the imagery was so dark. Is this no longer a problem? Or the lesser of two evils?

Fxaa hits tiled layers pretty hard as well, especially labels rendered onto tiles and road/stylized tiles. Mildly related to #2752.

Also reported here: https://groups.google.com/forum/?hl=en#!topic/cesium-dev/QTSlECbZmG8

I encountered this in #4304 and I later realized it's a general problem and not only GoogleEarthImageryProvider's issue (It looks much worse with Geographic Projection-'flat' in Google terminology- than with Mercator projection).

When I compare the same DB in Cesium and in Google Earth- the GE version looks much better.

In Cesium (with gamma fixed to 1.0):
cesium_sf_gamma1 0_ratio_1 0_fxaa_false

In GE (latest version):

ge_plugin_sf

At least in my case, the main reasons for this behavior are points 3 and 4 in @kring comment:

Cesium's default maximum SSE of 2 means that image texels map to between 1 and 2 screen pixels, so there's actually less detail on the screen compared to a 2D map where the SSE is effectively always exactly 1.

When I change the pixel ratio to, say, 0.5 (one level above), the image looks pretty much like in Google earth, and this is preserved while zooming.

After:
cesium_sf_gamma1 0_ratio_0 5_fxaa_false

I believe that this is exactly what Google Earth does, too. I found this when I implemented KML Region for cesium, by sniffing the network output and checking the tile's rectangle dimensions when the appropriate request is sent.
Here is an image of the tile's rectangle when its request was sent- you can see that it's about 128 pixels at the bigger edge.

tiles_rectangle_when_request_sent_ge

(you can read about GE tiling scheme and how to compute tile's rectangle here).

This has some impact on performances, impact that directly related to the ratio value, because more textures are rendered. But for my case this is the best solution and the impact is reasonable.

Of course, this is good only for Imagery, not for rasterized vector, because labels and other vector data would look much smaller than they have to. So we have to let the user choose if to apply such effect.

I think the best way to achieve this is to implement the errorRatio that's mentioned in this comment in the code,:

// The errorRatio should really be imagerySSE / terrainSSE rather than this hard-coded value.
// But first we need configurable imagery SSE and we need the rendering to be able to handle more
// images attached to a terrain tile than there are available texture units. So that's for the future.

From reading GlobeSurfaceTileProvider, it seems that handling more images than available texture units is supported now (correct me if I'm wrong), so only configurable imagerySSE per layer is needed.

I think that for most of the users, ImagerySSE says nothing - so I thought of 'pixelRatio' option in ImageryProvider constructing options (default to 1.0, lower value gives better quality).
@pjcozzi, @kring , @mramato , @hpinkos, @denverpierce, Does it seem to you? Any other suggestions?

@duvifn your analysis all sounds correct for me.

One thing I'd suggest is that you use a maximumScreenSpaceError of 1.33 instead of 0.5. That will make between 0.66 and 1.33 imagery texels map to a single pixel on the screen, with a slight tendency toward the smaller value because the estimate of the distance to the tile is conservative.

I'd definitely support adding a separate max SSE value for imagery. You're right that Cesium can now support any number of imagery tiles attached to a terrain tile, so the major roadblock in that comment no longer applies. I think a property called maximumScreenSpaceError on ImageryLayer would be appropriate. pixelRatio sounds less technical, perhaps, but it's less precise and also less consistent with the existing maximumScreenSpaceError property on Globe.

Ideally selection of imagery level would be decoupled from selection of terrain level. Currently we compute the appropriate imagery level for a given terrain tile and use that level for all imagery in that tile from then on. But that ignores the fact that some imagery tiles are closer to the viewer than others. It would be nice to render a high imagery level for the part of a terrain tile close to the viewer, and a lower imagery level for the parts farther away. This is a pretty big change, though.

Thanks @kring!

One thing I'd suggest is that you use a maximumScreenSpaceError of 1.33

Great suggestion.0.66 - 1.33 is indeed more efficient than 0.5-1.0 while it preserves a reasonable image quality.

I think a property called maximumScreenSpaceError on ImageryLayer

OK.

Ideally selection of imagery level would be decoupled from selection of terrain level.

You're right but as you wrote this is a big change and I prefer not to get into it currently.

Also reported by @giantss in #7644

After reading the above description, this problem seems to have not been resolved yet?
I am using the last version of cesium

Any ETA on the maximumScreenSpaceError property for ImageryProviders?

@laurensdijkstra I don't know that this will be a priority for us in the near future, but if you have time to submit a pull request we would be happy to review it! Thanks =)

I'm not sure this issue is worth keeping around. The original issue is not what most of the conversation in this thread is about and FXAA support has been rewritten since the initial issue.

There are two different things that were discussed.

First, if you are concerned that you are not getting enough detail in the tiles be loaded (i.e. you expect a higher LOD and Cesium is loading another one, this is do to the maximum screen space error setting, which defaults to 2.0. You can instruct Cesium to load more detailed LODs sooner by lowering this value, the recommended level would be 1.5. I'm going to open a separate issue to discuss making this a new default since Cesium's terrain and imagery rendering has improved greatly since this issue was written.

viewer.scene.globe.maximumScreenSpaceError = 1.5;

The second issue is fxaa being applied to imagery layers which causes "fuzziness" in imagery, especially imagery with crisp labels. This can be fixed by disabling FXAA:

viewer.scene.postProcessStages.fxaa.enabled = false;

In the long term, we want to make sure FXAA does not get applied to imagery and some other object types (labels) so I'll find or write up a specific issue for that.

If anyone still has questions or a different problem, please let us know; but I think keeping this issue open as-is a point of confusion.

Congratulations on closing the issue! I found these Cesium forum links in the comments above:

https://groups.google.com/forum/#!topic/cesium-dev/xWcXPYkh3c8
https://groups.google.com/d/msg/cesium-dev/6iH8YLXLEgQ/XvWuxTdFCQAJ
https://groups.google.com/forum/?hl=en#!topic/cesium-dev/QTSlECbZmG8

If this issue affects any of these threads, please post a comment like the following:

The issue at https://github.com/AnalyticalGraphicsInc/cesium/issues/3279 has just been closed and may resolve your issue. Look for the change in the next stable release of Cesium or get it now in the master branch on GitHub https://github.com/AnalyticalGraphicsInc/cesium.

Wrote up #7714 and #7715 so that we have clear actionable issues for the items discussed here

viewer.scene.globe.maximumScreenSpaceError = 1.5; does the trick for us, thanks

Was this page helpful?
0 / 5 - 0 ratings

Related issues

OmarShehata picture OmarShehata  路  4Comments

JacksonBates picture JacksonBates  路  3Comments

nikakhov picture nikakhov  路  4Comments

hpinkos picture hpinkos  路  4Comments

pjcozzi picture pjcozzi  路  4Comments