Calculating Vertex Normals With No idexes

Started by
6 comments, last by _DarkWIng_ 19 years, 7 months ago
Hey All, I am creating a simple heightmap renderer for a sort of 3D overworld. Everything in my renderer works fine, but I can't seem to figure out a way to calculate vertex normals correctly. Basicaly my heightmap is a 2D array of points which the program itterates thrugh and pushes off to the renderer using glVertex3f. I don't need any fancy vertex-lists, or indexes, etc. as this method works fine and dandy for my needs. My only issue is when it comes to calculating the vertex normals, Is there an easy way to figure it out in this situation?
Advertisement
You could always use the normal (0,1,0) until you find a better solution...

Or, if you're using triangle primitives, and you have all three vertices for the triangle, you can just calculate it using this snippet from a DirectX app (should be easily converted to OGL - it's just simple maths):
Private Function GetNormal(v0 As D3DVECTOR, v1 As D3DVECTOR, v2 As D3DVECTOR) As D3DVECTOR    '//0. Any Variables        Dim L As Double        Dim v01 As D3DVECTOR, v02 As D3DVECTOR, vNorm As D3DVECTOR        '//1. Get the vectors 0->1 and 0->2        v01.X = v1.X - v0.X        v01.Y = v1.Y - v0.Y        v01.Z = v1.Z - v0.Z                v02.X = v2.X - v0.X        v02.Y = v2.Y - v0.Y        v02.Z = v2.Z - v0.Z        '//2. Get the cross product        vNorm.X = (v01.Y * v02.Z) - (v01.Z * v02.Y)        vNorm.X = (v01.Z * v02.X) - (v01.X * v02.Z)        vNorm.X = (v01.X * v02.Y) - (v01.Y * v02.X)        '//3. Normalize this vector        L = Sqr((vNorm.X * vNorm.X) + (vNorm.Y * vNorm.Y) + (vNorm.Z * vNorm.Z))        vNorm.X = vNorm.X / L        vNorm.Y = vNorm.Y / L        vNorm.Z = vNorm.Z / L            '//4. Return the value:           GetNormal = vNormEnd Function

Though you'll have to translate it into whatever language you're using, as it's in VB6 right now. But that's generally how its done.

EDIT: this will create vectors correctly when the vertices are listed in clock-wise order. If they are listed ccw, then the resultant normal will be inversed. You can un-inverse it by multiplying each of the values by -1.

Mushu - trying to help those he doesn't know, with things he doesn't know.
Why won't he just go away? An question the universe may never have an answer to...
Hey Mushu,

The code example you gave, while good, illustrates how to find a Face normal of a given triangle. That is, you give the function your triangles 3 points and out pops the face normal. I need to obtain the vertex normal in order to achive a smooth lighting effect.

Does anyoen have any ideas on this?
Well, is it possible to use the position of the neighboring 4 vertices to find a plane, then use the tangent to that plane?

I have no idea how to implement that, but you'll probably end up doing something along those lines, more or less.

Or you can just calculate all the face normals, then average the 4/8 per vertex to find the vertex normal. That's all the ideas I have left. Good luck!

Mushu - trying to help those he doesn't know, with things he doesn't know.
Why won't he just go away? An question the universe may never have an answer to...
So the way I learned how to do this is through implementing subdivision surfaces. They're a bit complicated, but pretty neat. Anywho, you can find a doc that has the normal generation (among other things) http://www.mrl.nyu.edu/~dzorin/sig00course/ in the coursenotes00.pdf, specifically on page 71-72. Essentially, the idea is that you loop around a vertex, and preform this weighted sum of each of the vertices to calculate two tangent vectors. The smoothed normal is then the cross product of the two. More specifically:

assume connected_vertices is a std::vector of all the vertices around the vertex you're calculating the normal for. These must be in order. It should be something like this (i dont think the winding direction matters though):

 3__2__1 |\ | /| | \|/ | 4--a--0 | /|\ | |/_|_\| 5  6  7

t1, t2 are the tangent vectors, and are initially set to zero.

for (int i = 0; i < connected_vertices.size () - 1; ++i) {  t1 += connected_vertices.at (i) * cos (2 * PI * i / connected_vertices.size ());  t2 += connected_vertices.at (i) * sin (2 * PI * i / connected_vertices.size ());}normal = cross (t1, t2);


To calculate at a boundary, its a little complicated. Here you once again generate the two tangent vectors, and take the cross product. This time we'll call them t_along (along the boundary), and t_across. Say we've got a 10x10 heightmap, and we're trying to calculate the normal at (0,5), where the boundary is along x=0:

1--a--0

t_along = connected_vertices(1) - connected_vertices(0);


For the t_across, there are three separate cases: if the current vertex only has two connections (eg: in a line):

1--a--0

t_across = vertex(0) + vertex(1) - 2*vertex(a);


for three, we are trying to calculate at vertex 1 in this case:

   1  /|\    / | \ 2--a--0


t_across = vertex(1) - vertex(a);



for all the other cases:

 3__2__1 |\ | /| | \|/ | 4--a--0


theta = PI / (connected_vertices.size () - 1);for (int i = 1; i < connected_vertices.size () - 1; ++i) {  t_across += connected_vertices.at (i) * sin (i * theta);}t_across *= 2 cos (theta) - 2;t_across += sin (theta) * (connected_vertices.front () + connected_vertices.back ());


Then the normal is just cross (t_along, t_across). Its a bit complicated, you just have to make sure that you're using the right vertices.
Since you are using heightmap, "Finite Difference" is the best method to calculate by far. It's faster and simpler than anything else. Use google to finde some implementations. And I belive nVidia's normalmapper has source for this.
You should never let your fears become the boundaries of your dreams.
yeah, but my way's funner

:p

but in all actuality, mine might just be a complicated way to do finite difference on complicated meshes, and heightmaps are much simpler. I'm not sure however which one generates better normals. One thing mine will do is generate along the edge, where finite difference I don't think will work there. You could combine the two for that case though.
Quote:Original post by idadesub
yeah, but my way's funner

Nice one :)

Quote:Original post by idadesub
One thing mine will do is generate along the edge, where finite difference I don't think will work there.

Finite difference will work on edges if you take that in account while writing it. You can select to clamp, clamp to border or to repeat the heightmap, depending on what you need.

But the downside is that it only works on hightmaps, not on general meshes.
You should never let your fears become the boundaries of your dreams.

This topic is closed to new replies.

Advertisement