1
$\begingroup$

Assume you have a list of vertices and their connectivity information. (I.e you can find all the adjacent edges and faces to each vertex).

What is a good approach to calculate a normal from this information?

Currently I first tried naive averaging, i.e:

$\frac{\sum n_i}{N}$

Where $n_i$ are the face normals and $N$ is the total number of faces

And I also tried:

$\frac{\sum n_i * a_i}{A}$

Where $a_i$ are the respective areas of each face and $A$ is the sum of the areas of all adjacent faces.

The first gave me this:

enter image description here

The second gave me this:

enter image description here

What I want is:

enter image description here

That last image I obtained by setting the normal to be the vector from the origin to the vertex position, but this is only true for highly regular meshes like cubes and spheres, it doesn't hold generally. The data I am using to make this is just the default blender cube.

Increasing precision to doubles doesn't seem to affect the result. This is the wiremesh:

enter image description here

Obviously the issue occurs because the number of faces adjacent to a vertex isn't balanced, which skews the normals into certain directions, but I don;t know how to compensate for that

$\endgroup$
  • $\begingroup$ Do you normalise the length of the normal vectors before adding them together ? $\endgroup$ – PaulHK Apr 9 at 5:38
  • $\begingroup$ Yes, the error comes from the number of normals not their magnitudes. $\endgroup$ – Makogan Apr 9 at 5:50
  • $\begingroup$ Are the lengths of the normals 1 though ? The first pic looks like what happens when you sum up cross-products with normalising them. $\endgroup$ – PaulHK Apr 9 at 6:09
  • $\begingroup$ ETA: Given this is a simple cube we should be able to look at the normals with our eyes and know what to expect. Can you list an example of 1 vertex being computed along with all the face normals that are summed up into it ? $\endgroup$ – PaulHK Apr 9 at 9:04
3
$\begingroup$

You are not too far off with your second averaging approach. The problem is, that the area is the wrong weighting factor for what you want to achieve. You want each of the 3 sides of the cube to contribute equally to the vertex normal, but you need to extract the information from the adjacent triangles. The area and the number of triangles are bad weighting factors since they vary with the number of triangles per side. But one thing stays always the same at each vertex: The angle between two edges at the side. So you just need to take the angle of each triangle connected to the vertex and use it as a weighting factor for its normalized face normal. The weighting factors will always sum up to 90°/$\frac{\pi}{2}$ per side of the cube. Look for example at the bottom left vertex at the front in the image you posted.

Your first approach fails because it yields:

$$N_v = \frac{2*N_l + 2 \cdot N_f + 1 \cdot N_b}{5}$$

where $N_l$ is the left, $N_f$ the front and $N_b$ the bottom side normal. Assume them to be normalized. The factors result from the triangle count at the vertex and their sum.

Your second approach fails because it yields:

$$N_v = \frac{N_l + 0.5 \cdot N_f + 0.5 \cdot N_b}{2}$$

Here the factors result from the fact, that only the left face has all its triangles connected to the vertex. The bottom face connects only 1 of 2 and front face only 2 of 4 equally sized triangles to the vertex.

If you take the angles, you get:

$$N_v = \frac{2\cdot 45 \cdot N_l + 2 \cdot 45 \cdot N_f+ 90 \cdot N_b}{270}$$

Now each side contributes equally and independent of the number and size of triangles connected to the vertex.

EDIT:

I just realized, that the front face also contains only 2 triangles. The diagonal of the back face triangles irritated me. However, this will not affect the conclusion but the weigting factors change. For the fist approach you get:

$$N_v = \frac{2*N_l + 1 \cdot N_f + 1 \cdot N_b}{4}$$

The second remains unchanged and for the angles you get:

$$N_v = \frac{2\cdot 45 \cdot N_l + 90 \cdot N_f+ 90 \cdot N_b}{270}$$

which is still correct. What is interesting is that both your approaches give the same results for the shown cube, since a connection of the vertex to 2 triangles also means that the area weight is doubled. You can also see this if you compare both pictures.

| improve this answer | |
$\endgroup$
  • $\begingroup$ Using the angle worked $\endgroup$ – Makogan Apr 9 at 18:12
0
$\begingroup$

Assuming you want to calculate the shared vertex normal... if you know which faces use the vertex and they have unitised normals.

\begin{equation*}\left \| \sum \left \| n_{t} \right \| \right \|\end{equation*}

Your first appraoch is close, but would only work if the face normals are unitised otherwise they will skew.

See this question for more information

Given your last image, it looks like you want normals per face (flat shading) in which case you shouldn't share your vertices between faces, instead you need three vertices for each triangle (i.e 36 for a cube using tgriangles, or 24 for a cube if using quads).

If you want to use no more than 8 vertices, you will effectively get smooth shading (possibly what your first two images show) and not flat shading (what it looks like you want with your 3rd image), or you need to have normals per face (and not per vertex).

| improve this answer | |
$\endgroup$
  • $\begingroup$ That's just doing the area weighted average of the face normals, it's just going about it differently than I am $\endgroup$ – Makogan Apr 9 at 15:49
  • $\begingroup$ @Makogan the vertex normal has nothing to do with area. When you say calculate normal, what is it do you mean exacatly? Did you check out the qu I linked to. $\endgroup$ – lfgtm Apr 9 at 15:53
  • $\begingroup$ I read the question you linked, what Inigo Quilez is doing is, he's adding the cross product then nromalizing the vertex, the cross product gives you the normal times the area of the triangle times 2. Then when you nromalize it, that;s equivalent to dividing by the norm, and the norm is equal to the sum of the areas of all trinagles times 2. i.e the formula in the linked question is the same as averaging the face normals based on the area of the adjacent triangles. $\endgroup$ – Makogan Apr 9 at 15:57
  • $\begingroup$ @Makogan I see what you are saying, apologies my bad, you are looking for face weighted vertex normal rather than the vertex normal. This is not the correct normalisation to do for the topology you have then (i.e a cube, which has hard edges) and so not going to give you the answer you are looking for. Agree with wychmaster, you should weight the normals based on adjacent angle difference, and not area. The type of normalisation you wish to do depends on what you want it for (lighting, topology analysis etc), and the topology you have. What do you want the normal for? $\endgroup$ – lfgtm Apr 9 at 18:00
  • $\begingroup$ I need the normal to do afine scaling $\endgroup$ – Makogan Apr 9 at 18:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.