2013-08-15 6 views
5

ich ein paar der verschiedenen Implementierungen für kohärentes Rauschen zu untersuchen (ich weiß, es gibt Bibliotheken, aber dies ist vor allem für meine eigene Erbauung und Neugier) und wie Sie es verwenden können, und es gibt ein Problem Ich habe mit dem originalen Perlin Geräusch Ding.Ausgangsbereich von Perlin Geräusch

Laut this frequently linked Math FAQ wird der Ausgabebereich zwischen -1 und 1 liegen, aber ich verstehe nicht, wie der Wert in diesem Bereich sein wird.

Wie ich es zu verstehen, ist der Algorithmus im Grunde das: jeden Gitterpunkt hat einen zugeordneten zufälligen Gradientenvektor Länge 1. Dann berechnen Sie für jeden Punkt für alle vier umgebenden Gitterpunkte das Skalarprodukt des zufälligen Gradienten und den Vektor, der von diesem Gitterpunkt ausgeht. Dann verwenden Sie eine einfache Lockerungskurve und lineare Interpolation, um das auf einen Wert zu reduzieren.

Aber hier ist mein Problem: Diese Dot-Produkte werden gelegentlich außerhalb des Bereichs [-1, 1] liegen, und da Sie die lineare Interpolation letztendlich zwischen den Punktprodukten durchführen, bedeutet das nicht, dass der endgültige Wert gelegentlich außerhalb des Bereichs von [-1, 1] liegen?

sie beispielsweise an, dass eines des Zufallsvektoren ist (sqrt(2)/2, sqrt(2)/2) (die eine Länge von 1 hat) und (0.8, 0.8) (die in dem Einheitsquadrat ist), dann ein Ergebnis von etwa 1.131 erhalten. Wenn dieser Wert in der linearen Interpolation verwendet wird, ist es durchaus möglich, dass der generierte Wert größer als 1 ist. Und in der Tat passiert das mit meiner geradlinigen Implementierung ziemlich häufig.

Fehle ich hier etwas?

Als Referenz hier ist mein Code in Java. Vec ist eine einfache Klasse, um einfache 2d Vektorarithmetik zu tun, fade() ist die Leichtigkeitskurve, lerp() ist lineare Interpolation und gradient(x, y) gibt Ihnen den Gradienten für diesen Gitterpunkt als Vec. Die gridSize Variable Sie die Größe des Gitters in Pixel gibt (es hat Typ double):

public double getPoint(int x, int y) { 
    Vec p = new Vec(x/gridSize, y/gridSize); 
    Vec d = new Vec(Math.floor(p.x), Math.floor(p.y)); 


    int x0 = (int)d.x, 
     y0 = (int)d.x; 


    double d00 = gradient(x0 , y0 ).dot(p.sub(x0 , y0 )), 
      d01 = gradient(x0 , y0 + 1).dot(p.sub(x0 , y0 + 1)), 
      d10 = gradient(x0 + 1, y0 ).dot(p.sub(x0 + 1, y0 )), 
      d11 = gradient(x0 + 1, y0 + 1).dot(p.sub(x0 + 1, y0 + 1)); 

    double fadeX = fade(p.x - d.x), 
      fadeY = fade(p.y - d.y); 

    double i1 = lerp(fadeX, d00, d10), 
      i2 = lerp(fadeX, d01, d11); 

    return lerp(fadeY, i1, i2); 
} 

Edit: Hier ist der Code für die Erzeugung der Zufalls Gradienten:

double theta = gen.nextDouble() * 2 * Math.PI; 
gradients[i] = new Vec(Math.cos(theta), Math.sin(theta)); 

Wo gen ist ein java.util.Random.

Antwort

6

Sie haben y0 = (int)d.x;, aber sie bedeuten d.y. Dies wird sich höchstwahrscheinlich auf Ihren Ausgabebereich auswirken und ist der Grund dafür, dass Sie solche Werte weit außerhalb des zulässigen Bereichs sehen.


, dass der Ausgangsbereich des Perlin Rausch gesagt, ist nicht tatsächlich [-1, 1]. Während ich mir selbst nicht ganz sicher bin (ich muss alt werden), this rather lengthy discussion funktioniert, dass der tatsächliche Bereich ist [-sqrt (n)/2, sqrt (n)/2], wo n ist die Dimensionalität (2 in Ihrem Fall). Daher sollte der Ausgabebereich Ihrer 2D Perlin-Rauschfunktion [-0.707, 0.707] lauten. Dies ist irgendwie mit der Tatsache zusammen, daß sowohl d und die Interpolationsparameter sind eine Funktion der p.Wenn Sie diese Diskussion durchlesen, finden Sie möglicherweise die genaue Erklärung, nach der Sie suchen (insbesondere post #7).

Ich prüfe Ihre Implementierung mit dem folgenden Programm (die ich zusammen aus Ihrem Beispiel gehackt, so verzeihen Sie das seltsame Verwendung von gridCells und gridSize):

import java.util.Random; 


public class Perlin { 

    static final int gridSize = 200; 
    static final int gridCells = 20; 
    static final Vec[][] gradients = new Vec[gridCells + 1][gridCells + 1]; 

    static void initializeGradient() { 
     Random rand = new Random(); 
     for (int r = 0; r < gridCells + 1; ++ r) { 
      for (int c = 0; c < gridCells + 1; ++ c) { 
       double theta = rand.nextFloat() * Math.PI; 
       gradients[c][r] = new Vec(Math.cos(theta), Math.sin(theta));     
      } 
     } 
    } 

    static class Vec { 
     double x; 
     double y; 
     Vec (double x, double y) { this.x = x; this.y = y; } 
     double dot (Vec v) { return x * v.x + y * v.y; } 
     Vec sub (double x, double y) { return new Vec(this.x - x, this.y - y); } 
    } 

    static double fade (double v) { 
     // easing doesn't matter for range sample test. 
     // v = 3 * v * v - 2 * v * v * v; 
     return v; 
    } 

    static double lerp (double p, double a, double b) { 
     return (b - a) * p + a; 
    } 

    static Vec gradient (int c, int r) { 
     return gradients[c][r]; 
    } 

    // your function, with y0 fixed. note my gridSize is not a double like yours.  
    public static double getPoint(int x, int y) { 

     Vec p = new Vec(x/(double)gridSize, y/(double)gridSize); 
     Vec d = new Vec(Math.floor(p.x), Math.floor(p.y)); 

     int x0 = (int)d.x, 
      y0 = (int)d.y; 

     double d00 = gradient(x0 , y0 ).dot(p.sub(x0 , y0 )), 
       d01 = gradient(x0 , y0 + 1).dot(p.sub(x0 , y0 + 1)), 
       d10 = gradient(x0 + 1, y0 ).dot(p.sub(x0 + 1, y0 )), 
       d11 = gradient(x0 + 1, y0 + 1).dot(p.sub(x0 + 1, y0 + 1)); 

     double fadeX = fade(p.x - d.x), 
       fadeY = fade(p.y - d.y); 

     double i1 = lerp(fadeX, d00, d10), 
       i2 = lerp(fadeX, d01, d11); 

     return lerp(fadeY, i1, i2); 

    } 

    public static void main (String[] args) { 

     // loop forever, regenerating gradients and resampling for range. 
     while (true) { 

      initializeGradient(); 

      double minz = 0, maxz = 0; 

      for (int x = 0; x < gridSize * gridCells; ++ x) { 
       for (int y = 0; y < gridSize * gridCells; ++ y) { 
        double z = getPoint(x, y); 
        if (z < minz) 
         minz = z; 
        else if (z > maxz) 
         maxz = z; 
       } 
      } 

      System.out.println(minz + " " + maxz); 

     } 

    } 

} 

ich die Werte innerhalb des theoretischen Bereich von sehen [-0.707, 0.707], obwohl ich im Allgemeinen Werte zwischen -0,6 und 0,6 sehe; Das könnte nur eine Folge der Wertverteilung und einer niedrigen Abtastrate sein.

+0

Danke für Ihre Hilfe! Das hat es behoben. – Oskar

0

Wenn Sie ein Skalarprodukt berechnen, können Sie Werte außerhalb des Bereichs -1 +1 erhalten, während des Interpolationsschritts liegt der Endwert jedoch im Bereich -1 +1. Dies liegt daran, dass Abstandsvektoren von Punktprodukten, die interpoliert werden, in entgegengesetzte Richtungen der interpolierten Achse zeigen. Während der letzten Interpolation wird die Ausgabe nicht größer als -1 +1.

Der endgültige Ausgabebereich für Perlin-Rauschen wird durch die Länge der Gradientenvektoren definiert. Wenn wir über 2D-Rauschen sprechen und unser Ziel den Ausgabebereich -1 +1 hat, sollte die Länge der Gradientenvektoren sqrt (2) sein (~ 1,4142). Es ist ein häufiger Fehler, diese Vektoren (1, 1) (-1, 1) (1, -1) (-1, -1) und (1, 0) (0, 1) (-1, 0) zu mischen (0, -1). In diesem Fall liegt der endgültige Ausgabebereich immer noch bei -1 +1, die Werte im Bereich -0.707 +0.707 sind jedoch häufiger. Um dieses Problem zu vermeiden (1, 0) (0, 1) (-1, 0) (0, -1) Vektoren sollten ersetzt werden durch (sqrt (2), 0) (0, sqrt (2)) (-sqrt (2), 0) (0, -sqrt (2)).

Verwandte Themen