PixarAnimationStudios / OpenSubdiv

An Open-Source subdivision surface library.
graphics.pixar.com/opensubdiv
Other
2.88k stars 558 forks source link

Bi-cubic patches output not working as expected #968

Closed johnfea closed 6 years ago

johnfea commented 6 years ago

In my testing, bi-cubic patches generated by Far::PatchTableFactory were not C2 continuous and hence the resulting patch mesh did not look smooth when rendering with analytic bi-cubic patch normals.

Additionally, using ENDCAP_BSPLINE_BASIS instead of ENDCAP_GREGORY_BASIS lead to erratic bi-cubic patches output especially at endcap positions (spikes).

Also, I had to set max subdivision level to at least 2 for adaptive refiner to get working patch output. This is a big bummer since what I want to use OpenSubdiv for is to convert models to patches that approximate the limit surface with roughly one patch per original mesh face.

I followed far_tutorial_6 to make OpenSubdiv generate bi-cubic patches from input mesh, using patches created by Far::PatchTableFactory as the final output. Outputting the final subdivided geometry as polygons (quads) did work without any of the above problems.

The attached jeep image is rendered by only drawing bi-cubic patches and skipping Gregory patches.

jeep

barfowl commented 6 years ago

That's surprising. Generating continuous BSpline patches in the regular areas of meshes is fundamental to so many use cases that I can't believe the resulting PatchTable is so broken.

Before digging into this much further, could you please confirm which version you are using -- specifically whether you are using the dev branch or master.

We recently pulled some changes into dev that significantly change the way the PatchTable is constructed and I'm hoping that did not introduce a regression. If you are using dev, please try master and let me know if problems persist.

As for the problems themselves...

It's hard to infer what might be going on from the image provided. A closer view of a couple of simple BSpline patches would be more helpful -- maybe using a simpler example like a cube. The control points used for the BSpline patches in regular regions come from the refined vertices, and they appear to be in order. I wonder if the evaluation of the patches is the issue here.

I'm also surprised to hear you had to set adaptive refinement to level 2 -- typically level 1 is enough. What behavior forced you to level 2?

Unfortunately there are some historical limitations that prevent a minimal number of patches from being generated, i.e. one per face of the original mesh. Some of those limitations will be going away soon, but if the original face is not a quad, at least one level of adaptive refinement will always be necessary.

johnfea commented 6 years ago

I used master and also tried v3_0_0. I tried now also dev which gives similar looking output.

To determine if I'm outputting patch data correctly, I attach minimally modified far_tutorial_6_modified.cpp which prints the final patches as Povray scene file. I assume the patches are non-rational bi-cubic Bezier patches. There doesn't actually seem to be any facility in OpenSubdiv to iterate over all patches matching a particular face of the original mesh so I just iterate over all regular patches in PatchTable instead. Far::PatchMap gives nearest patch of a face corresponding to particular u,v coordinates, which is not helpful when you need all of the patches. Attached is also pov-ray rendition of that which does not look right, being bugged in similar way as my earlier Jeep rendition using another program with confirmed working bi-cubic patch rendition.

I had to use refinement level 2 because nearly all of the patches were of the gregory basis with level 1. e.g. for the jeep model I get 42 bi-cubic patches and about 4k gregory patches. With cube I get 24 gregory patches and zero bi-cubic patches.

As for the one patch per face, I really hope this will be made possible in the future at least for quad meshes. A paper called "Approximating Subdivision Surfaces with Gregory Patches for Hardware Tesselation" deals with triangles as a special case with triangular patches without adding refinement levels so I think supporting triangles would be important too.

EDIT: The issue here might be that I'm referring to bi-cubic Bezier surface patches with 16 control points whereas "patches" that OpenSubdiv creates are not those but instead bi-cubic B-Spline patches with 16 control mesh points, and conversion between the two is required.

far_tutorial_6_modified.zip

rendition

barfowl commented 6 years ago

Glad to hear we didn't introduce a regression in dev. PatchTables in the versions you are using are producing the results we expect -- barring some as yet undiscovered bug.

One statement caught my eye that I initially thought was in error: "I assume the patches are non-rational bi-cubic Bezier patches." The regular patches are non-rational bi-cubic BSplines not Bezier. I just saw your EDIT (it didn't appear in my email) and that may be the cause of your problems.

Back to the one patch per face topic, it is actually possible to generate patches in the style of the paper you cited -- for some meshes. The limitations in OpenSubdiv that prevent this in general are mainly superficial. It's not recommended or officially supported, but if you specify level 0 for the adaptive refinement and max level, there will be no refinement but the TopologyRefiner will be tagged as adaptive to trick the PatchTableFactory into assembling the appropriate patches (ideally you shouldn't have to do this). But meshes with non-quad faces -- not just triangles -- and some unisolated boundary faces will currently cause problems (PatchTable construction will crash when it encounters unisolated boundaries).

If that is your longer term goal, you will be getting a higher ratio of Gregory patches compared to BSpline patches. I just generated 6 Gregory patches for the cube at level 0. As noted, you get 24 at level 1. Level 2 is the first where regular patches start to appear. Whether to generate Gregory patches at level 0 or level N is ultimately a compromise between performance and accuracy.

johnfea commented 6 years ago

Converting from BSpline patches to Bezier patches solved most of my rendering issues.

I am seeing some holes in some models with patch versions versus polygonal output, for example the Jeep gets cut in half at higher isolation levels and has a couple of cracks at level 1, I'm not sure what that is about and whether it should happen (picture).

I get the one patch per face working at least with simple quads only models. Most models are, however, triangular or mixed and will not work. I think it would be important to get this working too with OpenSubdiv at some point. Is there some fundamental reasons it can't be implemented or has it just never been a priority?

jeep-patches

barfowl commented 6 years ago

Glad to hear the BSpline/Bezier confusion was the main source of the problems.

It's difficult for me to infer what else might be going on given that you are exporting a subset of the PatchTable and evaluating and tessellating the patches yourself. I suggest you try building one of the graphical viewer example programs (e.g. examples/glViewer) to help debug your issues. The viewer examples optionally accept an input mesh on the command line (in the form of an Obj file) and you'll be able to view expected results with the various options available.

I also don't mind having a closer look at the model if you don't mind sharing it (as an Obj file). I wonder if the "cut in half" comment might be due to duplicated vertices along an axis of symmetry. Cracks are expected if you're not careful to tessellate patches at different levels to avoid them. I'd probably be able to identify the reasons for holes with a copy of the model.

Back to the one patch per face, as I mentioned in the previous comment, the reasons its not currently supported are more superficial than fundamental. They have more to do with legacy decisions that were made when generating patches for GPU display in 2.x versions and earlier (the same era that gave us the name REGULAR for the patch type rather than BSPLINE, which would probably have spared you some pain). Most of those GPU dependencies were resolved in 3.x and others in the code have been incrementally removed since. Its never a been a priority to support that particular usage (being an extreme of the performance versus accuracy compromise -- with the poorest accuracy) given more pressing issues to those actively contributing.

It is already possible to generate most patches at level 0 and quad patches at and around non-quad faces for level 1 (the boundary issues I mentioned can be avoided). We are not likely to generate triangular patches for quadrilateral subdivision schemes as parameterization and texturing of the surface is well established as quad-based (e.g. Ptex). There has also been a reluctance to represent a mesh with two patch types when it can be drawn with one. So an option to treat triangular faces of Catmull-Clark meshes specially in this way is not out of the question, just unlikely at this point.

johnfea commented 6 years ago

Here is the jeep model in obj format (it is one of the test models that come with assimp). I am currently unable to use glViewer because it doesn't work properly as my OpenGL version is 3.3 and I believe it requires at least 4.0 to properly function.

There seems to be a rendering issue at all/most open mesh boundaries, which goes away with VTX_BOUNDARY_NONE (please see attached image). However, then all boundary patches are just dropped. The mesh in the picture itself is clean with 4 vertices per face and no duplicated vertices. This also shows up in the Jeep model at higher isolation levels. It would be nice to know if the problem is with my bspline -> bezier conversion or with patches generated by OpenSubdiv.

Also, at one patch per face and with a box, the generated 6 b-spline patches don't seem to have continuous surface normals at patch boundaries. This is probably expected, but I wonder if this would be fixed by using gregory endcaps to at least G1 level?

jeep1.zip

boundary

johnfea commented 6 years ago

I had another look at the border issue. It appears, for a corner face B-Spline patch outputted by OpenSubdiv, that eight mesh vertices are collapsed to a single point. Similarly, for an edge face, five vertices are collapsed to a single point. For B-Spline to Bezier conversion I use geometric method from "Approximating Catmull-Clark Subdivision Surfaces with Bicubic Patches".

davidgyu commented 6 years ago

OpenSubdiv uses a special encoding for the patches along mesh boundaries and along infinitely sharp creases. The encoding is described in more detail here:

https://github.com/PixarAnimationStudios/OpenSubdiv/blob/master/documentation/far_overview.rst#patch-parameterization

and also in the API documentation for the Far::PatchParam class.

This encoding uses different evaluation weights for the control points of the patch than the standard B-spline evaluation weights. This is implemented by Far::PatchTable::EvaluateBasis() using methods from far/patchBasis.h

barfowl commented 6 years ago

As David notes, boundary patches are handled specially for BSplines -- OpenSubdiv extrapolates the missing phantom points implicitly rather than explicitly to evaluate them. If you are building a PatchTable and digging into it to do your own evaluation, you will need to inspect the Far::PatchParam associated with each patch which includes the boundary information required, the depth of the patch and other useful information related to parameterization.

Back to the one patch per face, 6 Gregory patches will represent the cube with tangent continuity, 6 BSpline patches will not.

The whole point of using Gregory patches in general is that they have added degrees of freedom to enforce tangent continuity around extra-ordinary vertices that cannot be achieved with non-rational bicubic patches like BSpline or Bezier. The BSpline option exists for use cases for which a single patch type is preferred, but that usage expects adaptive refinement to reduce the patch size and any associated continuity artifacts to the point of being insignificant (if only the corners are evaluated for these smaller patches, the collective tessellation will appear tangent continuous). This is yet another performance versus accuracy trade-off -- faster drawing with a single patch type at the expense of compromised tangent continuity that is ideally made unnoticeable.

johnfea commented 6 years ago

I added handling for the boundary patches to fix open mesh boundaries but, unfortunately, it did not fix the hole in the jeep model pictured above, although it looks a bit different (image).

The model is pure triangular so the first and only subdivision turns it into quads which do render ok. Perhaps a clue as to what causes this issue, the problematic patches are from one of the two parts of the model where PatchTableFactory uses regular patches instead of gregory patches after the first subdivision. The other part does not render correctly either. I'm looking for some ideas as to what might cause this issue. It looks like the patches are there but not quite the right shape to fill the gap.

jeep-crack

barfowl commented 6 years ago

I imported the model into the glViewer example program and it does exhibit cracks in these areas (this is level 2):

jeep1-cracks

The reason is that the topology of the mesh in these areas is riddled with non-manifold artifacts. It looks like there are some faces underneath and perpendicular to the hood that are causing trouble. Some of the edges along the center also appear to be boundary edges (red) for some reason.

It's a known issue that patches around non-manifold vertices can often cause cracks due to some short cuts we took when adding non-manifold support in 3.x (in 2.x you wouldn't have been able to subdivide this). In many simple cases cracks won't appear, but this one clearly does. It's been on my list to improve this situation with the use of irregular patches and it will probably be fixed in the coming months in conjunction with some other work (for release later in the year).

Usually a mesh needs to be cleaned up in these areas unless there is a good reason for the non-manifold features. The inspector I use also shows some problems between the rear axles but I haven't looked closely. The mesh appears to have been largely quads at some point and then triangulated -- that's really not a good idea if you're going to apply Catmull-Clark subdivision...

jtran56 commented 6 years ago

Filed as internal issue #162590.

barfowl commented 6 years ago

Closing this as the patches generated are working as expected, as illustrated.

Cracks between patches are known to appear around some non-manifold features (see the "catmark_fan" example) and that situation will be improved in an upcoming release.