计算地形网格上的法线

时间:2017-05-22 18:57:10

标签: java opengl 3d normals

我试图通过减少三角形数量来优化我的地形,同时保持尽可能多的细节。减少工作正常,我将顶点的数量减少了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());
}

其中三角形是连接到顶点(点)的三角形。我将所有三角形法线加在一起(不进行标准化以使最终向量按三角形区域加权),然后最终对最终结果进行标准化。

我相信计算是正确的,但结果中有令人讨厌的瑕疵(用方向灯照亮):

Unwanted lines where the vertices are sparse

正如您可以看到顶点稀疏的不需要的线条。它是由于小点聚集在一起但远离下一组点而引起的(见下图)。怎么预防这个?这里的点渲染视图相同:

Same view with point rendering

1 个答案:

答案 0 :(得分:2)

感谢ybungalobill,我做了以下工作让它发挥作用:

  1. 使用以下代码从原始地图(对称网格)创建法线贴图:
  2. 从高度图计算法线

    // 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;
    }
    
    1. 在片段着色器中应用图像,并使用顶点着色器中的顶点位置计算纹理坐标:
    2. 片段着色器的一部分:

      void main() {
          vec3 newNormal = texture(normalMap, vec2(worldPos0.x / maxX, worldPos0.z / maxZ)).xyz;
          newNormal = (2.0 * newNormal) - 1.0;
          outputColor = calcColor(normalize(newNormal));
      }
      

      结果如下:

      enter image description here

      点渲染的相同视图:

      enter image description here

      换句话说:几个顶点但视觉上高细节的地形