1

我使用 perlin 噪声来生成 2D 高度图。起初,我手动尝试了一些参数,并为我的工作找到了幅度、持久性等的良好组合。

现在我正在开发程序,我添加了用户更改地图参数并为自己制作新地图的功能,但现在我看到对于某些参数(主要是八度音阶和频率),值不在我使用的范围内查看。我认为如果设置 Amplitude = 20,我从中获得的值(高度)将在 [0,20] 或 [-10,10] 或 [-20,20] 范围内,但现在我看到 Amplitude 是不是控制输出范围的唯一参数。

我的问题是:是否有一个精确的数学公式(幅度、八度、频率和持久性的函数)来计算范围,或者我应该采取大量样本(如 100,000)并检查它们的最小值和最大值以猜测近似值范围?

注意:下面的代码是一个 perlin 噪声的实现,stackoverflow 的一个人用 C 编写了它,然后我将它移植到了 java。

PerlinNoiseParameters.java

public class PerlinNoiseParameters {

    public double persistence;
    public double frequency;
    public double amplitude;
    public int octaves;
    public int randomseed;

    public PerlinNoiseParameters(double persistence, double frequency, double amplitude, int octaves, int randomseed) {
        this.ChangeParameters(persistence, frequency, amplitude, octaves, randomseed);
    }

    public void ChangeParameters(double persistence, double frequency, double amplitude, int octaves, int randomseed) {
        this.persistence = persistence;
        this.frequency = frequency;
        this.amplitude = amplitude;
        this.octaves = octaves;
        this.randomseed = 2 + randomseed * randomseed;
    }
}

PerlinNoiseGenerator.java

public class PerlinNoiseGenerator {

    PerlinNoiseParameters parameters;

    public PerlinNoiseGenerator() {
    }

    public PerlinNoiseGenerator(PerlinNoiseParameters parameters) {
        this.parameters = parameters;
    }

    public void ChangeParameters(double persistence, double frequency, double amplitude, int octaves, int randomseed) {
        parameters.ChangeParameters(persistence, frequency, amplitude, octaves, randomseed);
    }

    public void ChangeParameters(PerlinNoiseParameters newParams) {
        parameters = newParams;
    }

    public double get(double x, double y) {
        return parameters.amplitude * Total(x, y);
    }

    private double Total(double i, double j) {
        double t = 0.0f;
        double _amplitude = 1;
        double freq = parameters.frequency;

        for (int k = 0; k < parameters.octaves; k++) {
            t += GetValue(j * freq + parameters.randomseed, i * freq + parameters.randomseed)
                    * _amplitude;
            _amplitude *= parameters.persistence;
            freq *= 2;
        }

        return t;
    }

    private double GetValue(double x, double y) {
        int Xint = (int) x;
        int Yint = (int) y;

        double Xfrac = x - Xint;
        double Yfrac = y - Yint;

        double n01 = Noise(Xint - 1, Yint - 1);
        double n02 = Noise(Xint + 1, Yint - 1);
        double n03 = Noise(Xint - 1, Yint + 1);
        double n04 = Noise(Xint + 1, Yint + 1);
        double n05 = Noise(Xint - 1, Yint);
        double n06 = Noise(Xint + 1, Yint);
        double n07 = Noise(Xint, Yint - 1);
        double n08 = Noise(Xint, Yint + 1);
        double n09 = Noise(Xint, Yint);
        double n12 = Noise(Xint + 2, Yint - 1);
        double n14 = Noise(Xint + 2, Yint + 1);
        double n16 = Noise(Xint + 2, Yint);
        double n23 = Noise(Xint - 1, Yint + 2);
        double n24 = Noise(Xint + 1, Yint + 2);
        double n28 = Noise(Xint, Yint + 2);
        double n34 = Noise(Xint + 2, Yint + 2);

        double x0y0 = 0.0625 * (n01 + n02 + n03 + n04) + 0.1250
                * (n05 + n06 + n07 + n08) + 0.2500 * n09;

        double x1y0 = 0.0625 * (n07 + n12 + n08 + n14) + 0.1250
                * (n09 + n16 + n02 + n04) + 0.2500 * n06;

        double x0y1 = 0.0625 * (n05 + n06 + n23 + n24) + 0.1250
                * (n03 + n04 + n09 + n28) + 0.2500 * n08;

        double x1y1 = 0.0625 * (n09 + n16 + n28 + n34) + 0.1250
                * (n08 + n14 + n06 + n24) + 0.2500 * n04;

        double v1 = Interpolate(x0y0, x1y0, Xfrac);
        double v2 = Interpolate(x0y1, x1y1, Xfrac);

        double fin = Interpolate(v1, v2, Yfrac);

        return fin;
    }

    private double Interpolate(double x, double y, double a) {
        double negA = 1.0 - a;
        double negASqr = negA * negA;
        double fac1 = 3.0 * (negASqr) - 2.0 * (negASqr * negA);
        double aSqr = a * a;
        double fac2 = 3.0 * aSqr - 2.0 * (aSqr * a);

        return x * fac1 + y * fac2;
    }

    private double Noise(int x, int y) {
        int n = x + y * 57;
        n = (n << 13) ^ n;
        int t = (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff;
        return 1.0 - (double) t * 0.931322574615478515625e-9;
    }
}
4

2 回答 2

2

单个 perlin 噪声步长的范围是: http ://digitalfreepen.com/2017/06/20/range-perlin-noise.html

-sqrt(N/4), sqrt(N/4)

N是维度的数量。2 在你的情况下。

除此之外,八度音阶、持久性和振幅还增加了:

double range = 0.0;
double _amplitude = parameters.;
for (int k = 0; k < parameters.octaves; k++) {
    range += sqrt(N/4) * _amplitude;
    _amplitude *= parameters.persistence;
}
return range;

可能有某种方法可以将其作为单个数学表达式来执行。涉及 pow(),但现在我的大脑让我失望了。

于 2019-08-15T07:48:10.477 回答
1

这不是八度音阶和频率影响幅度的问题,至少不是直接的问题。这是整数溢出的问题。因为您通过将随机种子添加到 x 和 y 坐标来引入随机种子(这很不寻常,我认为这不是通常的暗示)

t += GetValue(j * freq + parameters.randomseed, i * freq + parameters.randomseed)* _amplitude;

随机种子可能很大(可能是整数的接近全尺寸),因为

this.randomseed = 2 + randomseed * randomseed;

因此,如果您为 j 和 i 输入较大的值,则最终通过的双精度值GetValue(double x, double y)大于 int 的最大大小,此时您调用

int Xint = (int) x;
int Yint = (int) y;

Xint 和 YInt 不会像 x 和 y 那样(因为 x 和 y 可能很大!)等等

double Xfrac = x - Xint;
double Yfrac = y - Yint;

可能比 1 大得多,允许返回不在 -1 和 1 之间的值。

使用合理且小的值,我使用您的代码的范围在 -1 和 1 之间(对于幅度 1)


顺便说一句,在 java 中,方法名通常是methodName,而不是MethodName

如果它有用,请在这里找到另一个关于 perlin 噪声的 java 实现:http: //mrl.nyu.edu/~perlin/noise/

于 2013-07-02T15:17:12.663 回答