# Lighting problems with Terrain

I'm in the process of learning Open GL and am having issues with lighting on my Terrian, I don't know if the issue is related to how I calculate my normals or the shader itself (I am using the shader from the Open GL wikibook - Lighting Arc tutorial 1). I calculate the normals like this:

``````void Terrian::construct_normals()  {
terrian_normals.reserve(width * height);

for (int i = 0; i < terrian_elements.size() / 3; i++)  {
int index1 = terrian_elements.at(i * 3);
int index2 = terrian_elements.at(i * 3 + 1);
int index3 = terrian_elements.at(i * 3 + 2);

glm::vec3 side1 = terrian_vertices.at(index1) - terrian_vertices.at(index3);
glm::vec3 side2 = terrian_vertices.at(index1) - terrian_vertices.at(index2);
glm::vec3 normal = glm::cross(side1, side2);
normal = glm::normalize(normal);

terrian_normals.emplace_back(normal);
terrian_normals.emplace_back(normal);
terrian_normals.emplace_back(normal);
}

std::cout << "Terrian Vertices: " << terrian_vertices.size() << "\nTerrian Normals: " << terrian_normals.size();
}
``````

And the vertex shader can be viewed here:

``````http://pastebin.com/3vU8SHvK
``````

Here's what the terrain looks like:

If you would like me to post any more of my code I would be happy to do so.

Edit:

Vertices:

``````void Terrian::construct_vertices()  {
terrian_vertices.reserve(width * height);
terrian_colors.reserve(width * height);

std::cout << "Generating data\n";

for (int y = 0; y < height; y++)  {
for (int x = 0; x < width; x++)  {
int i = x + y * width;

terrian_vertices.emplace_back(glm::vec3(x, terrian_heightmap[y][x], -y));
terrian_colors.emplace_back(Color(0.0, 1.0, 0.0, true));
}
}
}
``````

and elements:

``````void Terrian::construct_elements()  {
terrian_elements.reserve((width - 1) * (height - 1) * 6);

for (int y = 0; y < height - 1; y++)  {
for (int x = 0; x < width - 1; x++)  {
GLushort bottom_left = x + y * width;
GLushort bottom_right = (x + 1) + y * width;
GLushort top_left = x + (y + 1) * width;
GLushort top_right = (x + 1) + (y + 1) * width;

terrian_elements.emplace_back(top_left);
terrian_elements.emplace_back(bottom_right);
terrian_elements.emplace_back(bottom_left);

terrian_elements.emplace_back(top_left);
terrian_elements.emplace_back(top_right);
terrian_elements.emplace_back(bottom_right);
}
}
}
``````
-

``````void Terrian::construct_normals()  {
terrian_normals.resize(width * height, glm::vec3(0.f)); // Resize and initialize to zero

for (int i = 0; i < terrian_elements.size() / 3; i++)  {
int index1 = terrian_elements.at(i * 3);
int index2 = terrian_elements.at(i * 3 + 1);
int index3 = terrian_elements.at(i * 3 + 2);

glm::vec3 side1 = terrian_vertices.at(index1) - terrian_vertices.at(index3);
glm::vec3 side2 = terrian_vertices.at(index1) - terrian_vertices.at(index2);
glm::vec3 normal = glm::cross(side1, side2);
normal = glm::normalize(normal); // <- this is optional

// accumulate normals for each triangle
terrian_normals.at(index1) += normal;
terrian_normals.at(index2) += normal;
terrian_normals.at(index3) += normal;
}

// normalize all the accumulated normals
for (std::size_t i=0; i<terrian_normals.size(); ++i)
terrian_normals[i]=glm::normalize(terrian_normals[i]);

std::cout << "Terrian Vertices: " << terrian_vertices.size() << "\nTerrian Normals: " << terrian_normals.size();
}
``````

This code generates point normals by "averaging" the triangle normals of the adjacent triangles. I assumed this is what you wanted anyways. Note that this is an arbitrary method to generate point normals. Strictly speaking, points to not have normals. But for lighting, it is usually fine to generate them like this - it "fakes" a smoother surface. I normally leave out the normalization of the triangle normals - it biases the final normals to the directions with greater surface (on a terrain, these would be the slopes). Just experiment with it a little.

Alternatively, you can also derive the normals by constructing partial derivatives of your heightmap. This is a lot faster, but needs special treatment on the broders.

-
Great answer! Thanks! Are there any tutorials you could recommend regarding lighting techniques and such? – Darestium Aug 6 '12 at 10:48

I assume you are using glDrawElements, and not the (deprecated) glVertex* functions. Then you have to decide, if

• you want to have 1 normal per vertex, which means you have to average the normals of all faces adjacent to the vertex, or
• you want to have 3 normals per triangle, which means you have to duplicate the vertices and store 3 normals for each face.

Currently your code looks like a mixture of both. You're adding 3 normals per terrain triangle, but your terrian_normals.reserve() call only allocates one normal for each vertex. I suspect your terrian_vertices and terrian_normals arrays have different size, and you're not properly aligning vertices with their normals during rendering.

-
Yes, I am using this: "glDrawElements(GL_TRIANGLES, size / sizeof(GLushort), GL_UNSIGNED_SHORT, 0);" to draw my terrain. – Darestium Jul 29 '12 at 0:50
How do I go about implementing your first suggestion? – Darestium Jul 29 '12 at 2:19