normals for a terrain

BlitzMax Forums/OpenGL Module/normals for a terrain

deps(Posted 2006) [#1]
Hi,

I'm creating a simple heightmap for my isometric game.
But I have some problems with the calculation of normals to make the lighting look nice. The problem is that I have no idea of how to do it.
A google search gives me a lot of stuff, but they all use vectors, something I don't use. And they also talks about triangles, I'm using a quad.
They talk a lot about math and I suck at it. :P

This is the code for a tile:
Type Tile

	Field y1:Float, y2:Float, y3:Float, y4:Float
	Field nx:Float[4], ny:Float[4], nz:Float[4] ' Normal for this tile

	' Resources	
	Field gold:Int, metal:Int, stone:Int	
	
	
	Method CalcNormal()
		'TODO: Implement me!
                ' I make the normal point straight up for the moment.
		For Local n = 0 Until 4
			nx[n] = 0
			ny[n] = 1
			nz[n] = 0
		Next
	EndMethod
	
	Method Render( x:Float, z:Float )
	
		glBindTexture( GL_TEXTURE_2D, Textures.grass )
		glBegin( GL_QUADS )	
			glNormal3f( nx[0],ny[0],nz[0] ) ; glTexCoord2f(0, 0) ; glVertex3f( x,y1,z )
			glNormal3f( nx[1],ny[0],nz[1] ) ; glTexCoord2f(1, 0) ; glVertex3f( x+1,y2,z )
			glNormal3f( nx[2],ny[0],nz[2] ) ; glTexCoord2f(1, 1) ; glVertex3f( x+1,y3,z+1 )
			glNormal3f( nx[3],ny[0],nz[3] ) ; glTexCoord2f(0, 1) ; glVertex3f( x,y4,z+1 )
		glEnd()
	EndMethod
	
EndType


CalcNormal() really needs some work. But I don't know how to fix it. :P
Each corner of a tile has a height (y1 to y4) and a normal nx[],ny[],nz[].
Can anyone give me some help on this?


deps(Posted 2006) [#2]
No one knows how to do this?


Jim Teeuwen(Posted 2006) [#3]
You can usesome code to get the nrmal from a triangle.
Since you are using quads, you have more than the required 3 points for a single face.

Just pick 3 points from the quad and generate the normal from that. The result will be the same as doing it from a quad.
It doesnt really matter which 3 points as far as I know. Just use the first 3 or so.

Also, normals are not set per vertex, but per-face. In your case a quad.

Your code can be changed from:
glBegin( GL_QUADS )
glNormal3f( nx[0],ny[0],nz[0] ) ; glTexCoord2f(0, 0) ; glVertex3f( x,y1,z )
glNormal3f( nx[1],ny[0],nz[1] ) ; glTexCoord2f(1, 0) ; glVertex3f( x+1,y2,z )
glNormal3f( nx[2],ny[0],nz[2] ) ; glTexCoord2f(1, 1) ; glVertex3f( x+1,y3,z+1 )
glNormal3f( nx[3],ny[0],nz[3] ) ; glTexCoord2f(0, 1) ; glVertex3f( x,y4,z+1 )
glEnd()


to:
glBegin( GL_QUADS )
glNormal3f( nx[0],ny[1],nz[2] )
glTexCoord2f(0, 0) ; glVertex3f( x,y1,z )
glTexCoord2f(1, 0) ; glVertex3f( x+1,y2,z )
glTexCoord2f(1, 1) ; glVertex3f( x+1,y3,z+1 )
glTexCoord2f(0, 1) ; glVertex3f( x,y4,z+1 )
glEnd()


The part between glBegin( GL_QUADS ) and glEnd()must be repeated for each polygon/quad/triangle in the mesh you are rendering.


fredborg(Posted 2006) [#4]
Hi,

You can find some code here: http://www.blitzbasic.com/Community/posts.php?topic=62118

What you need to do is calculate the normal for each triangle, and then for every vertex that is connected to that triangle, add the triangle normal. Once you are done processing the triangles you can simply normalize the normals.

If you need more help, just ask.

Also, normals are not set per vertex, but per-face.
Nope, that's not right. Normals are set per vertex. And it DOES matter which three vertices you use, and in which order, when you calculate a normal of a quad/triangle.


deps(Posted 2006) [#5]
I wrote a reply to Jim T, but during the post preview I saw that it was unneeded to post it after fredborgs post.

So here goes the new reply:

	For Local v:Int = 0 Until CountVertices(surf)
		Local nx:Float = surf.VertexNX(v)
		Local ny:Float = surf.VertexNY(v)
		Local nz:Float = surf.VertexNZ(v)
		Local d:Float = 1.0 / Sqr(nx*nx + ny*ny + nz*nz)
		VertexNormal surf,v,nx*d,ny*d,nz*d
	Next


This looks like something I need, but I have a couple of questions... :)

surf.VertexN[X|Y|Z]. I think I know what it does, but how can I adapt that to the 4 Y variables I have?
The Tile is always 1 GL unit wide so I don't have to measure a distance or anything, and can probably calculate the vector from the height angle between two different corners. Right? But how? This requires a bit too much math for me. :/


Brendane(Posted 2006) [#6]
I'm going to give you enough info to research and do this yourself. You'll feel better than if someone handed it to you on a plate, and trust me, it's not really difficult and you'll realise that it's not "too much math" at all.

To compute the normal of a plane (your quad) you take the cross-product of 2 vectors on that plane (you can easily compute 2 vectors from your quad by using 3 of its vertices). Here's the math for that.

http://www.lighthouse3d.com/opengl/maths/

That gives you a vector which points 90degrees to the plane.

Next (if you're going to use it for lighting calculations) that vector needs to be 'normalised' - that is to say its length needs to be 1. You do this by dividing the vector by its length. To compute its length use pythagoras theorem. In 3d that is, length = sqrt( (x*x) + (y*y) + (z*z) )


deps(Posted 2006) [#7]
It's always a nice idea to give someone the right ammount of info and let them figure it out themself. It gives a nice "ah-ha!" feeling when they (I) finally get it. :)

I managed to solve one part of the problem. Normals are calculated for each corner of a tile. (yay!)
The current problem I have is to average nearby normals to make the lighting smoother. It won't work for some reason.

But the terrain code development is put on hold for some time. I have too much fun fiddling with some AI stuff. :)

But thanks for the help. I will take a more closer look on the lighthouse3d tutorial later when I feel I really need the terrain to work as expected.


Brendane(Posted 2006) [#8]
Great!

If you care to post your code to compute the normals and average them I can see where it's going wrong.

Averaging the normals is simply a matter of summing the normals and then renormalising the result ( you don't need to divide by the number of normals in this case since the process of renormalising takes care of it).

Does your lighting look correct for the unaveraged normals? If not then perhaps it's just that your normals are pointing the wrong way. To correct that, swap the order of the cross product operation.

Hope that helps.


deps(Posted 2006) [#9]
I was going to show the code, but I spotted a small error in the normal calculations. Soon after I found a lot of other small errors. Then I took a look of the code that averages the normals, fixed some more small errors and suddenly it all works just fine. :)
Stupid typos like using one vertex two times, or averaging with the wrong corner of another tile.

I'm currently trying to find a nice spot for the directional light to make the shadown look a bit better, to give the scene a bit more depth. After that I'm going to prod some buttocks in the Entity-Map collision department.


Thanks a lot for the help. :)


Brendane(Posted 2006) [#10]
Glad to hear you're on track!


Defoc8(Posted 2006) [#11]
EDIT - oops! - removed misleading info. ;)