2016-06-23 13 views
2

Я следовал за руководством к Перлин шума здесь: http://flafla2.github.io/2014/08/09/perlinnoise.htmlКак увеличить стандартное отклонение распределения шума Перлина

код я использую точно такой же, как и то, что он описывает, хотя моя реализация рендеринга отличается. Однако, когда я получаю шум от этого распределения, я, как правило, получаю очень большую долю значений вблизи диапазона 0,5 и очень мало на полосах. Вы можете увидеть это из образца, который я превратил в быструю графику на изображении ниже. Я масштабировал свою шумовую функцию по ширине экрана и увеличивал ее при index = noise на 1 каждый раз, когда генерировал значение шума. Как вы можете видеть, распределение вряд ли нормальное, так как полосы просто выпадают полностью. (Это технически гистограмма без этикеток и штрих-линий)

Distribution

Что такое лучший способ сделать это распределение более широкий в сторону дна, но не к вершине? Я хочу, чтобы большинство значений лежало в распределении Гаусса, но я не могу просто использовать распределенное случайное число Гаусса, потому что мне нужно, чтобы значения, близкие друг к другу, были близки друг к другу (шум Перлина).

Я думаю, лучший способ задать вопрос: почему я не получаю никаких значений шума в верхней и нижней частях моего диапазона? Есть ли способ определить хороший коэффициент масштабирования для расширения графика?

Вот код, я использую, чтобы получить изображение:

/** 
* 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(), но я не уверен, как это сделать. Спасибо за вашу помощь.

+0

Это приближение первого порядка гауссова, о котором вы говорите, - где вы хотите, чтобы значения в углах вашего изображения были пустыми? Я не знаю Perlin, но если вы подберете код, мы найдем ошибку – gpasch

+0

@gpasch. Проблема в том, что я не думаю, что гауссовский дистрибутив будет работать, поскольку значения шума не должны быть независимо случайными, но вместо этого построенный на самолете над «временем».Дайте мне секунду, и я отправлю код. –

+0

Есть ли у них какие-либо визуальные артефакты в вашем выходе? – Pikalek

ответ

0

Я исправил эту проблему следующим образом.

Прежде всего, создавая значения шума, я отслеживал минимальные и максимальные значения.

Во-вторых, при сохранении значений шума в моем массиве, я нормализует данные: (значение - мин)/(макс - мин). Таким образом, все мои значения находятся между 0 и 1, но лучше распределены. Уравнение отображает минимальное значение (min - min)/(max - min) или 0 и отображает наибольшее значение в (max - min)/(max - min) или 1, и оно масштабирует все значения между ними.