Я следовал за руководством к Перлин шума здесь: http://flafla2.github.io/2014/08/09/perlinnoise.htmlКак увеличить стандартное отклонение распределения шума Перлина
код я использую точно такой же, как и то, что он описывает, хотя моя реализация рендеринга отличается. Однако, когда я получаю шум от этого распределения, я, как правило, получаю очень большую долю значений вблизи диапазона 0,5 и очень мало на полосах. Вы можете увидеть это из образца, который я превратил в быструю графику на изображении ниже. Я масштабировал свою шумовую функцию по ширине экрана и увеличивал ее при index = noise
на 1 каждый раз, когда генерировал значение шума. Как вы можете видеть, распределение вряд ли нормальное, так как полосы просто выпадают полностью. (Это технически гистограмма без этикеток и штрих-линий)
Что такое лучший способ сделать это распределение более широкий в сторону дна, но не к вершине? Я хочу, чтобы большинство значений лежало в распределении Гаусса, но я не могу просто использовать распределенное случайное число Гаусса, потому что мне нужно, чтобы значения, близкие друг к другу, были близки друг к другу (шум Перлина).
Я думаю, лучший способ задать вопрос: почему я не получаю никаких значений шума в верхней и нижней частях моего диапазона? Есть ли способ определить хороший коэффициент масштабирования для расширения графика?
Вот код, я использую, чтобы получить изображение:
/**
* Simple linear interpolation
* @param a Start
* @param b End
* @param weight weighting
* @return A linear interpolation between points a and b
*/
public double lerp(double a, double b, double weight) {
return a + weight*(b-a);
}
/**
* Calculates a dot product between a distance vector and a pseudorandom
* "gradient" vector which gets picked using the hash
* @param hash
* @param x distance vector x component
* @param y distance vector y component
* @param z distance vector z component (z = 0 for 2D map)
* @return dot product of <x, y, z> and a pseudorandom gradient vector
*/
public double grad(int hash, double x, double y, double z) {
switch(hash & 0xF)
{
case 0x0: return x + y;
case 0x1: return -x + y;
case 0x2: return x - y;
case 0x3: return -x - y;
case 0x4: return x + z;
case 0x5: return -x + z;
case 0x6: return x - z;
case 0x7: return -x - z;
case 0x8: return y + z;
case 0x9: return -y + z;
case 0xA: return y - z;
case 0xB: return -y - z;
case 0xC: return y + x;
case 0xD: return -y + z;
case 0xE: return y - x;
case 0xF: return -y - z;
default: return 0; // never happens
}
}
/**
* A fifth order fade function: 6t^5 - 15t^4 + 10t^3
* @param t The x-value along the function, t is in [0, 1]
* @return The y-value for the fade function
*/
public double fade(double t) {
return t * t * t * (t * (t * 6 - 15) + 10);
}
//repeat is set to 0 so this method is just a regular "increment by 1"
public int inc(int num) {
num++;
if(repeat > 0) num %= repeat;
return num;
}
/**
* Generates a noise value in the range [0,1].
* Each coordinate is a given distance from a pseudorandomly picked set of
* gradient vectors. The vectors are determined by an array of 256 indexes,
* so the noise pattern inevitably repeats at a scale greater than 255,
* which is bigger than we should need.
* @param x
* @param y
* @param z
* @return
*/
public double perlin(double x, double y, double z) {
if(repeat > 0) {
x = x%repeat;
y = y%repeat;
z = z%repeat;
}
int xi = (int)x & 255;
int yi = (int)y & 255;
int zi = (int)z & 255;
double xf = x-(int)x;
double yf = y-(int)y;
double zf = z-(int)z;
double u = fade(xf);
double v = fade(yf);
double w = fade(zf);
int aaa, aba, aab, abb, baa, bba, bab, bbb;
aaa = p[p[p[ xi ]+ yi ]+ zi ];
aba = p[p[p[ xi ]+inc(yi)]+ zi ];
aab = p[p[p[ xi ]+ yi ]+inc(zi)];
abb = p[p[p[ xi ]+inc(yi)]+inc(zi)];
baa = p[p[p[inc(xi)]+ yi ]+ zi ];
bba = p[p[p[inc(xi)]+inc(yi)]+ zi ];
bab = p[p[p[inc(xi)]+ yi ]+inc(zi)];
bbb = p[p[p[inc(xi)]+inc(yi)]+inc(zi)];
double x1, x2, y1, y2;
/*
Box has corners:
____
|ab|
|cd|
----
Interpolate a-b, then c-d then both of those together, then repeat on the z-1 level
*/
x1 = lerp(grad (aaa, xf , yf , zf), // The gradient function calculates the dot product between a pseudorandom
grad (baa, xf-1, yf , zf), // gradient vector and the vector from the input coordinate to the 8
u); // surrounding points in its unit cube.
x2 = lerp(grad (aba, xf , yf-1, zf),
grad (bba, xf-1, yf-1, zf),
u);
y1 = lerp(x1, x2, v);
x1 = lerp(grad (aab, xf , yf , zf-1),
grad (bab, xf-1, yf , zf-1),
u);
x2 = lerp(grad (abb, xf , yf-1, zf-1),
grad (bbb, xf-1, yf-1, zf-1),
u);
y2 = lerp (x1, x2, v);
return (lerp (y1, y2, w)+1)/2; //Interpolate everything again and move the range from [-1, 1] to [0, 1]
}
/**
* Layers levels of noise, each with decreasing amplitudes and persistence
* @param x
* @param y
* @param z
* @param octaves
* @param persistence how much each layer impacts the layer below it
* @return
*/
public double octave(double x, double y, double z, int octaves, double persistence) {
double total = 0, frequency = 1, amplitude = 1, maxValue = 0;
for(int i = 0; i < octaves; i++) {
total += perlin(x * frequency, y * frequency, z * frequency) * amplitude;
maxValue += amplitude;
amplitude *= persistence;
frequency *= 2;
}
return total/maxValue;
}
Вот метод запуска:
public void enter() {
eOffsetX = r.nextInt(10000);
eOffsetY = r.nextInt(10000); //This will "randomize" the seed of the noise
p = new int[512];
for(int x = 0; x < 512; x++) {
p[x] = permutation[x%256]; //Fill twice
}
eNoise = new float[(int)(1280/tile)][(int)(800/tile)];
gauss = new float[1280];
Arrays.fill(gauss, 0);
for(int i = 0; i < eNoise.length; i++) {
for(int j = 0; j < eNoise[0].length; j++) {
eNoise[i][j] = 1f * 100 * (float) octave(((double)i*zoom+eOffsetX)/1280, ((double)j*zoom+eOffsetY)/800, 0, 7, 0.60);
gauss[(int)(((eNoise[i][j]/100)-.5)*1280*2.5+640)] += 1f;
}
}}
рендер метод:
public void render(Graphics g) {
g.setBackground(Color.white);
g.setColor(Color.black);
for(int k = 0; k < 1280; k++) {
g.fillRect(k, 800-(gauss[k]/10), 5, 5);
}
}
И, наконец, , набор перестановок, указанный в вводе():
private static final int[] permutation = { 151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
};
Это все числа от 0 до 255 в случайном порядке.
Опять же, чтобы повторить проблему, функция генерирует огромную долю чисел от 0,45 до 0,55 и почти не имеет номеров за пределами этого диапазона. Я хочу перемасштабировать функцию, чтобы получить больше чисел в этих нижних и верхних диапазонах. Это может произойти где-то в последней строке функции perlin()
, но я не уверен, как это сделать. Спасибо за вашу помощь.
Это приближение первого порядка гауссова, о котором вы говорите, - где вы хотите, чтобы значения в углах вашего изображения были пустыми? Я не знаю Perlin, но если вы подберете код, мы найдем ошибку – gpasch
@gpasch. Проблема в том, что я не думаю, что гауссовский дистрибутив будет работать, поскольку значения шума не должны быть независимо случайными, но вместо этого построенный на самолете над «временем».Дайте мне секунду, и я отправлю код. –
Есть ли у них какие-либо визуальные артефакты в вашем выходе? – Pikalek