Geometry Nodes - Recalculate mesh normals

Hi,
I would like to make a node group that automatically recalculates normals with wrong directions, exactly like the “Recalculate Normals” operator (bpy.ops.mesh.normals_make_consistent()). Problem is, I have no idea how this operator works, so I don’t know where to start to make nodes that emulate its behavior.

Below are three different cases with randomly flipped normals, I’d like the node group to work perfectly on.
I saw this node setup somewhere on the internet, and it seems to work for the extruded shape, it kind of works for some faces of the monkey but when the mesh curvature becomes concave (the ears, the eyes, the mouth…) it doesn’t do well. The flat grid is left unbothered by the node setup…

Here’s the test file:
Consistent Normals.blend (1.4 MB)

1 Like

Hello !

Quite an interesting topic !
I’ve looked a bit into it, for now I didn’t find a way that would apply to all the cases.
I suspect that the recalculate normal operator loops over every faces and try to make each face normal consistent with their surroundings.

Then ( or before ) we need to find one main direction to orient all the faces to.
For that a method I find by looking on stackexange was to create a vector between the point/face position and the center of the object(average of points) , that allows to approximate the inside and outside.

Then for case like the flat plane ( that needs to be find and treated separately) an arbitrary vector needs to be chosen, this can be done by choosing the positive X/Y/Z axis.

I’m not sure it’s doable without looping possibilities.
Finding the center and flipping normals based on that works in basic cases but it can quickly fail, that’s why it’s needed to inspect each face and make sure each surrounding faces are aligned the same way.

It’s probably possible to take inspiration from blender source code, or google a bit about it.
If that’s doable in GN then that would be a very useful nodegroup for sure !

2 Likes

I don’t know where I can find the raw Blender source code to check how it was done, I never ventured in those waters…

The flat plane is quite easy to flip on its own, but I’m not satisfied with a per case solutions:

However, doing the above led me to an idea:

  1. Capture the original points position
  2. Unwrap / flatten mesh according to UVs
  3. Compare / select normals that go upwards
  4. Reconstruct original mesh’s points position from capture at (1)

GIF

And it works pretty damn well (for the monkey and the grid)! We avoid having to wonder for an arbitrary vector. Plus, UVs are pretty much a universally acceptable orientation for any mesh. The only problem with this solution, is that not all meshes have UVs. And no UVs, no flattening!

But I’m wondering if you can help me dig this idea a little more…
How would you flatten a mesh’s connected faces?
My guess would be to probably use the “Mesh Island” and the “Corners of Face”, but I’m stuck here…
Any ideas?

Here’s the improved file:
Consistent Normals.blend (1.8 MB)

6 Likes

Hello ! Well done ! It’s a great idea worth testing a bit more !
I like how much working in geometry nodes equals thinking out the box * hacking * creative problem solving.

In blender 3.3 there is a new UV Unwrap node. You need to set some seams to the mesh.

Then it’s worth testing it as much as possible on different object, but it look very promising !
If you send you final .blend I’ll use it in my work and see if I can improve it !

It’s also worth testing on heavy mesh and look for possible optimization.

I’ve tested quickly but didn’t find a way to isolate wrong nomals, I guess in your case it works because the mesh is unwarped before you flip face normals :smiley:

is it a bummer ? that suspense is quite unbearable …

Here is my nodetree if that can be useful :

2 Likes

Your unwrap is very interesting. It can even split all faces if you put an unsigned angle greater than 0.000.
I don’t even think I need islands anymore. The only thing that is needed is that it retains the flipped faces when unwrapped, which unfortunately it does not.
Unless we find a way to retain the normals when flattened…

3 Likes

Yes, at this stage I’m left with no idea …
I started to read that and now my head hurts : https://schemingdeveloper.com/2014/10/17/better-method-recalculate-normals-unity/

I’ve started to look for pseudo code examples of how it works but it’s a bit too late I guess…

2 Likes

Hello, just an idea… What about to use Ray cast node? After unwrap, add plane as a reference , below the unwrap uv. Ray cast node in the direction of the normals, if is hit send it to flip faces selection.

Btw i have no idea if it will work, don’t have the opportunity to try it now. :slightly_smiling_face:

2 Likes

Hello @Raptor724 and thanks for suggesting ideas !

The problem is that when unwrapping all the faces are aligned correctly. In sort on the UV map there is no flipped normal. Unless I didn’t understand correctly what you where suggesting, I suspect that using UV while it allow to simplify the problem is probably a dead end …

If someone is interested here is how I do it, based on a post on stackexange, and a cool technique to find islands center by @FrankFirsching !

The idea is simple, we compare faces’s normal with a a vector that goes from their origin to the center of the geometry.
If these two vectors goes rougly in the same direction then the normal is pointing inside, therefore we flip it.

This will probably work in many case, but it’s also easy to make it fail, with weird mesh shape, or face very far from the points, it’s not bulletproof.
But so far, it’s the best I find.

7 Likes

So I asked the same question on Devtalk and got this answer:

The operator is documented here:
https://docs.blender.org/manual/en/3.5/modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-normals-make-consistent

The code implementing the operation is in bmo_recalc_face_normals_exec in bmo_normals.c.

So if anyone wants to dig the source code with me, I found this link that might explain how it works:
https://fossies.org/dox/blender-3.4.1/bmo__normals_8c.html

And this one (see the function at the end);
https://fossies.org/dox/blender-3.4.1/bmo__normals_8c_source.html

3 Likes

Cool !

From what I read quickly it’s similar to what I suspected,
I think they first select a face a make sure it’s pointing away from the center.
Then it iterates through all the surrounding/connected faces and make sure they point in the same direction …

That’s what I grasp from reading the comments anyway…

It sounds like another dead end since we can’t do loop in GN yet, and even with that , it sounds tricky , but probably by using the new topology node at least doing the checks might be doable…

I’m not sure if it’s not worth waiting / finding someone that can port the operator as a new node…

1 Like

You might be right about the UV solution being a dead end.
However I had another idea. Here is a grid plane with flipped normals deformed randomly in all directions to simulate a complex mesh island.

How about we:

  1. planarize each island of the mesh (all points on Z = 0). Now it should a mess, but it’s flat.

  2. Smooth relax said flat island, until there are no more overlapping faces (like the Smooth modifier would do).
    This should lay out the geometry like we did with the UVs, with the difference that flipped normals will be kept.

Now the problems:
I watched a tutorial for smoothing the mesh, but as you can see above, it’s very dirty having to duplicate the Set Position nodes so many times without garranty it will smooth the mesh enough. We may need some kind of very strong multiplier to smooth the island enough (I’d say smooth it to the point it can’t smooth anymore…)
Any ideas how I can better smooth the mesh (or stretch it like a pelt, might be another solution) to prevent overlapping faces?

The problem I foresee with this method, is for closed meshes (such a sphere)? But let’s resolve one problem at a time.

Here’s the file:
recalc normals_flatten and smooth.blend (1.6 MB)

there is a blur attribute node in 3.5 to smooth the mesh.

2 Likes

Hello !

Sorry for not having time to look into the blend now, but this new idea sounds pretty cool !

Yeah as @canerasln pointed out, there is a blur node that should be able to do the smoothing.

I think, you also need to split the mesh in different parts based on faces’s orientation.
Say parts of the mesh are 100% vertically aligned, then once flattened no matter how much smoothing / relax you apply they’ll stay faces with 0 area.

But by doing so it will also fix the closed mesh issue too.

Hello, so here is result based on UV Unwrap and raycast. Only issue is that now it needs manual seams based on vertex group. This problem could be resolved by “shorterst part” = introducing procedural seams, but that is something i dont know properly yet. No prior UV are nessesery. :slight_smile:

The only thing is blender evaluation of normal. I can guess it is based on face area or face count.
Long story short the point is to have several mesh island, then the island as whole has the same normal orientation (means blue or red :smiley: ).

This could be solved by some masive node tree for lets say 16 mesh island and manual swiches.

It works in 3.3 and 3.4.

Node group:

Blend file: Reculculate normals - uv aproach.blend (1.5 MB)

Feel free to combine it with your aproach or improve it in any way imaginable :slight_smile:
Btw just want to say that my understanding of this topic is very limited, so if i repeted anything introduced before, or didnt followed the question, then I apologize in advance. (English is my second language)

1 Like

Well done @Raptor724 !

That’s quite funny since using roughly the same tools you don’t get the same result as I did.
In your case UV-Unwarp preserve face normal, so it works \o/

I tried to add procedural seams and then I ended up with one of the island being fully red …

So it looks like island as a effect on how unwarp tries to deal with normals.
I guess now the problem is mainly about finding the right way to produce the seam, since adding it manually defeat the purpose of a node for that. But it’s already a great step forward I think !

Thanks a lot !

@Raptor724
In my opinion the grid + raycast is superfluous. If we manage to unwrap it properly, it works in less nodes to just compare normals in the upwards direction like this:
Capture

@sozap
Not sure, but I think it retained the normals better because there was some unwrap (albeit manual) to begin with, that halved the mesh in 2 islands.

I myself am not very fluent with the Shortest Path approach either. I understand the basic principles, but I will have to do more tutorials before achieving something with it.

2 Likes

More precisely, it’s the seam that matter in that case, even without prior UV the unwarp ( in geo node) manage to orient each faces properly.

Maybe the shortest path could help indeed. By taking a point in the top and one in the bottom it might be possible to make a big cut in the mesh while maintaining all the faces connected.

It should also ( at some point) do that for each mesh islands.

This is all too great for a good tink-tank but so long they keep expecting users to come up with novel ways to solve very basic problems like this one, they really got another thing coming. I mean wtf? There just have to be low level nodes to handle this!! If people at Sidefx had the same mindset, there would have been just VOPs and nothing else. Many modeling routines/operations should have been addressed by now with low level nodes… What a convoluted way to develop Geometry Nodes?

Well… usually if an object is created procedurally then your normals doesn’t go crazy and has to be recalculated… and if they did while modeling… well…

…usable by Shift-N…but the OP wanted to recreate this… for whatever reasons…

And everone is free to use any other architecture like Houdini… and pay for it… with money and time to learn… :person_shrugging:
They may have even the better solution because they did this long before blender… or it may just be different… IDK…

But nevertheless… all this doesn’t help this discussion at all.

1 Like

Well, there isn’t a way to develop them that will make everyone happy. A lot of tools are needed for modeling, but current development around hairs and simulations bring a lot of benefits too.
Needless to say that a lot of current works is about optimization and refactor to allow even more features in the long run.

Obviously we are going to wait for quite some time until we get something that feels complete and usable by everyone. At least every releases opens a lot of new possibilities and eventually the missing blocs will be added either builtin or as nodegroups.

Covering the whole range of what GN is supposed to do, like modeling, loops, simulation, bake … without going into all the details ensure at least that the design is solid. At some point it might be possible to maybe focus only on adding the missing bits. Rather than discovering 2 years later that every nodes and the whole system needs to be revisited because it doesn’t allows for say loops…

You can believe that developers are highly talented and motivated to do the best software possible, and they strive to make the best choices possible, at least they are a group of people taking decision mixing developpers ,artists and supervisors so if they take a decision, great chances are that it’s a clever one and it’s one of the best possibilities according to the situation.

3 Likes