We only see this on Android not on Windows, is this some sort of floating point precision problem, or something else? This is on Google Earth 3D tiles, we see this everywhere, this example just happens to be mostly green. What can we do to fix this?
Yes, this does appear to be a precision problem. We could probably confirm so – do the cracks disappear when you move closer to the Earth?
Unfortunately we’ve encountered this ourselves when viewing the Earth from high altitude. It looks like the skirts of some tiles are being drawn over tiles in front of them. But we’re not sure why that would be happening. Perhaps it could be mitigated with origin shifting?
@janine@Kevin_Ring we’re using CesiumOriginShift component which makes the pawn always stay at 0,0,0 and rotates earth when moving. And we can confirm this doesn’t happen in Windows. Were it an OS problem, we’d have seen it there too.
It seems like code where you’re stitching tiles together might have an issue over Android. We’d like to debug this. Can you guide us as to where in your code yuo stitch tiles together? That way we can do some more tests and provide additional information
We actually don’t stitch tiles together at all. We just render each tile with the appropriate transformation defined by the tileset.
Those artifacts look like a depth buffer precision problem to me. The tile skirts are being drawn over the top of the main part of the tile, even though the skirts should be behind it. What I don’t know is why that would happen. It looks like Unreal has a setting to control the precision of the depth buffer on Android, so perhaps it’s set too low by default. Try changing that first and see if it helps:
It’s also worth trying both OpenGL ES3.2 and Vulkan rendering, to see if either one is better than the other in this regard. There are two checkboxes for this, found in Project Settings → Android → Build.
So we tried with Android Depth Buffer, with 32-bit, but this one did not fix anything, i think it improved but not a lot. I found this post where it says.
Your Z resolution is going to be divided between your near clip plane (Project settings->General settings->Settings->Near Clip Plane) and your far clip plane.
So when i increase the near clip plane, it fixes the issues with skirt, but as you increase the near clip plane, you will not see your hands or objects around you, which is an issue. We are going to try to see if we can exclude our hands and certain objects from the clipping planes, eg : make hands and body mesh render all the time. but as you go high on earth you may want to increase the Near Clip plane to rectify the issue. but by increasing the Near Clip plane it increase the Z resolution. i wonder if you can do anything about this internally.
A 32-bit depth buffer is pretty good, and Unreal should be using it “reversed”, which helps a lot with precision. One thing that comes to mind is that reversing is not usually possible in OpenGL (at least not without an extension), so do make sure you’re using Vulkan. There’s a chance the combination of the 32-bit depth buffer and the Vulkan renderer will do the trick, even if neither one alone helps.
As a really hand-wavy rule of thumb, I wouldn’t expect a “reasonable” near plane of, let’s say, 10cm or so to cause major problems like you’re seeing with a 32-bit, floating point, reversed depth buffer. But if it’s drastically smaller than that, e.g., because the hands or their bounding volume are really close, precision drops off quickly.
Your idea to exclude the hands from the clipping planes (by rendering them in a separate pass) is a good one. I have to imagine this is a common problem in AR applications in Unreal. It could be worth taking the question to Epic or a more general Unreal forum.
We’ve been experimenting with 32-bit depth and Vulkan, but we’re still encountering the same issue. Could you please confirm if this setup works for you?
We’ve identified that the function void FVulkanDepthStencilState::SetupCreateInfo(const FGraphicsPipelineStateInitializer& GfxPSOInit, VkPipelineDepthStencilStateCreateInfo& OutDepthStencilState) is responsible for configuring the DepthTest method, and it appears that this might be reversed. We’re uncertain about the next steps.
Would reducing the mesh size (using many smaller tiles) be a viable solution? We suspect the problem may be related to tile skirts being visible through the mesh. Does this issue occur only with larger tiles?
What other approaches would you recommend?
Additionally, we found this resource: Cesium VR Movement Guide. How are you achieving similar results?
We’re continuing to work on this issue and would greatly appreciate your input on the questions above. Here’s a quick update from our side:
We’ve conducted a test where we generated tiles similarly to how Cesium does, but instead of generating them in real time, we created static meshes. Each static mesh was positioned to connect seamlessly with the next, recreating the same scenario. We also applied our own shader to match Cesium’s world shader for consistency.
Interestingly, we only see the issue with the shader in Cesium, whereas the static mesh test doesn’t exhibit the same problem. Assuming our test is valid, this could suggest that there might be something in the mesh generation process contributing to the issue.
What do you think? Are there any additional tests or troubleshooting steps we could take to further investigate and resolve this? Would there be additional tests or adjustments on your end that might help clarify the cause?
@Kevin_Ring one more question, what exactly are the skirts for? We are wondering what would happen if we removed the skirts when we are farther away, and where is the code that generates the skirts?
@janine is there any way to ping @Kevin_Ring ? Been waiting for a week on the above, maybe he’s on PTO? Thanks so much! We’re making some progress but could really use the help. Much appreciated.
We’ve conducted a test where we generated tiles similarly to how Cesium does, but instead of generating them in real time, we created static meshes. Each static mesh was positioned to connect seamlessly with the next, recreating the same scenario. We also applied our own shader to match Cesium’s world shader for consistency.
Interestingly, we only see the issue with the shader in Cesium, whereas the static mesh test doesn’t exhibit the same problem. Assuming our test is valid, this could suggest that there might be something in the mesh generation process contributing to the issue.
That’s surprising to hear. I don’t know what we could be doing that would cause different depth precision in 3D Tiles.
Do you mind sharing a screenshot of these meshes and their appearance in VR? It would also help to know the way in which you generated the vertex data.
what exactly are the skirts for? We are wondering what would happen if we removed the skirts when we are farther away, and where is the code that generates the skirts?
Interestingly, skirts are generated because you’d otherwise see very thin lines between each tile. The skirts are extruded just enough to fill that space below the tile to hide those lines. It is unfortunate that they’re having the opposite effect in VR…
The skirts are being added to quantized-mesh tiles in cesium-native.
There is currently no setting in Cesium for Unreal to disable skirts, but you’re welcome to modify the source code to implement the skirt-disabling behavior. In particular I wonder if you could use the SkirtMeshMetadata to identify which vertices belong to the skirt, and pass per-vertex flags into an Unreal material, to hide them when they’re at a certain distance.
I hope this is helpful Let us know if we can help in any other way.
Forgot to mention the other place where skirts are being added in cesium-native:
It might be heavy-handed to modify the cesium-native code itself. I’m not super familiar with this process but I think you could use the SkirtMeshMetadata to derive which vertices actually belong to the skirts. Then, you could do something with it in the CesiumGltfComponent.cpp in Cesium for Unreal, where the meshes are actually being created.
We’ve delved into the relevant Cesium native code and added logs across all the functions. Through testing, we discovered that when using Google Earth 3D URLs, none of these functions are being called. This suggests that the skirt data may already be embedded in the Google Earth 3D dataset. Interestingly, these functions are triggered when using other data sources, such as Cesium Ion or RasterOverlaysComponent.
Given that Cesium receives data for each tile to generate the geometry, we’re now focused on understanding how the skirt geometry is derived from this data. Our next step is to create a function that selectively removes the skirts from the dataset.
Additionally, we’ve conducted a couple of tests regarding depth precision on Android, including:
Experimenting with 32-bit depth and Vulkan, but we’re still encountering the same issue. Could you confirm if this setup works for you?
We’ve identified that the function void FVulkanDepthStencilState::SetupCreateInfo(const FGraphicsPipelineStateInitializer& GfxPSOInit, VkPipelineDepthStencilStateCreateInfo& OutDepthStencilState) configures the DepthTest method, and it seems this might be reversed. We’re uncertain about the next steps.
Would reducing the mesh size by using smaller tiles be a viable solution? We suspect the issue may be related to tile skirts becoming visible through the mesh, and that this only happens with larger tiles. What are your thoughts on this?
Finally, could you confirm if the Google dataset inherently includes skirt data, and if so, provide guidance on how we might remove or ignore this skirt data programmatically?
After downloading one of the Google 3D Tiles, we noticed that each tile contains its own skirts. This means that the code provided earlier doesn’t address our issue, as we need a way to either remove the skirts or identify the skirt faces and assign a vertex color to them, allowing us to handle the skirts via shaders.
Could you provide guidance on the best place within the code to traverse the mesh data and identify the skirts? We’re looking for an efficient way to pinpoint the skirt faces for further processing.
Sorry for the delay in response, it was our weekend.
I apologize for the misdirection, I hadn’t recognized that you were using Google Photorealistic 3D Tiles. Unfortunately I don’t know how they generated their tiles, so I don’t know if the skirts are identifiable within the glTF.
When you’re viewing the Earth in VR, are you viewing it from a far distance the entire time? Or do you zoom into the Earth at some point? One thing I could suggest is reducing the scale of the Earth using the CesiumGeoreference. The idea is that the reduced scale will therefore reduce the floating point error when you get farther away from the Earth. However, depending on how you’ve implemented movement around the Earth, you may have to incorporate this scale into the rest of your code.
Let me know if that results in any visual improvements for you!
@janine thanks but since we’re multiplayer this won’t work with making earth smaller. Can we please loop @Kevin_Ring back? We’ve been stuck on this for 14 days, we have made a ton of progress but we think this is technical at the point that Kevin might be able to help out.
Kevin we’re trying to differentiate between the skirt and a face of a tile. Our approach was to create an average of the normal and using that normal to decide which vertex is facing in the normal direction, so it’s considered face tile, or if not, a skirt. However, so far this works only 50% of the time, we’re not sure why. We’d need a way to debug this.
We feel like we’re getting closer but at the same time we can’t really ship with all these skirt artifacts. Since you guys know this is an issue for you too, can we put some focus on this to try to resolve together?
@carlrealvr there is unfortunately no reliable way to distinguish the skirts from the rest of the geometry in Google Photorealistic 3D Tiles. I don’t think eliminating the skirts is going to be a very viable option, anyway. Even if you could do it, the depth testing problems are not limited to the skirts. The problem is just more visible on the skirts because the textures are (intentionally) smeared across them.
I played around with this, and the good news is that I can reproduce it on my Pixel 6 Pro as well. The bad news is that I wasn’t able to find any workaround, other than increasing the near plane distance. From what I can tell, Unreal Engine uses a totally different - and less precise - depth testing system on Android compared to Windows and presumably other desktop platforms. Perhaps there are hardware limitations at play as well, I’m not sure. I played around with a bunch of settings, and wasn’t able to find one that helps.
So I think increasing the near plane distance is the right approach here, but I know you said you had trouble with the rendering of the hands. Did you explore any options for moving them to a separate render pass, so that they’re not depth tested with the rest of the scene? I don’t know the mechanics of how that would be done, but it seems like the best approach here. A more general Unreal Engine forum might be more help for this aspect of it.
@Kevin_Ring thanks - we finally figured out a shader that removes the skirts. That in turn revealed a bunch of gaps (which slikely why the skirts are there in the first place) and we’re filling the gaps with another static sphere of Earth below. Seems to do the trick for now. However… we’re still seeing other issues related to loading of the tiles: When we fly high and look down to earth, two tiles aren’t loading the same LODs for some reason and it’s not distance related (ie, could happen right below or at a distance). It doesn’t even try to refine it unless you rotate head to unload and reload and then sometimes it gets unstuck. Notice the stark lines with zigzags that occur at the seam of tiles of different LODs, it’s really bad… How can we fix allt this? Also is there perhaps a way that as you get distant from Earth all the tiles are in the same LOD/quality ie to stop unloading the tiles?
Hi @Kevin_Ring we’re hoping you got to see the above, there’s some breakthrough but another related issue has come up. Hoping you might see it before the weekend as we wrap this project up. Thanks tremendously for your help! @janine can you please help ping Kevin before day’s end in Australia? It’s the weekend and we’re shipping after many weeks of wait. Thanks for your help