0
\$\begingroup\$

I colored normals with negative y red and here's what I get:

enter image description here

Any idea why some normals are flipped?

Here's simplified version of my code that add vertices, triangles, normals, etc.

I'm playing around with Godot, so axes are are the following:

  • Vertical is y axis, up is positive
  • Horizontal is x axis, left is positive (left in image)
  • Depth is z axis, forward is positive (up in image)
public static TerrainMesh GenerateMesh(float[,] heights, Color[,] colors) {
  int width = heights.GetLength(0);
  int height = heights.GetLength(1);
  float topLeftX = (width - 1) / 2f;
  float topLeftZ = (height - 1) / 2f;

  TerrainMesh mesh = new TerrainMesh(width, height);
  int vertexIndex = 0;

  for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
      mesh.Vertices[vertexIndex] = new Vector3(topLeftX - x, heights[x, y], topLeftZ - y);
      mesh.Uvs[vertexIndex] = new Vector2(x / (float)width, y / (float)height);

      // rudimentary normals
      if (x != 0) {
        // PS! if I swap Vector3's in Cross, upper half of the map has flipped normals
        Vector3 normal = mesh.Vertices[vertexIndex].Cross(mesh.Vertices[vertexIndex - 1]).Normalized();
        mesh.Normals[vertexIndex] = normal;
      } else if (x == 1) {
        Vector3 normal = mesh.Vertices[vertexIndex - 1].Cross(mesh.Vertices[vertexIndex]).Normalized();
        mesh.Normals[vertexIndex - 1] = normal;
      }

      if (x < width - 1 && y < height - 1) {
        mesh.AddTriangle(vertexIndex, vertexIndex + width + 1, vertexIndex + width);
        mesh.AddTriangle(vertexIndex + width + 1, vertexIndex, vertexIndex + 1);
      }

      vertexIndex += 1;
    }
  }

  return mesh;
}
\$\endgroup\$
1
  • \$\begingroup\$ I also get some normals artifacts in the border where normals flip. \$\endgroup\$ – Sir May 1 '20 at 13:12
2
\$\begingroup\$

The method of computing normals shown here is beyond "rudimentary" - it's just flat out wrong. What you're computing there in no way corresponds to a terrain normal.

It's taking two vectors from the origin to different vertices in your mesh - which don't always belong to the same mesh triangle, even - and computing a normal of the triangle they form with the origin. Naturally, when you cross the origin, this triangle - which has nothing to do with the triangles of the mesh you're drawing - flips over, resulting in the inverted normals you see for half your mesh.

Try something like this instead:

Vector3 GetNormal(int x, int y, float[,] heights, float stepSize = 1.0f) {
    float center = heights[x, y];

    float left  = x > 0                        ? heights[x-1, y], center;
    float right = x < heights.GetLength(0) - 1 ? heights[x+1, y], center;

    float bottom = y > 0                        ? heights[x, y-1], center;
    float top    = y < heights.GetLength(1) - 1 ? heights[x, y+1], center;

    return Vector3.Normalize(new Vector3(left - right, 2 * stepSize, bottom - top)
}

The stepSize variable corresponds to how great a distance there is between adjacent cells of your heightmap grid. Smaller values will exaggerate the normals, while larger values will tend to pull the normals closer to vertical.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.