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.

14 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?

  4. List requestedUnsmooth = new List();
    requestedUnsmooth.Add(myself);

    Hi!
    I’m using this method to smooth normals in my Texture Baking system for Unity.
    Your RecalculateNormals function does a wonderful job!
    As of now, when I need sharp edges I first reimport (by code) the model with 0° tolerance in the import settings and then I smooth its normals using your method. This is quite slow for medium meshes (i.e. 30k-40k polys) because the vertex splitting forces the system to recalculate about 3x more vertices than its original configuration.
    I wonder if a runtime method like the one you’re implementing could be a bit faster.

    I’ll wait for the Unsmooth version like a child waits for Santa at Christmas! 🙂

    P.S. Here’s a link to the thread of my system, if you wish to know why I use your method:
    https://forum.unity.com/threads/wip-total-baker-texture-baking-system.467017/

    Francesco

  5. Sorry about replying as an anonymous, here is the proper question:

    Im trying to adapt your code to be able to recalculate the normals in a GameObject in unity that is made of several other GameObjects, each having it mesh component, without having seams between the meshes (Actually if I use this code to calculate the normals in my GameObject, I get seams between the gameobjects since the mesh itself is made of several gameobjects and vertices that are in the same position but in different gameobjects meshes will have a different normal). I’m trying to hold the triangle normals in the class each time I calculate the normals for a different mesh in a different gameobject, but trying to understand struct VertexKey I came to the conclusion that GetHashCode() is not being used anywhere but I may be wrong since I don’t have an extensive knowledge in C#. Could you let me know when GetHashCode() is being used? and if you can, I will aprecciate if you can help me with an approach to solve my problem.

    Thanks so much!

Leave a Reply