11

is there a good robust algorithm to calculate normal vector of a convex polygon (in 3D, of course)? For triangles, it is easy: one takes two of the triangle's edges and calculates the cross product:

vec3 u = point[0] - point[1], v = point[0] - point[2];
vec3 n = normalize(cross(u, v));

But this approach does not really extend to polygons very well. Some edges of the polygon can be nearly or "exactly" collinear (this will happen often in meshes where T-Junction removal took place), therefore it is necessary to choose a pair of edges, giving a "strong" normal (both edges are "long enough" and they hold "almost perpendicular" angle).

This approach will still not work for all polygons, though. Imagine a polygon in the shape of a disc. If it is very finely subdivided, all the edges will be very short and all of the consecutive edges will be almost collinear, regardless of the radius of the disc. At the same time, the normal is very well defined.

One solution could be to find the largest inscribed triangle and to calculate the normal of that. However, finding it will have complexity of O(n^2), which seems prohibitive.

A better solution could be to use SVD or Eigenvalue decomposition to calculate the normal, given all the polygon points, not just three or four.

Is there a standard algorithm for this? Anyone has a good way of doing it?

the swine
  • 10,713
  • 7
  • 58
  • 100

3 Answers3

8

If you factorize the formula for a triangle, you'll get the following:

n ~ (p1 - p0) x (p2 - p0)
  = p0 x p1 + p1 x p2 + p2 x p0

You can generalize this formula for arbitrary polygons:

n ~ p0 x p1 + p1 x p2 + ... + pn x p0

So sum the cross product of consecutive edges. This is a robust algorithm and works for non-planar polygons.

If you can be sure that the polygon is planar, I would do the following (to save computation time):

Repeat k times
    Pick 3 random polygon vertices
    Calculate the normal of the according triangle
Choose the longest normal as the polygon's normal.

You might discard any normal that has a length <= epsilon.

Nico Schertler
  • 32,049
  • 4
  • 39
  • 70
  • Have you considered the disc example? I don't see it as very robust, since the cross products of the consecutive edges will be fairly small in there. But otherwise thanks, i guess this will work better most of the time. – the swine Apr 03 '14 at 12:55
  • 1
    The robustness comes with the summation. – Nico Schertler Apr 03 '14 at 12:56
  • Also, using epsilon guarantees the method not to be numerically robust. Consider having a mesh where there are narrow polygons. If the threshold is high, those polygons will not have a normal. If it is too low, the algorithm may accept a slightly non-planar edges or otherwise suboptimal edge pair, yielding imprecise normal even on polygons that have the normal well defined. – the swine Apr 03 '14 at 12:59
  • I'm not convinced about summation. Summing many small values does not work very well in limited floating point precision. – the swine Apr 03 '14 at 12:59
  • 2
    This is also known as Newell's Method (http://www.opengl.org/wiki/Calculating_a_Surface_Normal). – the swine Apr 03 '14 at 13:05
  • @theswine Are you sure this is Newell's Method, the formula at the link you provided does not look the same. – Lenny White Mar 01 '21 at 06:40
  • @LennyWhite https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal#Newell.27s_Method ?? It's a chapter in the page I sent. Are you seeing something else? – the swine Mar 02 '21 at 20:21
  • @theswine If we iterate through every two points of the polygon `cur` and `next`. The formula at the link is `normal.x += (cur.y-next.y)*(cur.z+next.z); normal.y += (cur.z-next.z)*(cur.x+next.x); normal.z += (cur.x-next.x)*(cur.y+next.y);`. While here the formula is `normal += cross(cur, next)`. If you write the cross product in terms of the x,y,z components you don't get the same result. At least I didn't. – Lenny White Mar 03 '21 at 07:56
  • 1
    @Lenny: The individual summands are not equal, however, the sum is. If we take a look at the x-component, the expanded formula from Newell's method is: `nx += cur.y * next.z - next.y * cur.z - next.y * next.z + cur.y * cur.z`. The first two summands are from the cross product. The last two are additional. So every vertex adds `y * z` for itself and subtracts `y * z` for the next vertex. If you do this for all vertices, the additional terms cancel out and you get the sum of cross products. – Nico Schertler Mar 03 '21 at 16:42
0

start from an arbitrary vertex(lets call it vertex A), and move to the next vertex in the list(call it vertex B). calculate the perpendicular vector(call it vector P) to the AB vector. Then continue iterating in the vertex list to find the vertex that is perpendicularly the most distant from vector AB. So at each iteration take the dot product of the current element(take vertex B as the origin) with the vector P and take the one that has the greatest result in magnitude(take absolute value) and call it C. calculate the cross product of A B C vectors.

if the poly is convex you can stop iterating untill the perpendicular distances starts to get smaller in magnitude.

I came up with this idea, i do not know how efficient this method would be since I do not know any other algorithm to compare with.

Kaan99__
  • 43
  • 1
  • 3
  • You cannot really calculate one perpendicular vector in 3-space. There are infinitely many of those, even if you limit yourself to unit length vectors. You can calculate distance from a point to a line passing through two points (AB). You can select such C that is the maximum distance from AB. This is very similar to choosing different ABCs and scoring them by the norm of their cross product. – the swine Jul 03 '18 at 02:45
  • In my solution, I assumed that the polygon is a flat surface, and all the points are coplanar. – Kaan99__ Jul 20 '18 at 19:30
  • That sort of makes for an chicken and egg issue. You need to know that plane first, to score the points. But you are scoring the points, so that you can calculate that plane. I don't think that can work. – the swine Jul 21 '18 at 00:25
-1

You can calculate the covariance matrix for all the points of the polygon (which will be a 3x3 matrix for 3D space). The normal to the polygon will be the Eigen vector corresponding to the smallest Eigen value.

Sourabh Bhat
  • 1,793
  • 16
  • 21
  • Any idea how can normal direction be determined? Because in calculating the covariance matrix, the order of the vertices is lost. Obviously, one can calculate a less precise normal using cross product and flip the precise normal to point in the more similar direction. Can you think of a more elegant solution? – the swine Apr 04 '14 at 14:08
  • You are right, covariance matrix will loose the connectivity and therefore normal vector may have a positive or negative sign depending on your convention. Sorry, I don't know any easy way, then the one you proposed, to get the sign. The method is very robust but expensive compared to other methods. – Sourabh Bhat Apr 04 '14 at 15:36