Neil Roy
Neil Roy

Reputation: 632

Graphic issue with vertex normals

I have checked out several solutions in here and other pages for calculating vertex normals. The common solution which seems to work best for my own implementation which renders a 3D terrain is to calculate the face normals, which isn't a problem. And then go over each face and add it's normal to the vertices which make it up and then normalize those when done. It seems to work for the most part, but I have some strange graphical problems, mainly where the light transitions from light to dark, you can tell where the faces are. In the following image you can see this near the lower right side, at the top of this hill.

So I am wondering what is causing this strange pattern. It has something to do with how I am calculating the normals, but I am just not seeing where the issue is. Any help would be appreciated. Lunar terrain render

The code to calculate the normals is...

// Calclulate surface normals
vec3 v1, v2, v3, vec1, vec2;
for(GLuint i = 0; i < terrain->NumFaces; i++) {
   v1 = terrain->Vertices[terrain->Faces[i].vert_indices[0]];
   v2 = terrain->Vertices[terrain->Faces[i].vert_indices[1]];
   vec1 = vector(&v2, &v1);

   v3 = terrain->Vertices[terrain->Faces[i].vert_indices[2]];
   vec2 = vector(&v3, &v1);

   terrain->Faces[i].surface_normal = crossProduct(&vec1, &vec2);

   normalize(&terrain->Faces[i].surface_normal);
}


// Calculate vertex normals...
// Add all the surface normals to their attached vertex normals
for(GLuint currentFace = 0; currentFace < terrain->NumFaces; currentFace++) {
   vec3 *f = &terrain->Faces[currentFace].surface_normal;
   for(GLuint faceVertex = 0; faceVertex < 3; faceVertex++) {
      vec3 *n = &terrain->Normals[terrain->Faces[currentFace].vert_indices[faceVertex]];
      *n = vec3Add(n, f); // adds vector f to n
   }
}

// Go over all vertices and normalize them
for(GLuint currentVertice = 0; currentVertice < terrain->NumVertices; currentVertice++)
   normalize(&terrain->Normals[currentVertice]);

Other utility functions I use in the above code are...

// Returns the vector between two vertices
vec3 vector(const vec3 *vp1, const vec3 *vp2)
{
    vec3 ret;
   ret.x = vp1->x - vp2->x;
   ret.y = vp1->y - vp2->y;
   ret.z = vp1->z - vp2->z;
    return ret;
}


// Returns the normal of two vectors
vec3 crossProduct(const vec3 *v1, const vec3 *v2)
{
   vec3 normal;
   normal.x = v1->y * v2->z - v1->z * v2->y;
   normal.y = v1->z * v2->x - v1->x * v2->z;
   normal.z = v1->x * v2->y - v1->y * v2->x;

   return normal;
}


// Returns the length of a vector
float vec3Length(vec3 *v1) {
   return sqrt(v1->x * v1->x + v1->y * v1->y + v1->z * v1->z);
}


// Normalizes a vector
void normalize(vec3 *v1)
{
   float len = vec3Length(v1);
   if(len < EPSILON) return;
   float inv = 1.0f / len;
   v1->x *= inv;
   v1->y *= inv;
   v1->z *= inv;
}


// Adds vector v2 to v1
vec3 vec3Add(vec3 *v1, vec3 *v2)
{
   vec3 v;

   v.x = v1->x + v2->x;
   v.y = v1->y + v2->y;
   v.z = v1->z + v2->z;

   return v;
}

Upvotes: 0

Views: 158

Answers (2)

Neil Roy
Neil Roy

Reputation: 632

After experimenting with different solutions, I discovered that my own normal generation in this post actually works extremely well, it's virtually instant and wasn't the problem. The problem seemed to be in using a large texture for the terrain. I changed the texture I used for the terrain to use a tiled texture which wouldn't get stretched so much and the graphic issue seems to have went away. It was a relief that the normal generation I posted works well as other solutions were horribly slow. This is what I ended up with and as you can see, there are no graphical problems. Plus it looks better with more detail. I wanted to post what I found out in case anyone else sees the same problem.

Lunar 3D Terrain

Upvotes: 0

1201ProgramAlarm
1201ProgramAlarm

Reputation: 32727

One problem with using the average of the face normals to compute the vertex normals is that the computed normals can be biased. For example, imagine that there is a ridge that runs north/south. One vertex on the peak of the ridge has three polygons on the east side, and two on the west. The vertex normal will be angled to the east. This can cause darker lighting at that point when the illumination is coming from the west.

A possible improvement would be to apply a weight to each face's normal, proportional to the angle that corner of the face has at that vertex, but this will not get rid of all of the bias.

Upvotes: 2

Related Questions