Calculating vertex normals - newbie question

Started by
8 comments, last by Solias 19 years, 5 months ago
This has been bugging me ever since I got help with fixing my sphere vertex normals. I've done lots of searching in here, but I couldn't find anything that completely answered my questions. Here goes... I understand that a vertex normal can be calculated by adding the adjacent face normals and normalizing the result. Q #1: Can you achieve the same result by dividing the result by the adjacent face count? It was pointed out to me that a quick and easy way to do vertex normals for a sphere is to normalize the vertices. This works awesome, by the way, and thanks again to the two chaps who pointed this out to me (I'm sorry, the names escape me right now). Q #2: Shouldn't the sphere's vertex normals be able to be calculated the same way? Or is the sphere special in how vertex normals are calc'd? Q #3: If they are able to be calculated as stated above (adding, normalizing) and my sphere looked a little odd, I'm assuming that my plane normals were a bit odd since the vertex normals were based on them. If that's the case, how can you possibly screw up calc'ing a plane normal? I'm calculating by: 1. Get a vector by subtracting vertex 1 from vertex 0 (CCW winding, if that matters) 2. Get a second vector from vertex 2 - 0 3. Calc the cross product of the two vectors 4. Normalize My Lambert shader looks fine, but if there's a subtle error, I don't think it'd be noticed. That's enough for now. It's just really bugging me that I believe the sphere vertex normals *should* be able to be calc'd like everything else, but my results show differently. Thanks!
Advertisement
the method you describe for calculating vertex normals is correct, yet nothing but an estimate. there is no 'true' way to calculate vertex normals based on only triangle information, because.... what a vertexnormal should really do isnt actually defined. well what a vertexnormal does is, it gives the intended surface orientation at a point. but how knows what the intended surface orientation at a point is? it could be anything. the trianges around it certainly wont tell us for sure.

there are plenty of situations thinkable where the method you suggested for calcing vertex normals would produce completely 'wrong' results, whatever that it. probably the most visually pleasing way to define a vertex normal for most cases would be to do a most-squares on the dotproduct of the vertexnormal and the planenormals, but then again that can go completely wrong for special cases aswell.

if you can find a mathematicly sound way to produce vertex normals, like in the case of a sphere or some other primitive, this is always better. if you first tesselate and then extract normals, youve already lost information.
Interesting. So a 'correct' vertex normal is considered to be whatever the author decides is correct? I haven't heard that before. But you're saying that no single way works in all cases. Bummer, I was afraid of that. *sigh* I will have to update my algorithms to reflect this new piece of information. But, on the bright side, I walk away knowing more than I did before. Thanks, Eelco, for the new viewpoint.
another way of computing vertex normals is when you compute a face's normal, weight the normal by the face area (actually, you do this by not renormalizing the normal after the crossproduct, and dividing it by two.

there are lots of other ways to compute face normals than these two. basically you can see the surface normal as being the normal of a higher-order surface than the base mesh (that high order surface being the surface your mesh is supposed to fake), that is, the normal of a subdivided version of the same mesh at the same point. how the normal will look like depends on the subdivision scheme, and you've got as many ways to compute normals as there are subdivision schemes...

but the most commonly used are these two, you'll rarely come across more complicated algos for normal calculation...

EDIT: there also are faster ways to compute normals for specific geometry (like regular heightfields, where you can get the normal by simply getting the two vertical offsets between the 4 surrounding vertices, and throwing them into the two horizontal offsets of the normal, and setting the vertical normal component to 2.0f * the heightfield grid spacing).
Yes, Eelco is absolutely right, vertice normals is arbitrary thing that should just be set to something that produces most visually good result, and there's no "correct way", and if you can mathematically find normal it's better to use it because otherwise information is lost.

And, regarding your questions:
Q #1: if you do that, normal will not be unit-length. If you add 2 vectors A and B and then divide result by 2 you don't get unit-length vector if A and B is not parallel. You can do it if you really want non-unit-length normals, but i doubt it's what you need.

Additionally: you may find it useful to compute vertex normals that faces with bigger area will influence resulting normal more than faces with smaller area (it may not be useful, in fact. It may even be useful to do opposite!). It's very simple. You just need to not normalize your cross products when computing sum. (and you still need to renormalize sum).

That is, and as i understand, what you're currently doing can be done as
set all vertex normals to 0,0,0;
for each face , compute cross product of two edge-vectors(like you're doing), and normalize it.
|for each vertice of that face, add that to it.

normalize all vertex normals.

And to weight your vertex normals by areas of faces that larger area faces influence more, you simply need

set all vertex normals to 0,0,0;
for each face , compute cross product of two edge-vectors
|for each vertice of that face, add that cross product to it.

normalize all vertex normals.


And to weight your vertex normals by areas of faces that smaller area faces influence more, you simply need

set all vertex normals to 0,0,0;
for each face ,
|A=cross product of two edge-vectors;
|A=A*(1/DotProduct(A,A));// invert length
|for each vertice of that face, add that cross product to it.

normalize all vertex normals.

I showed all those methods to show that there's no "right" way.
In some cases, first may give more eye-pleasing results, in some cases, second, and in some cases, third. But almost always, if you tesselate mathematical object, it's better to use mathematical normals of that object as Eelco said.
Vertex normals aren't arbitrary, they control what the surface looks like. If the normals 'bend' with the triangle faces and are consistent at the edges between triangles, the surface will appear smooth. If the normals are disjoint, it produces a hard edge.

Take a dodecahedron (20 sided die), with abrupt normals (orthogonal to the faces) it will look like a dodecahedron, but with normals that bend towards the next facing it will look like a sphere.

If the shape isn't smooth, there needs to be a different normal for each triangle that a vertex is in - that is only smooth surfaces should have consistently paired normals and vertices.
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
This is why I dig this forum, I learn so much just by asking a simple question. Thanks for the replies, I really appreciate it. I'm going to save this all off into one of my many reference files.

Magmai - I've heard of doing that, multiple normals per vertex to achieve the faceted look even with vertex shaders. But I've also heard of shading on a primitive basis. For example, a cylinder...the top and bottom primitives get Lambert for a crisp edge, while the body gets Gouraud for a smooth curve. Is one way better than the other?
sorry for OT, but IIRC "dodeca" == 12 ... anyway, i know you meant icosohedron.

As about making different surface shaders, i don't think it's necessary. For same cylinder, you can make planes to have separate normals, and tube to share normals.
Quote:Original post by Magmai Kai Holmlor
Vertex normals aren't arbitrary, they control what the surface looks like. If the normals 'bend' with the triangle faces and are consistent at the edges between triangles, the surface will appear smooth. If the normals are disjoint, it produces a hard edge.

what do you mean by arbitrary? they certainly have an effect on the outcome, but there is no consistent way to derive them from the geometry. as is evident from your own example, a 20 sided primitive could be meant to represent a sphere, in which case you need other normals than when you meant to show a icosahedron. there is no way to tell what was intended by just looking at the triangles. they dont hold this information, hence why any attempt to try and get vertexnormal information from triangles is bound to be ambigious at best.
Quote:Original post by oakskc

Q #2: Shouldn't the sphere's vertex normals be able to be calculated the same way? Or is the sphere special in how vertex normals are calc'd?



There's some good info in this thread, but I just thought I'd add a bit about sphere normals. For a sphere centered at the origin (in object space), finding normals for each vertex is trivial. Each vertex in the mesh you are using to approximate the sphere is a point on the surface of that sphere. The normal at any point on a sphere points directly away from the center of the sphere. The normal for a point on a sphere is parallel to the vector from the center of the sphere to that point. So just take each vertex in your sphere and normalize it to find the vertex normal. If your sphere isn't centered on the origin in object space you'll need to subtract the center of the sphere from each vertex before normalizing.

Ni = normalize(Vi - Center)

This topic is closed to new replies.

Advertisement