【问题标题】:Strange result from perlin noise generator柏林噪声发生器的奇怪结果
【发布时间】:2014-12-24 01:38:48
【问题描述】:

我一直在为我想要编程的游戏尝试使用 perlin noie 生成,但我遇到了一些奇怪的结果。当我让我的代码运行并以灰度绘制结果时,我得到以下图像:

我的代码基于关于 perlin noise 的维基百科页面和其中一个参考页面(下面的链接)。假设基于预生成的半随机向量网格为每个点生成 perlin 噪声点。

public static double[][] generateMap(long seed, int width, int height) {
    double[][] map = new double[width][height];
    double[][][] grid = generateGrid(seed, (int) (width/10)+1, (int) (height/10)+1);
    double max = Double.MIN_VALUE;
    for (int i = 0; i < map.length; i++) {
        for (int j = 0; j < map[0].length; j++) {
            double p = perlinNoise(((double) i) / 10, ((double) j) / 10, grid);
            map[i][j] = p;
            max = (max < p) ? p : max;
        }
    }
    for (int i = 0; i < map.length; i++) {
        for (int j = 0; j < map[0].length; j++) {
            map[i][j] = map[i][j] / max;
        }
    }
    return map;
}

private static double perlinNoise(double x, double y, double[][][] grid) {
    int x0 = ((x < 0) ? (int) x-1 : (int) x);
    int y0 = ((y < 0) ? (int) y-1 : (int) y);
    int x1 = x0 + 1;
    int y1 = y0 + 1;

    double tx = x - (double)x0;
    double ty = y - (double)y0;
    double wx = tx*tx*(3-2*tx);
    double wy = ty*ty*(3-2*ty);

    double l0, l1, ix, iy, p;
    l0 = dotGrid(x0, y0, x, y, grid);
    l1 = dotGrid(x1, y0, x, y, grid);
    ix = cerp(l0, l1, wx);
    l0 = dotGrid(x0, y1, x, y, grid);
    l1 = dotGrid(x1, y1, x, y, grid);
    iy = cerp(l0, l1, wx);
    p = cerp(ix, iy, wy);

    return p;
}

private static double lerp(double a, double b, double w) {
    return (1.0f - w)*a + w*b;
}

private static double cerp(double a, double b, double w) {
    double ft = w * 3.1415927;
    double f = (1 - Math.cos(ft)) * 0.5;
    return  a*(1-f) + b*f;
}

private static double dotGrid(int i, int j, double x, double y, double[][][] grid) {
    double dx = x - i;
    double dy = y - j;
    return (dx*grid[i][j][0] + dy*grid[i][j][1]);

}

private static double[][][] generateGrid(long seed, int width, int height) {
    Random r = new Random(seed);
    double[][][] grid = new double[width][height][2];
    for (int i = 0; i < grid.length; i++) {
        for (int j = 0; j < grid[0].length; j++) {
            double x = r.nextFloat();
            double y = r.nextFloat();
            double v = Math.sqrt((x*x) + (y*y));
            grid[i][j][0] = x/v;
            grid[i][j][1] = y/v;
        }
    }
    return grid;
}

对于那些想要测试我的代码的人,我还将包括我的渲染方法:

private void drawMap(double[][] map, Graphics g) {
    for (int i = 0; i < map.length; i++) {
        for (int j = 0; j < map[0].length; j++) {
            float d = (float) Math.abs(map[i][j]);
            d = (d > 1.0f) ? 1.0f : d;
            Color c = new Color(d, d, d);
            g.setColor(c);  
            g.fillRect(i, j, 1, 1);
        }
    }
}

我希望有人能告诉我为什么我的噪音中有这些奇怪的迷宫状结构,以及如何摆脱它们。

来源:

http://en.wikipedia.org/wiki/Perlin_noise

http://webstaff.itn.liu.se/~stegu/TNM022-2005/perlinnoiselinks/perlin-noise-math-faq.html

【问题讨论】:

    标签: java noise


    【解决方案1】:

    有两个小问题加起来可以让您看到有趣的模式。我在 C 中测试了网格大小为 21x21、输出大小为 200x200 和随机种子为 1234 的东西,但在 Java 中应该同样有效。

    我的原图对比(由于种子的不同,图案和你的基本一样):

    第一个问题在generateGrid() 中,因为您选择了初始随机值。通过使用nextFloat(),您的值被限制在 0 到 1 之间,而不是所需的 -1 到 1 范围。解决这个问题很简单:

    double x = r.nextFloat()*2.0 - 1.0;
    double y = r.nextFloat()*2.0 - 1.0;
    

    并且应该得到类似的东西:

    这是一个有趣的模式,在某些情况下可能有用,但不是“正常”的 Perlin 噪音。下一个问题是将地图值缩放为颜色。您采用的是绝对值,但我相信更好的方法是在 generateMap() 函数中向上移动和重新缩放,例如(未测试):

    double max = Double.MIN_VALUE;
    double min = Double.MAX_VALUE;
    
    for (int i = 0; i < map.length; i++) {
        for (int j = 0; j < map[0].length; j++) {
            double p = perlinNoise(((double) i) / 10, ((double) j) / 10, grid);
            map[i][j] = p;
            max = (max < p) ? p : max;
            min = (min > p) ? p : min;
        }
    }
    
    for (int i = 0; i < map.length; i++) {
        for (int j = 0; j < map[0].length; j++) {
            map[i][j] = (map[i][j] - min) / (max - min); //Limit 0 to 1
        }
    }
    

    这应该将映射值限制为 0-1 的值,这也使您的输出代码更加简单。这会产生更好的 Perlin 噪声输出:

    我认为这是“正常”的 Perlin 噪音,至少我看不到任何其他问题,也无法通过测试得到更好的结果。要获得“更好”的噪音,您必须将多个噪音频率相加。

    我在 Perlin 噪声中看到的图像颜色缩放函数的另一个噪声值是仅将 -1.0 设为黑色,将 1.0 设为白色,这会给你类似的结果:

    这与上一个基本相同,但对比度要小一些。实际上,如何将噪声值缩放为颜色取决于您。柏林噪音的一个很好的资源是LibNoise library。它是 C++,但易于阅读且资源丰富。

    【讨论】:

    • 感谢您的帮助,它显着改善了我的代码输出。然而我还不完全满足。我可能应该在我的第一篇文章中说明这一点,但我正在寻找一种更像多云的蓬松输出,而不是我现在得到的。我考虑过使用一种涉及八度音阶的方法,如下所述:freespace.virgin.net/hugo.elias/models/m_perlin.htm 但不知何故,我觉得这种方法效率低得多。
    • 是的,为了获得“云”,您必须将多个八度音阶的 Perlin 噪声加在一起。
    【解决方案2】:

    请记住,Perlin 噪声是一种较旧的算法,即使使用 uesp 提供的修复程序,通过将其所有特征与基轴和 45 度对角线对齐,仍然会显示出视觉上显着的网格伪影。在我看来,现在几乎没有理由使用 Perlin 噪声。

    看看一个叫做 OpenSimplex 噪声的东西:https://gist.github.com/KdotJPG/b1270127455a94ac5d19

    Perlin 和 OpenSimplex 之间的比较(3D 函数的 2D 切片)

    • 左边是噪声(x, y, 0) 灰度
    • 接下来是噪声(x, y, 0) > 0 ?白色:黑色
    • 接下来是|噪声(x, y, 0)| > 0.1 ?白色:黑色
    • 接下来是noise(x, y, 0.5)灰度

    请注意,OpenSimplex 噪声和 Simplex 噪声是两种不同的算法。在大多数实现中,Simplex 噪声比 OpenSimplex 具有更多的伪影,并且 3D+ 中的 Simplex 已获得专利。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-29
      • 2012-01-29
      • 1970-01-01
      • 2017-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多