【问题标题】:Coloring heightmap faces instead of vertices着色高度图面而不是顶点
【发布时间】:2016-09-27 00:34:56
【问题描述】:

我正在尝试创建一个按面而不是顶点着色的高度图。例如,这是我目前拥有的:

但这就是我想要的:

我读到我必须将每个顶点拆分为多个顶点,然后分别为三角形索引每个顶点。我也知道搅拌机的模型有这样的功能(分割顶点,还是什么?),但我不确定我会遵循什么样的算法。这将是最后的手段,因为除了颜色之外无缘无故地增加网格中的顶点数量似乎效率不高。

我还发现了一种叫做平面着色的东西(在着色器中对像素颜色使用 flat 限定符),但它似乎只绘制正方形而不是三角形。有没有办法让它阴影三角形?

作为参考,这是我当前的高度图生成代码:

public class HeightMap extends GameModel {

private static final float START_X = -0.5f;
private static final float START_Z = -0.5f;
private static final float REFLECTANCE = .1f;

public HeightMap(float minY, float maxY, float persistence, int width, int height, float spikeness) {
    super(createMesh(minY, maxY, persistence, width, height, spikeness), REFLECTANCE);
}

protected static Mesh createMesh(final float minY, final float maxY, final float persistence, final int width,
        final int height, float spikeness) {
    SimplexNoise noise = new SimplexNoise(128, persistence, 2);// Utils.getRandom().nextInt());

    float xStep = Math.abs(START_X * 2) / (width - 1);
    float zStep = Math.abs(START_Z * 2) / (height - 1);

    List<Float> positions = new ArrayList<>();
    List<Integer> indices = new ArrayList<>();

    for (int z = 0; z < height; z++) {
        for (int x = 0; x < width; x++) {
            // scale from [-1, 1] to [minY, maxY]
            float heightY = (float) ((noise.getNoise(x * xStep * spikeness, z * zStep * spikeness) + 1f) / 2
                    * (maxY - minY) + minY);

            positions.add(START_X + x * xStep);
            positions.add(heightY);
            positions.add(START_Z + z * zStep);

            // Create indices
            if (x < width - 1 && z < height - 1) {
                int leftTop = z * width + x;
                int leftBottom = (z + 1) * width + x;
                int rightBottom = (z + 1) * width + x + 1;
                int rightTop = z * width + x + 1;

                indices.add(leftTop);
                indices.add(leftBottom);
                indices.add(rightTop);

                indices.add(rightTop);
                indices.add(leftBottom);
                indices.add(rightBottom);
            }
        }
    }

    float[] verticesArr = Utils.listToArray(positions);
    Color c = new Color(147, 105, 59);
    float[] colorArr = new float[positions.size()];
    for (int i = 0; i < colorArr.length; i += 3) {
        float brightness = (Utils.getRandom().nextFloat() - 0.5f) * 0.5f;
        colorArr[i] = (float) c.getRed() / 255f + brightness;
        colorArr[i + 1] = (float) c.getGreen() / 255f + brightness;
        colorArr[i + 2] = (float) c.getBlue() / 255f + brightness;
    }
    int[] indicesArr = indices.stream().mapToInt((i) -> i).toArray();

    float[] normalArr = calcNormals(verticesArr, width, height);

    return new Mesh(verticesArr, colorArr, normalArr, indicesArr);
}

private static float[] calcNormals(float[] posArr, int width, int height) {
    Vector3f v0 = new Vector3f();
    Vector3f v1 = new Vector3f();
    Vector3f v2 = new Vector3f();
    Vector3f v3 = new Vector3f();
    Vector3f v4 = new Vector3f();
    Vector3f v12 = new Vector3f();
    Vector3f v23 = new Vector3f();
    Vector3f v34 = new Vector3f();
    Vector3f v41 = new Vector3f();
    List<Float> normals = new ArrayList<>();
    Vector3f normal = new Vector3f();
    for (int row = 0; row < height; row++) {
        for (int col = 0; col < width; col++) {
            if (row > 0 && row < height - 1 && col > 0 && col < width - 1) {
                int i0 = row * width * 3 + col * 3;
                v0.x = posArr[i0];
                v0.y = posArr[i0 + 1];
                v0.z = posArr[i0 + 2];

                int i1 = row * width * 3 + (col - 1) * 3;
                v1.x = posArr[i1];
                v1.y = posArr[i1 + 1];
                v1.z = posArr[i1 + 2];
                v1 = v1.sub(v0);

                int i2 = (row + 1) * width * 3 + col * 3;
                v2.x = posArr[i2];
                v2.y = posArr[i2 + 1];
                v2.z = posArr[i2 + 2];
                v2 = v2.sub(v0);

                int i3 = (row) * width * 3 + (col + 1) * 3;
                v3.x = posArr[i3];
                v3.y = posArr[i3 + 1];
                v3.z = posArr[i3 + 2];
                v3 = v3.sub(v0);

                int i4 = (row - 1) * width * 3 + col * 3;
                v4.x = posArr[i4];
                v4.y = posArr[i4 + 1];
                v4.z = posArr[i4 + 2];
                v4 = v4.sub(v0);

                v1.cross(v2, v12);
                v12.normalize();

                v2.cross(v3, v23);
                v23.normalize();

                v3.cross(v4, v34);
                v34.normalize();

                v4.cross(v1, v41);
                v41.normalize();

                normal = v12.add(v23).add(v34).add(v41);
                normal.normalize();
            } else {
                normal.x = 0;
                normal.y = 1;
                normal.z = 0;
            }
            normal.normalize();
            normals.add(normal.x);
            normals.add(normal.y);
            normals.add(normal.z);
        }
    }
    return Utils.listToArray(normals);
}

}

编辑

我尝试过做几件事。我尝试用平面阴影重新排列索引,但这并没有给我想要的外观。我尝试使用 uniform vec3 colors 并使用 gl_VertexID 或 gl_InstanceID 对其进行索引(我不完全确定差异),但我无法编译数组。 顺便说一句,Here 是 github 仓库。

【问题讨论】:

    标签: java algorithm opengl shader lwjgl


    【解决方案1】:

    flat 合格的片段着色器输入将接收相同原语的相同值。在你的情况下,是一个三角形。

    当然,三角形是由 3 个顶点组成的。而如果顶点着色器输出 3 个不同的值,片段着色器如何知道要获取哪个值?

    这归结为所谓的“provoking vertex”。渲染时,您指定要在 glDraw* 调用中使用的特定原语(GL_TRIANGLE_STRIPGL_TRIANGLES 等)。这些图元类型将根据您提供的顶点数生成许多基本图元(即:单个三角形)。

    当生成基本图元时,该基本图元中的一个顶点被称为“激发顶点”。用于所有flat 参数的是该顶点的数据。

    您之所以看到您所看到的,是因为两个相邻的三角形恰好使用了相同的激发顶点。您的网格是平滑的,因此两个相邻的三角形共享 2 个顶点。您的网格生成恰好是生成一个网格,以便每个三角形的激发顶点在它们之间共享。这意味着两个三角形将获得相同的flat 值。

    您需要调整索引列表或更改网格生成,以免发生这种情况。或者您可以将您的网格划分为单独的三角形;这可能要容易得多。

    【讨论】:

    • 您知道如何将网格划分为单独的三角形吗?
    • @Kyranstar 使用几何着色器并为每 4 个顶点 (QUAD) 或每个新的 2 个顶点 (QUAD_STRIP) 发射 2x 3 个顶点 (2x TRIANGLE) 或重写您的网格渲染例程直接使用TRIANGLE 原语。此外,由于此答案所描述的内容,您应该计算发出的基元的法线以覆盖平滑和复制法线...
    • @Spektre OpenGL 中的几何着色器据说非常慢,并且使用一个从四边形制作三角形的效率非常低。
    【解决方案2】:

    作为最后的手段,我只是复制了顶点,它似乎可以工作。我无法对其进行分析以查看它是否会导致性能大幅下降。我愿意接受任何其他建议!

        for (int z = 0; z < height; z++) {
            for (int x = 0; x < width; x++) {
                // scale from [-1, 1] to [minY, maxY]
                float heightY = (float) ((noise.getNoise(x * xStep * spikeness, z * zStep * spikeness) + 1f) / 2
                        * (maxY - minY) + minY);
    
                positions.add(START_X + x * xStep);
                positions.add(heightY);
                positions.add(START_Z + z * zStep);
                positions.add(START_X + x * xStep);
                positions.add(heightY);
                positions.add(START_Z + z * zStep);
            }
        }
        for (int z = 0; z < height - 1; z++) {
            for (int x = 0; x < width - 1; x++) {
                int leftTop = z * width + x;
                int leftBottom = (z + 1) * width + x;
                int rightBottom = (z + 1) * width + x + 1;
                int rightTop = z * width + x + 1;
    
                indices.add(2 * leftTop);
                indices.add(2 * leftBottom);
                indices.add(2 * rightTop);
    
                indices.add(2 * rightTop + 1);
                indices.add(2 * leftBottom + 1);
                indices.add(2 * rightBottom + 1);
            }
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-11-07
      • 1970-01-01
      • 2013-05-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多