cesium-unreal
cesium-unreal copied to clipboard
Add dithered LOD transitions
Resolves #508
Here's a comparison without LOD dithering and with dithering (will turn these into gifs soon so they're viewable). Before: https://user-images.githubusercontent.com/6706316/174805513-acacd64e-1191-4306-b2a4-1d9ffccade4c.mp4
After: https://user-images.githubusercontent.com/6706316/174806988-1ff34e5f-9289-47bc-abdf-c05674344f0c.mp4
Thanks for the pull request @nithinp7!
- :heavy_check_mark: Signed CLA found.
Reviewers, don't forget to make sure that:
- [ ] Cesium for Unreal Samples works.
@kring I think this is ready for review alongside https://github.com/CesiumGS/cesium-native/pull/543! Small changes, but I think it makes the overall visuals of the plugin much smoother. Tried to pick a sensible default fading period (2 seconds seemed to work nice for me, but let me know what you think). This seems to works well with photogrammetry and CWT (although CWT shows slight Z-fighting when the transition period is too high).
Added dithering to the tileset materials so hopefully it should work right out of the box when opening a new / existing project.
This should be ready to review now as well!
Thanks @nithinp7! This will be a fantastic visual improvement to the plugin. I did a UX review to look at how to best present this to our users.
Given the current performance impact of the dithering, I recommend we have it off by default. This will help users with less powerful machines and internet speed. In the future if tile load speeds become more comparable, it would be great to have it on by default. A load speed of 1 second also seems to be a good balance for now. I'm also going to leave some comments about meta tags for the parameters.
I'm noticing an issue (at least on the Melbourne tileset) where some element of the dithering effect remains after the tileset has fully loaded in. It's more noticeable when LOD transition length is lower. Setting it to 0.5 results in "cobwebs" of lower LODs ghosting on the screen. This is inconsistent and seems different every time - on LOD 1 or higher it's rarely noticeable, but I am still seeing the issue.

I'm noticing something similar on CWT, and I can only assume it's the same issue. It looks like some previously-dithered LODs are not being unloaded completely. To visualize, here's a section of CWT in Melbourne (LOD transition length 0.2):
Here it is with the Shader Complexity and Quads optimization viewmode:

There's a LOT of overlapping mesh faces here. It seems to get much worse when you're not looking - in this example, it seemed like every time I turned around a new layer would appear.
Thanks for reviewing and catching the bug @argallegos! I found the fix for it, it was a small mistake I made in Cesium Native. Try it again if you get a chance, I think both the invisible extra tiles as well as the buggy water mask should both be fixed now.
As for performance, I don't think the PR has a clearly bad performance impact after the latest fix. Regarding the "disabling" of load-culling: GPU memory cost and the network request count is unchanged, the only change is the order in which tiles are loaded. Previously lower priority tiles (offscreen tiles) now end up getting loaded before the parent tile refines. This causes detail on the screen to come in a bit slower, especially when first loading in (this can of course be offset in user apps by having a loading screen tied to the tileset loaded delegate).
Regarding draw calls, it's probably worth taking another look at the stats with / without lod transitions now that the bug is fixed. The increase in draw calls during transitions is somewhat inevitable to the concept of lod blending, but it will be much worse of an impact when you set the transition length to be too long (e.g., 4 seconds like you tried). So for performance measurement I would stick to 1 second or less for the transition length, that's usually the reasonable range.
Thanks @nithinp7! I've tested out the updated version.
I compared draw calls and the time taken to fully refine in a few different scenarios with different tilesets. Both are improved and my machine seems happier.
That being said, my main hesitation about having lod transitions on by default isn't the actual performance, but how quickly the tileset appears to load for users. In some of my tests, I noticed that turning on lod transitions increased the overall time until the tiles in view were fully refined by several seconds or more - and I have a good machine that's directly wired to the router. Even if the performance benchmarks are the same, the user doesn't necessarily know this and their overall impression could be that the plugin is too slow for their purposes, just based on how long it seems to take for their view to fully load.
I think the actual culprit here is the LOD transition length and not the frustrum culling value. Changing the transition length always makes the most impact on how long a tileset takes to fully load, which is kind of a given, but maybe there's a way to optimize here? I did not look too closely at the code, but it looks like it will always take the full n second for each level of detail before moving on to the next. Could that be the bottleneck, and if so, are there any options for a more dynamic solution where the transition speed is increased if the next tile is ready, or multiple levels can load in at the same time, or LOD levels can be skipped? I don't know what's possible to implement here.
Also, now that everything is working properly I suggest a 0.5 default transition length - it looks a little choppier than 1 second, but improves load times.
Overall, this is great, and it gets my review approval. But if we want it to be the default setting for tilesets, we should see if there's anything that can be done to reduce time until the tileset is fully loaded.
Thanks for the detailed review @argallegos! I agree with your comments. I defaulted the LOD transitions to off, per offline discussion.
Per my current understanding, enabling the option has an impact on latency, perhaps a bit beyond the expected latency when disabling culling, even. I set the default transition length to 0.5s, which seems to be quite responsive while still keeping a bit of transition smoothness. It is definitely a bit choppier than 1s, but still a world better than no transition at all.
That's also a good point on the waiting for successive LODs, currently a tile fade needs to be finished before it can be refined and faded out. However, once the fade is complete LODs get skipped and the target LOD should be faded into directly if it is loaded at that point. I tried allowing multiple tiles to be fading at once, so there was no sequential dependency whatsoever. It actually looked super nice and had low latency, but unfortunately it caused spikes in draw calls that were especially bad in dense city scenes. We might want to revisit the options here, like you said maybe we could explore varying the transition speed or doing instant transitions depending on the LOD gap we are jumping between. We might even want to have a way to abort a transition and change the fade-in target.
For now, I think this is a pretty decent improvement over no transition with no huge drawbacks. We can revisit the transition smoothness vs latency tradeoff in the future when we get a chance.
@kring Bao and Alex both gave this a review, can you have one last test and merge it if you feel it's ready for the release?
This looks good to me! Merging!
@nithinp7 @kring It looks like we have some promising starting points for potential improvements to the LOD transitions. An issue has been created for further discussion and future improvements - please add to it if you have any more ideas. https://github.com/CesiumGS/cesium-unreal/issues/961. I'd love to see this get to the point that we can have it on by default!