重新计算规则网格表面的法线 (C++/OpenGL)
Recalculate normals of a regular grid surface (C++/OpenGL)
我正在尝试计算网格表面的法线。
地图为 29952px x 19968px,每个单元格为 128px x 128px。所以我有 36895 个顶点。
网格:
我的平面贴图数组被发送到具有以下结构的着色器:
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,
......
}
我用函数计算 zNoise
float noise(float x, float y){};
它有效(我将它添加到顶点着色器中的 y 和 z)。
方法一
如果我使用 finite-difference 方法 计算法线,我会得到一个不错的结果。
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);
但是,在我需要手动编辑地图的情况下,例如在编辑器上下文中,您可以使用工具设置 zNoise 以根据需要创建山脉,此方法 不会帮助.
方法二
| | |
--6----1----+-
|\ |\ | Y
| \ | \ | ^
| \ | \ | |
| \| \| |
--5----+----2-- +-----> X
|\ |\ |
| \ | \ |
| \ | \ |
| \| \|
--+----4----3--
| | |
所以我尝试使用相邻的三角形来计算法线,但结果却大不相同(似乎某处存在错误):
代码。
getVertex()
是一个函数,它接受 x 和 y returns 与该顶点关联的顶点信息。 VerticesPos
是一个一维数组,其中包含 每个顶点的位置 ,以便能够从 vertices
(我上面描述的那个,每个顶点有 10 个值)。我决定在顶点着色器中编辑 y 和 z 以保持 x 和 y 不变,并将它们用于 索引顶点 到 VerticesPos
。 (我希望它很清楚)。
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()
是一个接受 vec2d(x 和 y 坐标)和 returns 6 个相邻顶点 的函数,有序 顺时针
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;
}
最后一个完成这项工作的函数:
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);
}
}
不幸的是,我得到了这个bad result(从小地图上看到):
注意:建筑物位于不同的位置,但表面使用两种方法具有相同的种子。
有人能帮忙吗? :-)
编辑:
我添加了更多图片以帮助理解问题。
方法一:
方法二:
已解决!
我在计算 zNoise 的同时计算了法线。
因此,有可能某些顶点不在正确的 Z 轴上。
我解决了所有Z之前的计算,然后是所有法线。
我只想放一些代码:
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;
}
}
}
耶耶耶!
我正在尝试计算网格表面的法线。 地图为 29952px x 19968px,每个单元格为 128px x 128px。所以我有 36895 个顶点。
网格:
我的平面贴图数组被发送到具有以下结构的着色器:
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,
......
}
我用函数计算 zNoise
float noise(float x, float y){};
它有效(我将它添加到顶点着色器中的 y 和 z)。
方法一
如果我使用 finite-difference 方法 计算法线,我会得到一个不错的结果。 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);
但是,在我需要手动编辑地图的情况下,例如在编辑器上下文中,您可以使用工具设置 zNoise 以根据需要创建山脉,此方法 不会帮助.
方法二
| | |
--6----1----+-
|\ |\ | Y
| \ | \ | ^
| \ | \ | |
| \| \| |
--5----+----2-- +-----> X
|\ |\ |
| \ | \ |
| \ | \ |
| \| \|
--+----4----3--
| | |
所以我尝试使用相邻的三角形来计算法线,但结果却大不相同(似乎某处存在错误):
代码。
getVertex()
是一个函数,它接受 x 和 y returns 与该顶点关联的顶点信息。 VerticesPos
是一个一维数组,其中包含 每个顶点的位置 ,以便能够从 vertices
(我上面描述的那个,每个顶点有 10 个值)。我决定在顶点着色器中编辑 y 和 z 以保持 x 和 y 不变,并将它们用于 索引顶点 到 VerticesPos
。 (我希望它很清楚)。
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()
是一个接受 vec2d(x 和 y 坐标)和 returns 6 个相邻顶点 的函数,有序 顺时针
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;
}
最后一个完成这项工作的函数:
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);
}
}
不幸的是,我得到了这个bad result(从小地图上看到):
注意:建筑物位于不同的位置,但表面使用两种方法具有相同的种子。
有人能帮忙吗? :-)
编辑:
我添加了更多图片以帮助理解问题。
方法一:
方法二:
已解决!
我在计算 zNoise 的同时计算了法线。 因此,有可能某些顶点不在正确的 Z 轴上。
我解决了所有Z之前的计算,然后是所有法线。 我只想放一些代码:
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;
}
}
}
耶耶耶!