6

我正在编写基于 perlin 噪声的地图生成器并遇到问题:

假设我想要 30% 的水和 70% 的泥瓦。使用通常的随机生成器没有问题:

tile = rnd.nextFloat() < 0.7f ? DIRT : WATER;

但是柏林噪声是正态分布的(范围从 -1 到 1,平均为 0),所以这并不容易。

有谁知道将正态分布转换为均匀分布的方法,或者我可以从噪声值中获得百分比的不同方法?

编辑:70% 只是一个例子,我希望能够动态地使用任何值,最好是 0.1% 的精度。

EDIT2:我想将 perlin 噪声转换为均匀分布,而不是正态分布(它已经是相似的)。

4

6 回答 6

6

如果您想获得正好 30% 的水(或其他指定值),您可以这样做。

  1. 生成您的高度图。
  2. 将所有高度值放入列表中。
  3. 对列表进行排序。
  4. 选择出现在列表中30%的值作为您的水位。
于 2012-07-14T23:00:02.293 回答
4

Perlin 噪声分布只是高斯分布,它不是真正的正态分布。

此外,峰值非常窄,标准偏差在 0.1 左右(我找不到确切的数字)。

只需将您的阈值选择为 ~ 0.1,这应该会给您大约70% 的值低于该值,30% 高于该值。

于 2012-07-14T22:25:43.517 回答
4

我想出了一个解决方案:首先,我生成 100,000,000 个柏林噪声并将它们存储在一个数组中。我对其进行排序,然后我可以将每 10,000 个值作为每千分之一的阈值。现在我可以对这些阈值进行硬编码,所以我只有一个包含 1,000 个浮点数的数组,以便在运行时进行查找。

好处:

真的很快,因为它只是运行时的一个数组访问。

缺点:

如果更改算法,则必须重新生成阈值数组。其次,平均值缩放到每千分之一约 10 个,使 50% 的阈值为 49.5% 或 50.5%(取决于您是否使用 < 或 <= 比较器)。第三,增加的内存占用(4kb,每单位精度)。您可以使用百分比精度或对数精度刻度来减少它。

生成代码:

final PerlinNoiseGenerator perlin = new PerlinNoiseGenerator(new Random().nextInt());

final int size = 10000; //Size gets sqared, so it's actually 100,000,000

final float[] values = new float[size * size];
for (int x = 0; x < size; x++)
    for (int y = 0; y < size; y++) {
        final float value = perlin.noise2(x / 10f, y / 10f);
        values[x * size + y] = value;
    }
System.out.println("Calculated");

Arrays.sort(values);
System.out.println("Sorted");

final float[] steps = new float[1000];
steps[999] = 1;
for (int i = 0; i < 999; i++)
    steps[i] = values[size * size / 1000 * (i + 1)];
System.out.println("Calculated steps");

for (int i = 0; i < 10; i++) {
    System.out.println();
    for (int j = 0; j < 100; j++)
        System.out.print(steps[i * 100 + j] + "f, "); //Output usuable for array initialization
    System.out.println();
    System.out.println();
}

查找代码:

public final static float[] perlinThresholds = new float[]{}; //Initialize it with the generated thresholds.

public static float getThreshold(float percent) {
    return perlinThresholds[(int)(percent * 1000)];
}

public static float getThreshold(int promill) {
    return perlinThresholds[promill];
}

X
于 2012-07-15T12:15:14.780 回答
4

这是一个分析解决方案,它不依赖于保留数据,并且是连续的。按照这里描述的方法,我生成了一个柏林噪声值的直方图,然后通过对直方图求和来近似连续分布函数,所以cdf(x)

cdf(x) = sum(histogram[i] for all i < x)

然后我用 Wolfram Alphacdf(x)用五次多项式逼近。这给了我这个功能:

function F(x) { return (((((0.745671 * x + 0.00309887) * x - 1.53841) * x - 0.00343488) * x + 1.29551) * x) + 0.500516;

x^5+0.00309887 x^4-1.53​​841 x^3-0.00343488 x^2+1.29551 x+0.500516 u = (u + 0.002591009999999949) / 1.0055419999999997; // 交叉 (0,0) 和 (1,1)

F(x) = 0.745671 x^5 + 0.00309887 x^4 - 1.53841 x^3 - 0.00343488 x^2 + 1.29551 x + 0.500516

现在F(perlin.noise2(...))相当接近均匀分布。

此函数并没有完全通过点(-1,0)(1,1)因此您可以将其更正为

F1(x) = (F(x) + 0.002591009999999949) / 1.0055419999999997

该函数也偏离 1 nearx = 1和 0 near 之下x = -1,因此如果这对您很重要,您应该将其限制在 0 和 1 之间。

F2(x) = max(min(F1(x), 1), 0)

(除非出现想要更多细节的人,否则我将保持简洁。如果是这样,请发表评论。)

于 2014-11-11T06:01:33.003 回答
0

只需加 1 除以 2 ?这将为您提供以 0.5 和从 0 到 1 为中心的分布。

编辑:您想要一个阈值拆分 70% 与 30%,您必须使用正态法的累积分布函数,您只需找到 x,例如低于 x 的概率为 0.7。请注意,这仅适用于正态分布,如果您的分布始终介于 -1 和 1 之间,则它不是正态分布。正态分布输出区间应该是-∞和+∞。@paxinum 提到的解决方案可能比自己进行计算更简单。

于 2012-07-14T22:15:25.700 回答
0

好吧,如果它几乎是高斯分布,那么 70% 将是低于 0.75 的所有值,请参阅此表:http ://www.roymech.co.uk/Useful_Tables/Statistics/Statistics_Normal_table.html

于 2012-07-14T22:29:29.663 回答