scier / MetalSplatter

Render Gaussian Splats using Metal on Apple platforms (iOS/iPhone/iPad, macOS, and visionOS)
MIT License
239 stars 27 forks source link

Weird wobbling artifacts in immersive mode on Vision Pro #12

Closed JiamingSuen closed 3 months ago

JiamingSuen commented 5 months ago

I noticed this strange wobbling artifact (especially on the depth edges) on Vision Pro. The same issue can be reproduced with a smaller splat file (34K points) and the closed-sourced version of the app on the App Store. I'm on visionOS v1.1.2.

https://github.com/scier/MetalSplatter/assets/8616975/16529d0e-abd7-4267-8964-98234464f43e

https://github.com/scier/MetalSplatter/assets/8616975/d5532caa-a25b-48c8-b540-b7241a3867fd

Hope this can be easily fixed. Thanks for this amazing repo!

scier commented 4 months ago

What you're seeing -- which I've dubbed WobbleSplat, trademark pending -- is a combination of two things. First, the Vision Pro extrapolates missed frames by reprojecting previous frames, using those frames' depth to warp the textures. VP needs to render at 90fps, so if the splat is rendering at anything less than that, it'll miss a frame which is super-ugly and disorienting in VR, so the VP creates a new one to match the user's latest pose. Reprojection isn't perfect -- especially around depth discontinuities, i.e. edges of foreground objects -- but it depends on having good depth to work at all.

Which brings us to the second issue: rendering depth is hard with splats (because of the transparency and layers involved). For instance, if there's a large, nearly-invisible splat, the depth buffer will still show that "ghost bubble" as the closest object for those pixels, and so the reprojection will happily warp the pixels as though they're closer to you than they actually are, and you'll see a warped bubble.

The faster you move, the bigger (i.e. slower to render) the splat model, and the more noisy large translucent splats there are in the model, the more this will all stand out.

If you want to see what things look like with no reprojection at all -- well, that's hard, it's automatic. But if you want to turn it off "halfway", you can turn off depth: in SplatRenderer, set depthStateDescriptor.isDepthWriteEnabled = false. Then the depth buffer will contain entirely 1.0. The reprojection will then treat everything as though it's really far away. Which means it does just fine when you just turn your head in place, but if you move side-to-side, it can't compensate, so foreground objects jitter a lot and seem to "lag" (since they can't be moved to the "right" place).

There are a few ways to fix this, none easy (sorry):

JiamingSuen commented 4 months ago

Thanks for your detailed reply! Now I believe I've fully understood the problem. However, I didn't find the same issue with the WebXR mode in https://github.com/mkkellogg/GaussianSplats3D (although it could be very laggy with large splat files). I'll try to dig deeper to figure out why.

jerbeers commented 4 months ago

I don't know if this is the same thing, but it looks like the interpolated frames are tracking with head movement too. I don't see the same thing in the MetalSplatter app on the store. If I run in the debugger, I get a system message that content will be head anchored while debugging. Other than the system message, it looks the same if I run it directly. This is on a fairly large model, but the store app renders it without the jitter movement.

Frame-11-05-2024-04-37-22

scier commented 4 months ago

@jerbeers are you perchance running with the debugger attached (and/or with a Debug build) when you see the increased jitter? The debugger causes the app to render a lot more slowly in my experience.

jerbeers commented 4 months ago

I've tried with the debugger attached and without it, but both are debug builds. I haven't tried a release build.

jerbeers commented 4 months ago

I take it back, it looks like the scheme is configured to run a release build.