Physically Based Rendering shows discontinuity on closed surface

140 Views Asked by At

I added a mesh to a pyvista.Plotter() with

p.add_mesh(mesh, show_edges=True, color='linen', pbr=True, metallic=0.8, roughness=0.1, diffuse=1)

but it displays with a discontinuity (where the mesh started and ended) enter image description here

Why is this junction of cells different from similar ones around this toroid?

2

There are 2 best solutions below

0
On

It's probably due to how surface normals are computed, and that your mesh connectivity is off along that edge.

The way you often generate such closed surfaces is to parametrise with respect to some generalised coordinates, one of which in this case is the azimuthal angle. Where your azimuthal angle sweep starts (following the physics convention: phi = 0 corresponds to the +x axis) and ends (phi = 2*pi, again the +x axis) overlaps, but if you don't take care to ensure connectivity across this boundary then you don't have a closed toroid, but rather an open tube turned back on itself that has a seam where the two open ends meet. This affects how surface normals are estimated on the boundary faces.

If you check mesh.extract_feature_edges(boundary_edges=True, non_manifold_edges=True, feature_edges=False, manifold_edges=False).plot() you will likely get a vertical ring right where the artifact happens. Depending on your mesh it's probably enough to call mesh = mesh.clean() with a small tolerance value, because if your points at the seam properly overlap then this can merge your boundary points and fuse your boundary faces to remove the spurious edge. (You might also have to recompute the normals yourself, but probably not; I'm not entirely sure.)

Case in point: here's a concrete example using an extruded polygon (which is what your mesh seems to be, or close enough):

import pyvista as pv

# generate toroid
square = pv.Polygon(n_sides=4).translate((0, -2, 0))
toroid = square.extrude_rotate(resolution=8, rotation_axis=(1, 0, 0), capping=False)

# let's see what we've got
plotter = pv.Plotter()
plotter.add_mesh(toroid, color='lightblue', smooth_shading=True)
plotter.view_yz()
plotter.show()

light blue toroid with shading artifact along one edge

From the shading it's obvious that something's amiss. It's the hidden boundary edge(s):

print(toroid.n_open_edges)
# 8
open_edges = toroid.extract_feature_edges(
    boundary_edges=True,
    non_manifold_edges=True,
    feature_edges=False,
    manifold_edges=False,
)

plotter = pv.Plotter()
plotter.add_mesh(toroid, color='lightblue', smooth_shading=True)
plotter.add_mesh(open_edges, color='red', line_width=5, render_lines_as_tubes=True)
plotter.view_yz()
plotter.show()

the previous image, with a red line overlayed right where the artifact is

And cleaning with a small tolerance (to allow for floating-point errors) solves the problem:

cleaned = toroid.clean(tolerance=1e-12)
print(cleaned.n_open_edges)
# 0

plotter = pv.Plotter()
plotter.add_mesh(cleaned, color='lightblue', smooth_shading=True)
plotter.view_yz()
plotter.show()

plot of the cleaned toroid with no artifact in shading

0
On

Good Answer. The first and last groups of points were not exactly the same due to floating point errors:-

 [[ 4.8000000e+01  0.0000000e+00  0.0000000e+00]
 [            ...                        ...                          ...          ]
 [ 4.8000000e+01 -3.9188699e-15  1.1756609e-14]]

I fixed it by rounding the coordinates when they were generated (to create an STL file):-

return (x.round(5), y.round(5), z.round(5))

Not sure why such tiny differences had such a noticeable visible effect...