2016-06-23 15 views
2

ich hier Perlin Geräusch nach einer Führung wurde erhöhen: http://flafla2.github.io/2014/08/09/perlinnoise.htmlWie die Standardabweichung einer Perlin Rauschverteilung

Der Code, den ich genau so, wie das gleiche verwenden ist, was er beschreibt, obwohl meine Rendering-Implementierung unterscheidet. Wenn ich jedoch ein Rauschen von dieser Verteilung erhalte, tendiere ich dazu, einen sehr großen Anteil von Werten nahe dem Bereich von 0,5 und sehr wenige an den Rändern zu erhalten. Sie können dies aus einer Probe sehen, die ich in der folgenden Abbildung in eine schnelle Grafik umgewandelt habe. Ich skalierte meine Rauschfunktion um die Breite des Bildschirms und ließ sie ein Array um index = noise um 1 erhöhen, jedes Mal wenn es einen Rauschwert erzeugte. Wie Sie sehen können, ist die Verteilung kaum Normal, da die Ränder einfach ganz herausfallen. (Es ist technisch ein Histogramm ohne Etiketten und Strichlinien)

Distribution

Was ist der beste Weg, um diese Verteilung breite Richtung auf dem Boden zu machen, aber nicht nach oben? Ich möchte, dass die meisten Werte in einer Gauß-Verteilung liegen, aber ich kann nicht einfach eine verteilte Gaußsche Zufallszahl verwenden, da ich nahe beieinander liegende Werte brauche, die nahe beieinander liegen (Perlin-Rauschen).

Ich denke, eine bessere Art zu fragen wäre: Warum bekomme ich keine Rauschwerte in den oberen und unteren Vierteln meines Bereichs? Gibt es eine Möglichkeit, einen guten Skalierungsfaktor zu bestimmen, um die Grafik zu erweitern?

Hier ist der Code, den ich um das Bild zu bekommen bin mit:

/** 
* 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; 
} 

Hier wird die Startmethode:

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; 
     } 
    }} 

Die Render-Methode:

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); 
     } 
} 

Und schließlich , die Permutationsmenge, auf die in enter() Bezug genommen wird:

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 
}; 

Es sind alle Zahlen von 0 bis 255 in zufälliger Reihenfolge.

Um das Problem erneut zu formulieren, generiert die Funktion einen großen Anteil von Zahlen zwischen 0,45 und 0,55 und fast keine Zahlen außerhalb dieses Bereichs. Ich möchte die Funktion neu skalieren, so dass ich mehr Zahlen in diesen unteren und oberen Bereichen bekomme. Dies kann irgendwo in der letzten Zeile der perlin()-Funktion auftreten, aber ich bin mir nicht sicher, wie es geht. Danke für Ihre Hilfe.

+0

Dies ist eine Näherung erster Ordnung des Gaußschen, von dem Sie sprechen - wo sollen die Werte an den Ecken Ihres Bildes, wo es leer ist? Ich weiß nicht Perlin, aber wenn Sie den Code aufstellen, könnten wir den Fehler finden – gpasch

+0

@gpasch Das Problem ist, ich glaube nicht, eine Gauss-Verteilung wird funktionieren, da die Rauschwerte nicht unabhängig zufällig sein sollen, sondern stattdessen sind auf "Zeit" gebaut.Gib mir eine Sekunde und ich poste den Code. –

+0

Sind irgendwelche visuellen Artefakte in Ihrer Ausgabe zu sehen? – Pikalek

Antwort

0

Ich habe dieses Problem durch die folgende Methode behoben.

Zunächst, während ich Rauschwerte erzeuge, behalte ich die minimalen und maximalen Werte im Auge.

Zweitens, wenn ich die Rauschwerte in mein Array speicherte, normalisierte ich die Daten: (Wert - Min)/(Max - Min). Auf diese Weise liegen alle meine Werte zwischen 0 und 1, sind aber besser verteilt. Die Gleichung bildet den minimalen Wert auf (min - min)/(max - min) oder 0 ab und bildet den größten Wert auf (max - min)/(max - min) oder 1 ab und skaliert alle dazwischen liegenden Werte.