# OpenGL Terrain Lighting Per Vertex - Looks Terrible

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

## Recommended Posts

I made a height map loader, and then calculated normals and turned on OpenGL lighting. When I do lighting per triangle it looks just like you would expect. When I do lighting per vertex, it looks like crap. Maybe someone has seen this before and help me out. I've checked everything I can think to check and it all seems right.
Here's some screen shots:
Height Coloring

Per Triangle Lighting

Per Vertex Lighting

Per Vertex Lighting + Normals

I appreciate it,
David

edit: ps. Sorry about the wide images, but I think you can see better that way.

##### Share on other sites
It looks like a bug in the way you average vertex normals. Can you post the relevant code snippet?

##### Share on other sites
It will help to see this:
std::vector< std::vector<float> > heightMap_;std::vector< std::vector< std::vector<Vec3f> > > triangleNormals_;

It is important that you understand how I am using triangle Normals_. It is a 3D vector, where its 2D components represent the vertex. The 3rd component only has a size of 3. So, triangleNormals_ holds 3 normals for each vertex: one for each triangle that makes up the square to its(the vertex) upper right, and the third normal is the vertex normal. So when I render using 1 normal per triangle I use the firsts 2 normals, and when I render using per vertex lighting I use the 3rd normal. If this is too difficult to understand I will draw a picture :)

Here I initialize heightMap_ which is a 2D vector of floats
    hmImage_.loadTextureFromFile(source);    if (!hmImage_.isReady()) {        std::cout << "Failed to load terrain image. Quitting." << std::endl;        return false;    }    heightMap_.resize(hmImage_.getHeight());    for (unsigned int i = 0; i < heightMap_.size(); i++) {        heightMap_.resize(hmImage_.getWidth());    }    for (int i = 0; i < hmImage_.getHeight(); i++) {        for (int j = 0; j < hmImage_.getWidth(); j++) {            heightMap_[j] = (float)hmImage_.getData()[j + hmImage_.getWidth()*i];        }    }

Then I compute a normal for each triangle:
void Terrain::computeTriangleNormals(){    triangleNormals_.resize(hmImage_.getHeight());    for (unsigned int i = 0; i < triangleNormals_.size(); i++) {        triangleNormals_.resize(hmImage_.getWidth());        for (unsigned int j = 0; j < triangleNormals_.size(); j++) {            triangleNormals_[j].resize(3);        }    }    for (unsigned int i = 0; i < triangleNormals_.size() - 1; i++) {        for (unsigned int j = 0; j < triangleNormals_.size() - 1; j++) {            Vec3f t1(1.0f * MAP_SCALE_AREA, (heightMap_[i+1][j+1] - heightMap_[j])*MAP_SCALE_HEIGHT, 1.0f * MAP_SCALE_AREA);            Vec3f t2(1.0f * MAP_SCALE_AREA, (heightMap_[i+1][j] - heightMap_[j])*MAP_SCALE_HEIGHT, 0.0f);            triangleNormals_[j][0] = t1.cross(t2);            Vec3f t3(0.0f, (heightMap_[j+1] - heightMap_[j])*MAP_SCALE_HEIGHT, 1.0f * MAP_SCALE_AREA);            Vec3f t4(1.0f * MAP_SCALE_AREA, (heightMap_[i+1][j+1] - heightMap_[j])*MAP_SCALE_HEIGHT, 1.0f * MAP_SCALE_AREA);            triangleNormals_[j][1] = t3.cross(t4);        }    }}

Lastly I compute the vertex normals:
void Terrain::computeVertexNormals(){    Vec3f t1;    //gooey center    for (unsigned int i = 1; i < triangleNormals_.size() - 1; i++) {        for (unsigned int j = 1; j < triangleNormals_.size() - 1; j++) {            t1 = triangleNormals_[j][0] + triangleNormals_[j][1] + triangleNormals_[i-1][j-1][0] //            + triangleNormals_[i-1][j-1][1] + triangleNormals_[i-1][j][0] + triangleNormals_[j-1][1];            triangleNormals_[j][2] = t1;        }    }    //Chewy Edges    for (unsigned int i = 1; i < triangleNormals_.size() - 1; i++) {        for (unsigned int j = 0; j < triangleNormals_.size(); j += triangleNormals_.size() - 1) {            if (j == 0) {                t1 = triangleNormals_[j][0] + triangleNormals_[j][1] + triangleNormals_[i-1][j][0];                triangleNormals_[j][2] = t1;            }            else {                t1 = triangleNormals_[i-1][j-1][0] + triangleNormals_[i-1][j-1][1] + triangleNormals_[j-1][1];                triangleNormals_[j][2] = t1;            }        }    }    for (unsigned int j = 1; j < triangleNormals_.size() - 1; j++) {        for (unsigned int i = 0; i < triangleNormals_.size(); i += triangleNormals_.size() - 1) {            if (i == 0) {                t1 = triangleNormals_[j][0] + triangleNormals_[j][1] + triangleNormals_[j-1][1];                triangleNormals_[j][2] = t1;            }            else {                t1 = triangleNormals_[i-1][j-1][0] + triangleNormals_[i-1][j-1][1] + triangleNormals_[i-1][j][0];                triangleNormals_[j][2] = t1;            }        }    }    //Chrunchy Corners    t1 = triangleNormals_[0][0][0] + triangleNormals_[0][0][1];    triangleNormals_[0][0][2] = t1;    t1 = triangleNormals_[0][triangleNormals_[0].size()-2][1];    triangleNormals_[0][triangleNormals_[0].size()-1][2] = t1;    t1 = triangleNormals_[triangleNormals_.size()-2][0][0];    triangleNormals_[triangleNormals_.size()-1][0][2] = t1;    t1 = triangleNormals_[triangleNormals_.size()-2][triangleNormals_[0].size()-2][0] //    + triangleNormals_[triangleNormals_.size()-2][triangleNormals_[0].size()-2][1];    triangleNormals_[triangleNormals_.size()-1][triangleNormals_[0].size()-1][2] = t1;}

Also Vec3f is a 3d vector class. If it will help you to see it, then I will post it.

Thanks!
edit: ps. Again, sorry for really screwing over the width of this page. That line of code is way too long, hah.

##### Share on other sites
OK, there no way anybody could follow this. Hopefully this will help:

##### Share on other sites
Your results look correct. The "seams" are due to the fact that a given terrain quad is made up of 2 triangles, each of which only have access to 3 of the four vertices, thus interpolation errors occur when there is a large enough discrepancy between the tri's 3 verts and the inaccessible fourth vert. Sorry, that's a rubbish way of explaining it but it's late over here, I'll try again in the morning.

##### Share on other sites
Quote:
 Original post by JackTheRapperYour results look correct. The "seams" are due to the fact that a given terrain quad is made up of 2 triangles, each of which only have access to 3 of the four vertices, thus interpolation errors occur when there is a large enough discrepancy between the tri's 3 verts and the inaccessible fourth vert.
The simplest way to fix this, is to derive your normal directly from the heightmap (via central distance, or similar), rather than from the triangles.

In fact, I would highly recommend that you use a much higher-resolution heightmap than you use vertex grid, and produce from that a similarly high-resolution normal map, allowing you to display much more detail.

##### Share on other sites
Quote:
 Original post by JackTheRapperYour results look correct. The "seams" are due to the fact that a given terrain quad is made up of 2 triangles, each of which only have access to 3 of the four vertices, thus interpolation errors occur when there is a large enough discrepancy between the tri's 3 verts and the inaccessible fourth vert. Sorry, that's a rubbish way of explaining it but it's late over here, I'll try again in the morning.

This actually makes a lot of sense to me, I guess I wasn't really understanding how openGL colors things, or blends colors, or something. I thought if you had two triangles that share an edge (they share two vertices) then along that edge each triangle would have the same color (no seam). To me it seems strange that the other two vertices could affect that. Thanks for bringing it to my attention. I guess I should really read up on how that works.

Quote:
 Original post by swiftcoderThe simplest way to fix this, is to derive your normal directly from the heightmap (via central distance, or similar), rather than from the triangles.In fact, I would highly recommend that you use a much higher-resolution heightmap than you use vertex grid, and produce from that a similarly high-resolution normal map, allowing you to display much more detail.

When I was searching I read something about calculating normals directly from the height map, but I don't remember where that was and I didn't think much of it at the time. I can't really think (thought I haven't given it a lot of thought yet) how exactly you could calculate normals from a height field. It seems like you still need to create surfaces and calculate their normals.

Maybe you could point me in the right direction, please.

Thank you both very much.

EDIT: Never mind. I just needed a little bit more time to think about it. I think I know what I want to try now. If it doesn't work out I'll let you know.
You guys are awesome!

##### Share on other sites
Try this:
when finnally displaying your terrain, and when colouring your terrain,
do this:

glColor3f ( <your terrain data x >/255.0f, <your terrain data y >/255.0f, <your terrain data z >/255.0f );
...
.....

that should work.

##### Share on other sites
I am very disappointed to report that what I tried did not fix the problem. This time I took each point in the height field and draw a vector from it to each adjacent point on the height field (in including diagonals). This produces eight triangles with a vertex at the point of interest. I calculate the cross product of each of those triangles and add them all together and then normalize. This means that every vertex normal is taking into account the 8 vertices around it.

I didn't not change the triangle pattern (maybe that's the problem) and it looks exactly the same as before. Do you recommend that I draw triangles in a different pattern? or is this one acceptable?

Maybe I should take into account more than just the 8 nearest vertices?

HELP, I'm going crazy.

##### Share on other sites
Quote:
 Original post by GMA965_X3100Try this:when finnally displaying your terrain, and when colouring your terrain,do this:glColor3f ( /255.0f, /255.0f, /255.0f );........that should work.

I don't really understand why I should do this. This will make the map black at the origin and lighter the further a point is from the origin?

Or maybe you mean something different than I am thinking when you say x data and z data.

In the first picture I posted the coloring was done like this:
glColor3f ( <your terrain data y >/255.0f, <your terrain data y >/255.0f, <your terrain data y >/255.0f );

1. 1
Rutin
25
2. 2
JoeJ
20
3. 3
4. 4
5. 5

• 9
• 9
• 46
• 41
• 23
• ### Forum Statistics

• Total Topics
631749
• Total Posts
3002053
×