# Terrain normals

This topic is 4905 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hi Everyone. My problem is this. I have a 2d array of height values that i use to represent a terrain surface. As of yet i havent had any success calculating proper normals for it. I read somewhere to find a vertex normal in a mesh, to average the face normals of all triangles it exists in. So, unless the vertex is up against one of the edges of the mesh, thats 6 triangles you are going to have to take into account when generating the normal. So far, i can get the face normals alright with (A-B)x(B-C), but im having trouble devising a decent , clean algorithm to average the normals for each vertex. At the moment, as my data was only stored as a set of heights, ive converted it into a 2d array of triangle structures, containing 3 verts each and 3 normals. Any help finding the final vertex normals from either the height data (preferably) or the triangle data would be greatly appreciated :)

##### Share on other sites
create an array the same size as the vertex arrayzero the memory in the normal arrayfor each triangle {    calculate face normal N for this triangle     // add the face normal to each vertex normal used    // by the triangle.    //    for each vertex in face {        vertex.normal += N    }}// we now need to normalise the vertex normals. Either do this// for each normal with pythagorus, the easier way though is // to enable GL_NORMALIZE or do it in a vertex shader.

##### Share on other sites
Getting there i guess :P

So i wrote this, by the above algorithm :
int Terrain::GenerateNormals() {	/*	For each Quad		Divide into triangles		Calculate triangle normals		Normalise normals		Add normals to each vertice in the triangles normal variable	for each Normal		normalise normal	*/	//Calculate intervals	float xInterval = dWidth / (float)iWidth;	float zInterval = dLength / (float)iLength ;		//Loop through heights	for (int x = 0 ; x < iWidth - 1 ; x++) {		for (int z = 0 ; z < iLength - 1 ; z++) {			//Form vertices			Vertex A, B, C, D ;			A.x = xInterval * (float)x ;			A.y = Data[x][z] ;			A.z = zInterval * (float)z ;			B.x = xInterval * (float)(x+1) ;			B.y = Data[x+1][z] ;			B.z = zInterval * (float)z ;			C.x = xInterval * (float)x ;			C.y = Data[x][z+1] ;			C.z = zInterval * (float)(z+1) ;			D.x = xInterval * (float)(x+1) ;			D.y = Data[x+1][z+1] ;			D.z = zInterval * (float)(z+1) ;					//Calculate triangle normals (A,B,C and B,D,C)			Vertex Normal1 = NormaliseVertex (CrossProductVertex (SubtractVertex (A,B), SubtractVertex (B,C))) ;			Vertex Normal2 = NormaliseVertex (CrossProductVertex (SubtractVertex (B,D), SubtractVertex (D,C))) ;			Normal1 = ReflectVertex (Normal1) ;			Normal2 = ReflectVertex (Normal2) ;						//Add normals to vertice 			Normals[x][z]     = NormaliseVertex (AddVertex ( Normals[x][z],     Normal1)) ;			Normals[x+1][z]   = NormaliseVertex (AddVertex ( Normals[x][z+1],   AddVertex (Normal1, Normal2))) ;			Normals[x][z+1]   = NormaliseVertex (AddVertex ( Normals[x][z+1],   AddVertex (Normal1, Normal2))) ;			Normals[x+1][z+1] = NormaliseVertex (AddVertex ( Normals[x+1][z+1], Normal2)) ;		} ;	} ;	return 0 ;} ;

The resulting render doesnt look right....everything is way too bright, with tiny bits of darkness here and there. It is also still very patchy, it doesnt progress smoothly from light to dark even using per pixel lighting.

Any ideas? Did i miss something in this code? Or does it look to be something else.

##### Share on other sites
Normally the vertexes of terrain are in an evenly-spaced grid. As a result, when finding the vertex's normal, most of the terms of the cross-products cancel out, and you are left with extremely simple code to compute the normal of the vertex. Here it is:
Vector3 ComputeGridNormal( HeightField const & hf, int x, int y ){//	The 4 adjacent points in a uniform grid: A, B, C, D////	   B//	   |//	C--0--A//	   |//	   D////	//	The ratio of XY-scale to Z-scale: s = Sxy / Sz//	The desired normal: N = cross(A,B) + cross(B,C) + cross(C,D) + cross(D,A), (then normalize)//	//	Nx = 2 * s * (Cz - Az)//	Ny = 2 * s * (Dz - Bz)//	Nz = 4 * s^2//	N = normalize( N )//	//	Since N is normalized in the end, it can be scaled by 1/(2*s) first://	//	Nx = Cz - Az//	Ny = Dz - Bz//	Nz = 2 * s//	N = normalize( N )//	HeightField::Vertex const * const	paV	= hf.GetData( x, y );	int const			sx	= hf.GetSizeI();	int const			sy	= hf.GetSizeJ();	float const	z0	= paV[ 0 ].m_Z;	float const	z1 = ( x + 1 < sx ) ? ( paV[   1 ].m_Z ) : z0;	float const	z2 = ( y + 1 < sy ) ? ( paV[  sx ].m_Z ) : z0;	float const	z3 = ( x - 1 >= 0 ) ? ( paV[  -1 ].m_Z ) : z0;	float const	z4 = ( y - 1 >= 0 ) ? ( paV[ -sx ].m_Z ) : z0;	Vector3	normal( z3 - z1, z4 - z2, 2 * hf.GetScale() );	return normal.Normalize();}

##### Share on other sites
Yes, I'm with John Bolton on this one. For a regular grid, things are much more simple: you can just do the cross product of two vectors:

Normal = (P[i+1][j] - P[i-1][j]) cross (P[j+1] - P[j-1]);
Normal.normalize ();

don't forget to divide the vector by its own magnitude. In OpenGL you can force it to renormalize normal vectors for you, but that is expensive when you could just normalize it once yourself.

##### Share on other sites
ah the simplicity :/

I got mine working anyway...was accidently normalising the terrain normals after adding each additional face normal to them...so the normals added later were weighted higher.

1. 1
2. 2
3. 3
JoeJ
12
4. 4
5. 5
Rutin
11

• 12
• 16
• 13
• 20
• 12
• ### Forum Statistics

• Total Topics
632178
• Total Posts
3004596

×