Search code examples
c++openglglm-mathvertex-shadernormals

Recalculate normals of a regular grid surface (C++/OpenGL)


I'm trying to calculate normals of my grid surface. The map is 29952px x 19968px and each cell is 128px x 128px. So I have 36895 vertices.

Grid:
Grid

My flat map array is sent to shaders with the following structure:

float vertices[368950] = {

  //  x     y     z    znoise   xTex  yTex  xNorm yNorm zNorm Type
    16384,16256,-16256,   0,    0.54, 0.45,   0,    0,   1,    1,
    16256,16384,-16384,   0,    0.54, 0.45,   0,    0,   1,    1,
    ......
} 

I calculate the zNoise with a function

float noise(float x, float y){};

And it works (I add it to y and z in the vertex shader).

Method 1

If i calculate normals using finite-difference method i obtain a nice result. Pseudo-Code:

  vec3 off = vec3(1.0, 1.0, 0.0);
  float hL = noise(P.xy - off.xz);
  float hR = noise(P.xy + off.xz);
  float hD = noise(P.xy - off.zy);
  float hU = noise(P.xy + off.zy);
  N.x = hL - hR;
  N.y = hD - hU;
  N.z = 2.0;
  N = normalize(N);

But, in the case I need to edit the map manually, for example in a Editor context, where you set the zNoise with a tool to create mountains as you want, this method won't help.

I get this nice result (seen from minimap) (Normals are quite dark purposely):
nice result

Method 2

  |    |    |
--6----1----+-
  |\   |\   |      Y
  | \  | \  |      ^
  |  \ |  \ |      |
  |   \|   \|      |
--5----+----2--    +-----> X
  |\   |\   |
  | \  | \  |
  |  \ |  \ |
  |   \|   \|
--+----4----3--
  |    |    |

So i'm trying to calculate the normal using the adjacent triangles, but the result is very different (it seems that there's a bug somewhere):

Code.

getVertex() is a function that takes x and y returns the vertex info associated to that vertex. VerticesPos is an 1d array which contains the position of each vertex, in order to be able to get the information from vertices (the one that i described above, with 10 values per vertex). i decided to edit the y and the z in the vertex shader to keep untouched the x and y and to use them for indexing the vertices through VerticesPos. (i hope it's quite clear).

glm::vec3 getVertex(int x, int y) {

    int j = VerticesPos[(int)(y/128 * 29952 / 128 + x/128)];

    float zNoise = vertices[j * 10 + 3] * 2;
    float x1 = vertices[j * 10];
    float y1 = vertices[j * 10 + 1] + zNoise;
    float z1 = vertices[j * 10 + 2] + zNoise;

    return glm::vec3(x1, y1, z1);
}

getAdjacentVertices() is a function that takes a vec2d (x and y coords) and returns the 6 adjacent vertices, ordered clockwise

std::array<glm::vec3, 6> getAdjacentVertices(glm::vec2 pos) {
    std::array<glm::vec3, 6> output;
    output = {
        getVertex(pos.x, pos.y + 128), // up
        getVertex(pos.x + 128, pos.y), // right 
        getVertex(pos.x + 128, pos.y - 128), // down-right
        getVertex(pos.x, pos.y - 128), // down
        getVertex(pos.x - 128, pos.y), // left
        getVertex(pos.x - 128, pos.y + 128), // up-left

    };
    return output;
}

And the last function that does the job:

glm::vec3 mapgen::updatedNormals(glm::vec2 pos) {

    bool notBorderLineX = pos.x > 128 && pos.x < 29952 - 128;
    bool notBorderLineY = pos.y > 128 && pos.y < 19968 - 128;

    if (notBorderLineX && notBorderLineY) {

        glm::vec3 a = getVertex(pos.x, pos.y);

        std::array<glm::vec3, 6> adjVertices = getAdjacentVertices(pos);
        glm::vec3 sum(0.f);

        for (int i = 0; i < 6; i++) {
            int j;
            (i == 0) ? j = 5 : j = i - 1;

            glm::vec3 side1 = adjVertices[i] - a;
            glm::vec3 side2 = adjVertices[j] - a;

            sum += glm::cross(side1, side2);
        }
        return glm::normalize(sum);
    }

    else {
        return glm::vec3(0.3333f);
    }
}

I get this bad result (seen from minimap) unfortunately:
bad result

Note: The buildings are in different positions but the surface has the same seed using the two methods.

Could anyone help? :-)


EDIT:

I add more images to help understanding the problem. Method 1: Image

Method 2: Image2


Solution

  • SOLVED!

    I was calculating the normals at the same time I was computing the zNoise. So, it was possible that some vertices weren't at the right Z.

    I solved calculating before all the Z, and then all the normals. I just want to put some code:

    struct Triangle {
        glm::vec3 a, b, c;
    };
    
    std::array<Triangle, 6> getAdjacentTriangles(glm::ivec2 pos) {
        int gap = 128;
        std::array<Triangle, 6> triangles;  
        triangles[0] = {
            getVertex(pos.x, pos.y), getVertex(pos.x - gap, pos.y),getVertex(pos.x - gap, pos.y + gap),
        };
        triangles[1] = {
            getVertex(pos.x, pos.y), getVertex(pos.x - gap, pos.y + gap), getVertex(pos.x, pos.y + gap)
        };
        triangles[2] = {
            getVertex(pos.x, pos.y), getVertex(pos.x, pos.y + gap), getVertex(pos.x + gap, pos.y)
        };
        triangles[3] = {
            getVertex(pos.x, pos.y), getVertex(pos.x + gap, pos.y), getVertex(pos.x + gap, pos.y - gap)
        };
        triangles[4] = {
            getVertex(pos.x, pos.y), getVertex(pos.x + gap, pos.y - gap),getVertex(pos.x, pos.y - gap),
        };
        triangles[5] = {
            getVertex(pos.x, pos.y), getVertex(pos.x, pos.y - gap), getVertex(pos.x - gap, pos.y)
        };
        return triangles;
    }
    
    glm::vec3 calculateTriangleNormal(Triangle T) {
        glm::vec3 N = glm::cross(T.c - T.a, T.b - T.a);
        return N;
    }
    
    void mapgen::updateNormals() {
        for (int i = 0; i < nVertices * 10; i += 10) {
            float xCoord = mapgen::MapVertices()[i];
            float yCoord = mapgen::MapVertices()[i + 1];
            glm::ivec2 pos = glm::ivec2(int(xCoord), int(yCoord));  
            if (pos.x > 128 && pos.x < 29952 - 128 && pos.y > 128 && pos.y < 19968 - 128) {
    
                std::array<Triangle, 6> adjacentTriangles = getAdjacentTriangles(pos);
                glm::vec3 sum(0.f);
    
                for (int i = 0; i < 6; i++) {
                    glm::vec3 normal = calculateTriangleNormal(adjacentTriangles[i]);
                    sum += normal;
                }
                glm::vec3 N = glm::normalize(sum);
    
                mapgen::MapVertices()[i + 6] = N.x;
                mapgen::MapVertices()[i + 7] = N.y;
                mapgen::MapVertices()[i + 8] = N.z;
            }
            else {
                mapgen::MapVertices()[i + 6] = 0.f;
                mapgen::MapVertices()[i + 7] = 0.f;
                mapgen::MapVertices()[i + 8] = 1.f;
            }
        }
    }
    

    Yaaaayy!