重新计算规则网格表面的法线 (C++/OpenGL)
Posted
技术标签:
【中文标题】重新计算规则网格表面的法线 (C++/OpenGL)【英文标题】:Recalculate normals of a regular grid surface (C++/OpenGL) 【发布时间】:2019-10-01 10:11:08 【问题描述】:我正在尝试计算网格表面的法线。 地图为 29952 像素 x 19968 像素,每个单元格为 128 像素 x 128 像素。所以我有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)。
方法一
如果我使用有限差分法计算法线,我会得到一个不错的结果。 伪代码:
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 返回与该顶点关联的顶点信息。 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 坐标)并返回 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(从小地图上看到):
注意:使用这两种方法,建筑物在不同的位置但表面具有相同的种子。
有人可以帮忙吗? :-)
编辑:
我添加了更多图片以帮助理解问题。 方法一:
方法二:
【问题讨论】:
我了解您的尝试,我可以按照您的代码进行操作。这对我来说似乎是正确的。问题可能出在getVertex
中吗? getVertex
似乎读取了与 2 维坐标相关联的高度并返回 3D 坐标。 (高度)查找是否有可能每行(行)移动 1?
@Rabbid76 我编辑了帖子,添加了getVertex
函数。
你确定(int)(y/128 * 29952 / 128 + x/128)
?如果纹理连续有 29952 个像素,那么它必须是(int)(y/128 * 29952 + x/128)
?我错了吗?
是的,因为 29952 是地图宽度,但请记住,我们每 128px 有一个顶点,所以我们在 x 轴上有 234 个顶点,在 y 轴上有 156 个顶点。
y 轴上是否有 234 个图块或顶点?
【参考方案1】:
解决了!
我在计算 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;
耶!
【讨论】:
以上是关于重新计算规则网格表面的法线 (C++/OpenGL)的主要内容,如果未能解决你的问题,请参考以下文章