当前位置: 首页 > 知识库问答 >
问题:

柏林噪声值范围

秦斌
2023-03-14

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

现在我正在开发该程序,我为用户添加了更改贴图参数并为自己制作新贴图的功能,但现在我发现某些参数(主要是倍频程和频率)的值不在我以前看到的范围内。我认为,如果设定的振幅=20,我从中得到的值(高度)将在[0,20]或[-10,10]或[-20,20]范围内,但现在我看到振幅不是控制输出范围的唯一参数。

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

注意:下面的代码是一个perlin noise的实现,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;
    }
}

共有2个答案

芮瑾瑜
2023-03-14

这不是八度音阶和频率影响振幅的问题,至少不是直接影响振幅的问题。这是整数溢出的问题。因为你通过把随机种子加到x和y坐标中来引入它(这很不寻常,我认为这不是通常的实现方式)

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

随机种子可能是巨大的(可能是int的近似全尺寸),因为

this.randomseed = 2 + randomseed * randomseed;

因此,如果为j和i输入较大的值,则在调用

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中,方法名通常是方法名,而不是方法名

如果有用,请在此处找到perlin noise的annother java实现:http://mrl.nyu.edu/~柏林/噪音/

郑星雨
2023-03-14

单个柏林噪声阶跃的范围为: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(),但我现在无法通过大脑。

 类似资料:
  • 我正在研究一些相干噪声的各种实现(我知道有库,但这主要是为了我自己的启发和好奇心)以及如何使用它,我对最初的Perlin噪声有一个问题。 根据这个经常链接的数学常见问题,输出范围将介于-1和1之间,但我不明白该值是如何在该范围内的。 据我所知,算法基本上是这样的:每个网格点都有一个相关的长度为1的随机梯度向量。然后,对于每个点,对于所有四个周围的网格点,计算随机梯度和从该网格点出发的向量的点积。然

  • 我有一个柏林噪声函数,我想用它来为我的游戏挑选生物群落地图。问题是,生物群落是由两个因素决定的——平均降雨量和平均温度。所以,我想,我只需要做两个柏林噪声函数,然后重叠它们。 现在的问题是,生物群落并不包括所有可能的降水温度组合。例如,没有高降雨量和低温的生物群落,如图所示。 我怎么能仍然使用柏林噪声,但永远无法到达生物群落未覆盖的区域?

  • 我最近一直在尝试用C实现一个Perlin噪声发生器(基于Ken Perlin的网站,使用SDL库作为屏幕输出),但输出显示插值块之间的边不是连续的或平滑的-插值块确实表现为块。 我很确定我的代码中缺少了一些东西,或者做错了,但我似乎找不到它。 作为参考,我的当前代码如下: 用法:跑步时,按住上下键可放大或缩小噪声网格。

  • 我想让3D Perlin噪波算法适应较低的维度,但我在梯度函数方面遇到了问题,因为我不完全理解推理。 原始的Perlin梯度函数采用四个参数:一个和一个三维坐标。函数的结果基于的值返回,如下所示。 : : : : : : : : : : : : : : : : 从0到11的返回值构成了一种模式,因为每个组合只表示一次。然而,最后四个是重复的。为什么选择它们来拟合最后四个返回值?有两个维度(x,y)

  • GaussianNoise层 因为这是一个起正则化作用的层,该层只在训练时才有效。 GaussianDropout层 因为这是一个起正则化作用的层,该层只在训练时才有效。 AlphaDropout Alpha Dropout是一种保持输入均值和方差不变的Dropout,该层的作用是即使在dropout时也保持数据的自规范性。 通过随机对负的饱和值进行激活,Alphe Drpout与selu激活函数

  • GaussianNoise层 keras.layers.noise.GaussianNoise(sigma) 为层的输入施加0均值,标准差为sigma的加性高斯噪声。该层在克服过拟合时比较有用,你可以将它看作是随机的数据提升。高斯噪声是需要对输入数据进行破坏时的自然选择。 一个使用噪声层的典型案例是构建去噪自动编码器,即Denoising AutoEncoder(DAE)。该编码器试图从加噪的输