我正在尝试通过减少三角形数量同时尽可能多地保留细节来优化我的地形。减少效果很好,我将顶点数减少了 5 分而没有太多视觉损失。这个新的非对称网格的法线计算存在问题。
我有每个顶点的法线,这里是计算法线的片段:
private void calcNormal(Vector<Triangle_dt> triangles, Point_dt point) {
Vec3 normal = new Vec3(0, 0, 0);
for (Triangle_dt triangle : triangles) {
Vec3 a = getPos(triangle.p1());
Vec3 b = getPos(triangle.p2());
Vec3 c = getPos(triangle.p3());
Vec3 AB = b.subtract(a);
Vec3 AC = c.subtract(a);
normal = normal.add(AB.cross(AC));
}
setNormal(point, normal.getUnitVector());
}
其中三角形是连接到顶点(点)的三角形。我将所有的三角形法线加在一起(没有归一化以使最终 vector 由三角形面积加权),然后最终归一化最终结果。
我相信计算是正确的,但结果中有恼人的伪像(它是用定向光点亮的):
如您所见,顶点稀疏处存在不需要的线。这是由于小簇的点靠近在一起但远离下一组点(见下图)。知道如何防止这种情况吗?这是点渲染的相同 View :
最佳答案
感谢 ybungalobill,我做了以下工作以使其工作:
- 使用以下代码从原始高度图(对称网格)创建法线贴图:
从高度图计算法线
// Calculating normals from height map
public void calcNormals() {
Vec3 up = new Vec3(0, 1, 0);
float sizeFactor = 1.0f / (8.0f * cellSize);
normals = new Vec3[rows * cols];
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
Vec3 normal = up;
if (col > 0 && row > 0 && col < cols - 1 && row < rows - 1) {
float nw = getValue(row - 1, col - 1);
float n = getValue(row - 1, col);
float ne = getValue(row - 1, col + 1);
float e = getValue(row, col + 1);
float se = getValue(row + 1, col + 1);
float s = getValue(row + 1, col);
float sw = getValue(row + 1, col - 1);
float w = getValue(row, col - 1);
float dydx = ((ne + 2 * e + se) - (nw + 2 * w + sw)) * sizeFactor;
float dydz = ((sw + 2 * s + se) - (nw + 2 * n + ne)) * sizeFactor;
normal = new Vec3(-dydx, 1.0f, -dydz).getUnitVector();
}
normals[row * cols + col] = normal;
}
}
}
从法线创建图像
public static BufferedImage getNormalMap(Terrain terrain) {
Vec3[] normals = terrain.getNormals();
float[] pixels = new float[normals.length * 3];
for (int i = 0; i < normals.length; i++) {
Vec3 normal = normals[i];
float x = (1.0f + normal.x) * 0.5f;
float y = (1.0f + normal.y) * 0.5f;
float z = (1.0f + normal.z) * 0.5f;
pixels[i * 3] = x * MAX;
pixels[i * 3 + 1] = y * MAX;
pixels[i * 3 + 2] = z * MAX;
}
BufferedImage img = new BufferedImage(cols, rows, BufferedImage.TYPE_INT_RGB);
WritableRaster imgRaster = img.getRaster();
imgRaster.setPixels(0, 0, cols, rows, pixels);
return img;
}
- 在片段着色器中应用图像并使用来自顶点着色器的顶点位置计算纹理坐标:
片段着色器的一部分:
void main() {
vec3 newNormal = texture(normalMap, vec2(worldPos0.x / maxX, worldPos0.z / maxZ)).xyz;
newNormal = (2.0 * newNormal) - 1.0;
outputColor = calcColor(normalize(newNormal));
}
结果如下:
与点渲染相同的 View :
换句话说:顶点很少但视觉上细节丰富的地形
关于java - 计算地形网格上的法线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44120220/