Smooth Vertex Normals with Crease Angle

Hi,
I’m pondering how to create good normals for any given triangulated mesh. The standard approach “just average all face normals that meet at a vertex” just doesn’t cut it.
One common answer to this is “don’t average normals of faces that meet at a sharp edge”. Well, I want to compute vertex normals. One vertex can be shared by many faces, even ones that don’t share an edge. So the edge-answer seems wrong to me.

I’m now doing:


1. weld the mesh
2. compute per-triangle face normals
2. for each vertex
       build a list of triangles that index the vertex
3. for each vertex V's triangle-list
       for each triangle T1 in the list
         find all others T2 in the list
            if ((T1 dot T2)<creaseAngle)
                remember in T1 and T2 that they are allowed to be averaged at V
4. finally, average the normals that are allowed to be averaged
5. distribute the averaged normals to the faces they belong to

This works well for sharp features but unfortunately, there are cases where it just fails :frowning:

Is there some well known algorithm that handles all (most) cases?
I have been searching the web, but did’t find anything useful.

Instead of introducing a discontinuity (if < creaseangle) try taking in account the area of the face, as a weight for its normal, instead of blindly averaging.

This helped solve my problems on chamfered boxes.
With large square face, axis aligned, had each corner vertex shared with long thin rectangles and a very small triangle, the naive “just average all face normals that meet at a vertex” gave poor results.

Yeah, tried that, but blindly weighting won’t solve you even the simplest box. Discontinuities are absolutely wanted, but only where “it makes sense”… but that is what the algorithm has to find out… somehow.

It’s been a while since I messed with this. In my opinion, there’s not the one way to do it. If your input data is just a cloud of triangles, no more info given, you won’t get satisfying results without at least building the adjacency graph, finding “good” edge loops and then using this edge info to find “good” vertex normals. There are plenty of ambiguous corner cases involved. Just think of an edge sharing more than two triangles for example. So this will fail, too. But separating surfaces by finding continuous edge loops gives at least visually pleasing results. And since you just generated the edges, you can use them for rendering, of course.

CatDog

Mhhm, what is a edge loop? What makes it a “good one”?
Is it a (ordered) list of triangles that all meet at a vertex?
What would this algorithm do with the top vertex of a cone?

The term “edge loop” is normally used to describe an ordered sequence of triangle edges, where the first and last vertex are the same. In particular the outer boundary of a surface describes an edge loop and each inner boundary (aka hole) also describes an edge loop.
What I propose is to find sequences of edges within the adjacency graph, that could possibly be good edge loops. They are good, if they match some kind of criteria, for example that crease angle, but you can make this as complicated as you want. You just don’t check it per edge, but try to find sequences of edges that match. Obviously, this problem is very hard to solve. It’s route finding within the adjacency graph. But you didn’t ask for fastest methods, did you? :slight_smile:

The top vertex of a cone would (should) be detected as some kind of singularity - it’s one of the corner cases. You detect the discontinuity at this vertex (some triangles forming a sharp angle) but you don’t find any sharp triangle edges. So this vertex is an edge loop of it’s own. It not only starts where it ends, but also has zero length.

Ah, this kind of stuff make programming fun. (But only if there’s no deadline waiting for you. :slight_smile: )

CatDog

Then you will need to compute several normals per vertex when angle between adjacent faces is > 30 degrees or whatever threshold.

  1. weld the mesh
  2. compute per-triangle face normals
  3. for each vertex
    build a list of triangles that index the vertex
  4. for each vertex V’s triangle-list
    for each triangle T1 in the list
    find all others T2 in the list
    if ((T1 dot T2)<creaseAngle)
    remember in T1 and T2 that they are allowed to be averaged at V
  5. finally, average the normals that are allowed to be averaged
  6. distribute the averaged normals to the faces they belong to

A crease angle only makes sense between two triangles sharing an edge.
So, you probably shouldn’t be testing two triangles that don’t share an edge.