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.
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).
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):
| | |
--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:
Note: The buildings are in different positions but the surface has the same seed using the two methods.
Could anyone help? :-)
I add more images to help understanding the problem.
Method 1:
Method 2:
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!