A better method to recalculate normals in Unity – Part 2

It’s been over 2 years since I posted about a method to recalculate normals in Unity that fixes on some of the issues of Unity’s default RecalculateNormals()  method. I’ve used this algorithm (and similar variations) myself in non-Unity projects and I’ve since made minor adjustments, but I never bothered to update the Unity version of the code. Someone recently reported to me that it fails when working with meshes with multiple materials, so I decided to go ahead and update it.

The most important performance update is that I now use FNV hashing to encode the vertex positions. The first implementation used a very naive hashing which would have many hashing collisions. A mesh with approximately a million vertices would end up with a significant amount of collisions, which was a performance killer.

Using FNV hashing reduced collisions to a negligible amount, even to 0 in most cases. I don’t have the figures now, as this change happened about a year ago, but I might do some newer measurements later (read: probably never).

To summarize, here are the changes:

  • Fixed issue with multiple materials not working properly.
  • Changed VertexKey to use FNV hashing.
  • Other (minor) performance improvements.

And I know you’re here just for the code, so here it is:

After you include this code in your project, all you have to do is call RecalculateNormals(angle)  on your mesh. Make sure you visit the original post for additional information about the algorithm.

7 thoughts on “A better method to recalculate normals in Unity – Part 2”

  1. Hey, I’m procedurally generating a mesh (convex hull) but I now I want to change the ‘SmoothingAngle’ value, but the Unity API doesn’t let me do this through script, can I solve this with your script?

    1. Yes, just put this script in your project and you’ll be able to use the angle. Keep in mind that when the vertices are already merged (two triangles sharing the exact same vertices, not just distinct vertices at different positions), they will be smoothed together regardless of the angle.

  2. I’m trying to use this to get hard angles after moving vertices, and then reassigning the mesh vertices. The original mesh is just a flat plane imported with a 0 tolerance.

    The script appears to work before moving the vertices on any object (tested it with spheres and teapots), but after moving the vertices any change to the smoothing angle results in the exact same smoothing. Results are similar as tolerance is increased as well, unless tolerance is dropped to 1, then smoothing results change as smoothing angle is increased or decreased but not with appropriate results.

    1. The problem is that two triangles can share a vertex, and a vertex can only have one normal. When importing, Unity splits the vertex into two only if any attribute other than position differs (normal, uv, color, etc).

      To get around this you import the mesh at 0 tolerance, but then you end up with a larger mesh and this (probably) still won’t work if the triangles are exactly flat to one another.

      In other words, any normals that are smooth after calling the original RecalculateNormals() method will not get un-smoothed using my method.

      It’s a bit more complicated going the other way, because you’ll have to modify ALL data and not just the normals. So, it’s currently not possible with this script. But seeing how people have been asking for this and that it’s a useful scenario (originally I thought it wasn’t useful enough to bother) I decided to work on this. I can’t give a time estimate yet though.

  3. Hi Charis, firstly, thank you for posting this. It has been very helpful. I am trying to use this with a procedural terrain engine (marching cubes). I’m using chunks of mesh and getting seams along the borders after applying this. Can you give me any clues how i might be able to apply your method to account for triangles and verts in neighboring chunks and smooth across the chunk borders?

Leave a Reply