Error Computing Terrain Normals

Started by
5 comments, last by Hamster 17 years, 12 months ago
I followed a tutorial here on GameDev for calculating normals for heightfields. It works as far as creating surface normals, but somehow does not manage to smooth them out, though I have the code that is supposed to do it. Any trouble you see in the code?
normals = new SimVector[width * depth];

    SimVector *sNormals = new SimVector[width * depth * 10];

    int normalIndex = 0;
    float height0, height1, height2;
    SimVector normal1, normal2, normal3, normal4, normal5, normal6;

    for(j = -1; j < depth; j++)
    {
        for(i = -1; i < width; i++)
        {
            if(!IsValidCoord(i, j) || !IsValidCoord(i + 1, j) ||
                !IsValidCoord(i, j + 1) || !IsValidCoord(i + 1, j + 1))
            {
                normalIndex += 2;
                continue;
            }

            height0 = GetHeight(i, j);
            height1 = GetHeight(i, j + 1);
            height2 = GetHeight(i + 1, j);
            normal1.x = height0 - height2;
            normal1.y = 1;
            normal1.z = height0 - height1;

            height0 = height2;
            height2 = GetHeight(i + 1, j + 1);
            normal2.x = height1 - height2;
            normal2.y = 1;
            normal2.z = height0 - height2;

			SimStructs::Normalize(normal1);
			SimStructs::Normalize(normal2);

            sNormals[normalIndex++] = normal1;
            sNormals[normalIndex++] = normal2;
        }
    }


    int triIndex = 1;
    int indexPlusOne = 2;
    int indexPlusTwo = 3;
    int vertIndex = 0;
    int rowOffset = ((width + 1) * 2) - 1;
    SimVector vertNormal;

    for(j = 0; j < depth; j++)
    {
        for(i = 0; i < width; i++)
        {
            indexPlusOne = triIndex + 1;
            indexPlusTwo = triIndex + 2;

            normal1 = sNormals[triIndex];
            normal2 = sNormals[indexPlusOne];
            normal3 = sNormals[indexPlusTwo];
            normal4 = sNormals[rowOffset + triIndex];
            normal5 = sNormals[rowOffset + indexPlusOne];
            normal6 = sNormals[rowOffset + indexPlusTwo];

            vertNormal = normal1 + normal2 + normal3 + normal4 + normal5 + normal6;
            vertNormal /= 6.0f;

			SimStructs::Normalize(vertNormal);

            normals[vertIndex] = vertNormal;

            triIndex += 2;
            vertIndex++;
        }
        triIndex += 2;
    }
	delete[] sNormals;
Advertisement
Just skimmed over it. It looks ok to me. Perhaps you've set the API to render flat shading rather than smooth shading?
I do have the shading turned on (glShadeModel(GL_SMOOTH) for OpenGL people), but I still get non-smooth normals. Here's an image:
That does look "smoothed" to me, but the problem may be the terrain its self. I cant be for sure, could you post a wireframe version of the terrain?
The "appearance" of non-smoothness is on edges that border both light and dark triangles

The reason for this (I believe) is that:

A) The diffuse color uses a straight bilinear color interpolation between vertices.

B) The specular color uses a straight bilinear normal interpolated between the vertices.

There is thus a mismatch between the interpolation types

one is interpolating color while the other is interpolating normals. Combining these two methods in an additive way is sure to create artifacts.

Fixed function pipeline with no texturing, right?

It looks like you're using a regular grid of triangles, this probably contributes to the problem.

You appear to be using this pattern:
 - -|\|\| - -|\|\| - -


You might want to use the diamond square pattern, which looks like this:

 - -|/|\| - -|\|/| - -


You'll probably have to use an index array to accomplish this.

Hopefully this helps with the problem, although there does appear to be something else amiss with that picture.

Edit: Are you sure you are calculating your normals with the correct triangles? I had a similar problem when I was calculating the normals on triangles which were not along the correct diagonal (see the \ in the top source block, you may be calculating your normals in the direction of / instead by accident).
I only had a brief skim over the code, but it looks like you're using a slightly unorthodox method to keep your normals unit length. You shouldn't normalize them until the very last step, when you've added all contributing normals at a given vertex together. When you've done that sum, then normalize, don't divide by six and don't normalise any of the vectors you take in the earlier steps. If you do it this way, which I understand is a fairly standard way to do it with smooth shading, the influence of each triangle that uses the vertex on the direction of the final vertex normal will be proportional to the area of that triangle. It should speed up the code a bit too. Good luck with it.

This topic is closed to new replies.

Advertisement