• Advertisement
Sign in to follow this  

Terrain normals

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

If you intended to correct an error in the post then please contact us.

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 this post


Link to post
Share on other sites
Advertisement

create an array the same size as the vertex array

zero the memory in the normal array

for 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 this post


Link to post
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 this post


Link to post
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 this post


Link to post
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 this post


Link to post
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.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement