Диапазон значений шума Перлина

Я использовал шум Перлина для создания двухмерной карты высот. Сначала я попробовал некоторые параметры вручную и нашел хорошее сочетание амплитуды, настойчивости ... для моей работы.

Теперь, когда я разрабатываю программу, я добавил функцию, позволяющую пользователю изменять параметры карты и создавать новую карту для себя, но теперь я вижу, что для определенных параметров (в основном октав и частоты) значения не находятся в диапазоне, который я использовал чтобы увидеть. Я думал, что если задано Amplitude = 20, значения (высоты), которые я получаю, будут в диапазоне, например, [0,20], [-10,10] или [-20,20], но теперь я вижу, что Amplitude не единственный параметр, который контролирует выходной диапазон.

У меня вопрос: есть ли точная математическая формула (функция амплитуды, октав, частоты и постоянства) для вычисления диапазона, или я должен взять много выборок (например, 100 000) и проверить минимум и максимум их значения, чтобы угадать примерный диапазон?

Примечание. Следующий код представляет собой реализацию шума Перлина, который один из разработчиков 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;
    }
}

person Mohammad Jafar Mashhadi    schedule 02.07.2013    source источник
comment
Стоит отметить, что результаты шума Перлина в значительной степени ориентированы на 0, поэтому даже если [-10,10] - это полный диапазон, вы ожидаете регулярно видеть только центр этого диапазона, например [-5,5]. Я считаю, что если вы взяли неприличное количество образцов, диапазон всегда был бы [-амплитуда, амплитуда]   -  person Richard Tingle    schedule 02.07.2013
comment
@RichardTingle, поэтому вы предлагаете метод выборки, потому что он более удобен в реальной жизни. Правильно?   -  person Mohammad Jafar Mashhadi    schedule 02.07.2013
comment
ну да и нет. У меня есть генератор симплексного шума, настроенный как часть моей программы (симплексный шум - это модернизация шума Перлина), и если я прошу 1000 сэмплов, мин / макс будет -0,67 / 0,77, 1000000 сэмплов мин / макс будет -0,83 /0.80, если я прошу 10000000 сэмплов, это -0,84 / 0,87.   -  person Richard Tingle    schedule 02.07.2013
comment
Итак, я говорю, что диапазон не является полезной величиной, потому что при достаточном количестве сэмплов он стремится к [-1,1] для любых октав, частоты и стойкости. Просто скорость, с которой он стремится к [-1,1], меняется.   -  person Richard Tingle    schedule 02.07.2013
comment
Или, точнее, имеет тенденцию к [- амплитуда, + амплитуда] для любых октав, частоты и послесвечения.   -  person Richard Tingle    schedule 02.07.2013
comment
Возможно, стандартное отклонение может быть полезным показателем, и оно будет меняться в зависимости от октав и частоты.   -  person Richard Tingle    schedule 02.07.2013
comment
@RichardTingle большое спасибо за ваш ответ. Теперь я думаю, что если я пробую значения карты и устанавливаю количество выборок на основе размера моей игровой карты, не будет особой необходимости знать точную формулу диапазона выходных значений, но они очень близки к 0 и не подходят пределы [-amp, + amp] часто, почему выходят за его пределы   -  person Mohammad Jafar Mashhadi    schedule 02.07.2013
comment
Хорошо, я знаю, что не так, отправляю в качестве ответа   -  person Richard Tingle    schedule 02.07.2013


Ответы (2)


Это не проблема с октавами и частотой, влияющими на амплитуду, по крайней мере, напрямую. Это проблема с целочисленным переполнением. Потому что вы вводите свое случайное семя, добавляя его к координатам x и y (что необычно, я не думаю, что это обычное внедрение)

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

И случайное семя может быть огромным (возможно, почти полным размером int), потому что

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

Если это полезно, пожалуйста, найдите здесь другую java-имплементацию шума Perlin: http://mrl.nyu.edu/~perlin/noise/

person Richard Tingle    schedule 02.07.2013
comment
Спасибо! так что я должен взглянуть на подлинную реализацию Кена Перлина! О camelNotation: когда я портировал исходный код с C на java, я забыл реорганизовать имена, в любом случае спасибо за совет :) - person Mohammad Jafar Mashhadi; 02.07.2013
comment
@Mjafar Потрясающе, извините за то, что у меня есть ответ, затем полчаса молчания - person Richard Tingle; 02.07.2013
comment
не надо извиняться! на самом деле тем временем я работал над другой частью проекта и не заметил, как прошло время! Вы тоже заняты, не жду немедленных ответов! Спасибо :) - person Mohammad Jafar Mashhadi; 03.07.2013

Диапазон одного шага шума Перлина: 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 (), но мозг меня подводит прямо сейчас.

person Daid Braam    schedule 15.08.2019