Replies: 3 comments 9 replies
-
The difference between "smallest" and "conservative" tends to be like that :)
Because I lack a full fledged viewer that does LOD selection, I have a function to compute a DAG cut from a given viewpoint for testing purposes, to be able to visually inspect the resulting mesh. What I'm doing right now is computing the distance to sphere surface from the camera viewpoint, and dividing the error returned by meshopt_simplify by that distance. This is a little bit incorrect: ideally it would account for camera field of view, and perhaps account for the perspective distortion which could help select better LOD in screen corners. I usually like selection algorithms that are invariant to camera rotation, but if the goal is to select an LOD with a given pixel error a more precise method that Nanite uses may be relevant. UE5 Nanite computation is similar in principle, but mechanically different - it takes into account cases where the sphere is clipped by znear, and it takes camera orientation into account, so the computation is not camera rotation invariant. Conceptually I think it's the same as your reference to fig 3.15, even though I find that specific figure odd as no lines or points there connect to sphere radius :) they compute the projected sphere radius in pixels, invert it (that way they get the length that would project to one pixel, using the same coordinates that the sphere is in), and compare that to the error (which is also in linear units in the same coordinates that the sphere is in). The "single DAG cut" should be a property of Note that the monotonicity should also be maintained for the version Nanite is using: a smaller sphere (contained within the larger sphere) would project to a smaller number of pixels. So it's the same criteria, just computed more precisely. On further review I think there might be a couple issues in my code though:
Perhaps a more correct formula would be "return bounds.error / max(d, z_near)"; this should fix both issues and keep monotonicity. And to make it more production ready I would also divide by Note that all of this is just about converting the error to screen space (and as such not really dependent on whether simplification uses quadrics or something else, but is dependent on the simplifier returning something distance-like). The error returned by |
Beta Was this translation helpful? Give feedback.
-
I couldn't quite figure out the projecting an error-sized sphere centered on the LOD sphere's closest point to the camera. It's unclear how to handle the case where the camera is intersecting the LOD sphere - I'm not convinced rejecting the meshlet and using a finer LOD makes sense. It also seems like a strange method to me to begin with, and the paper does not explain it well or link to something else that does. Instead I tried mimicking the other method you wrote about:
However it A) is greatly underestimating the visible error, and B) feels weird to do this, instead of just projecting a sphere based on the error directly... Can you speak more about this paragraph you wrote? I'm possibly doing something wrong, but I also don't understand why the inversion step is valid.
In case I didn't explain well, here's the code I wrote as a reference, specifically |
Beta Was this translation helpful? Give feedback.
-
Fyi on this line Line 112 in 1e48e96 I think this should be tan(), not cot(), right? |
Beta Was this translation helpful? Give feedback.
-
I've been looking at improving my DAG building based on what you've done in nanite.cpp recently.
One thing that stood out to me is that the way I'm using the LOD group bounds is definitely wrong. What you're doing with keeping the error and bounding sphere separate, and forcing parent bounding spheres to contain their children in addition to error monotonic seems correct to me (side note: doing this properly seems like a 183 page thesis https://people.inf.ethz.ch/emo/DoctThesisFiles/fischer05.pdf, but your boundsMerge() seems to work?).
The thing I'm now confused about, is how do you select the DAG cut? What is this section https://github.com/zeux/meshoptimizer/blob/master/demo/nanite.cpp#L603-L615 of code, and boundsError() doing exactly? Some kind of distance-based heuristic it seems, but I'm not quite sure.
I've also looked into what Nanite does (the slides don't really explain, they just mention projecting the error within the group sphere somehow), and the Batched Multi Triangulation paper (figure 3.15, unclear to me what the logic is here https://d-nb.info/997062789/34#page=48), but overall I'm not exactly sure how to project error to the screen in a way that ensures a single DAG cut.
I'm wondering if you have any insight into this area, especially since you're familiar with QEM and the underlying meaning of the error.
Beta Was this translation helpful? Give feedback.
All reactions