Perlin génération de bruit pour le terrain

4 réponses

PerlinNoise.h

#pragma once

class PerlinNoise
{
public:

  // Constructor
    PerlinNoise();
    PerlinNoise(double _persistence, double _frequency, double _amplitude, int _octaves, int _randomseed);

  // Get Height
    double GetHeight(double x, double y) const;

  // Get
  double Persistence() const { return persistence; }
  double Frequency()   const { return frequency;   }
  double Amplitude()   const { return amplitude;   }
  int    Octaves()     const { return octaves;     }
  int    RandomSeed()  const { return randomseed;  }

  // Set
  void Set(double _persistence, double _frequency, double _amplitude, int _octaves, int _randomseed);

  void SetPersistence(double _persistence) { persistence = _persistence; }
  void SetFrequency(  double _frequency)   { frequency = _frequency;     }
  void SetAmplitude(  double _amplitude)   { amplitude = _amplitude;     }
  void SetOctaves(    int    _octaves)     { octaves = _octaves;         }
  void SetRandomSeed( int    _randomseed)  { randomseed = _randomseed;   }

private:

    double Total(double i, double j) const;
    double GetValue(double x, double y) const;
    double Interpolate(double x, double y, double a) const;
    double Noise(int x, int y) const;

    double persistence, frequency, amplitude;
    int octaves, randomseed;
};

PerlinNoise.rpc

#include "PerlinNoise.h"

PerlinNoise::PerlinNoise()
{
  persistence = 0;
  frequency = 0;
  amplitude  = 0;
  octaves = 0;
  randomseed = 0;
}

PerlinNoise::PerlinNoise(double _persistence, double _frequency, double _amplitude, int _octaves, int _randomseed)
{
  persistence = _persistence;
  frequency = _frequency;
  amplitude  = _amplitude;
  octaves = _octaves;
  randomseed = 2 + _randomseed * _randomseed;
}

void PerlinNoise::Set(double _persistence, double _frequency, double _amplitude, int _octaves, int _randomseed)
{
  persistence = _persistence;
  frequency = _frequency;
  amplitude  = _amplitude;
  octaves = _octaves;
  randomseed = 2 + _randomseed * _randomseed;
}

double PerlinNoise::GetHeight(double x, double y) const
{
  return amplitude * Total(x, y);
}

double PerlinNoise::Total(double i, double j) const
{
    //properties of one octave (changing each loop)
    double t = 0.0f;
    double _amplitude = 1;
    double freq = frequency;

    for(int k = 0; k < octaves; k++) 
    {
        t += GetValue(j * freq + randomseed, i * freq + randomseed) * _amplitude;
        _amplitude *= persistence;
        freq *= 2;
    }

    return t;
}

double PerlinNoise::GetValue(double x, double y) const
{
    int Xint = (int)x;
    int Yint = (int)y;
    double Xfrac = x - Xint;
    double Yfrac = y - Yint;

  //noise values
  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);

    //find the noise values of the four corners
    double x0y0 = 0.0625*(n01+n02+n03+n04) + 0.125*(n05+n06+n07+n08) + 0.25*(n09);  
    double x1y0 = 0.0625*(n07+n12+n08+n14) + 0.125*(n09+n16+n02+n04) + 0.25*(n06);  
    double x0y1 = 0.0625*(n05+n06+n23+n24) + 0.125*(n03+n04+n09+n28) + 0.25*(n08);  
    double x1y1 = 0.0625*(n09+n16+n28+n34) + 0.125*(n08+n14+n06+n24) + 0.25*(n04);  

    //interpolate between those values according to the x and y fractions
    double v1 = Interpolate(x0y0, x1y0, Xfrac); //interpolate in x direction (y)
    double v2 = Interpolate(x0y1, x1y1, Xfrac); //interpolate in x direction (y+1)
    double fin = Interpolate(v1, v2, Yfrac);  //interpolate in y direction

    return fin;
}

double PerlinNoise::Interpolate(double x, double y, double a) const
{
    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; //add the weighted factors
}

double PerlinNoise::Noise(int x, int y) const
{
    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;/// 1073741824.0);
}
55
répondu Nick Banks 2011-01-20 22:05:01

un ami vient de me relier à cette question, et j'ai pensé que je pourrais essayer de clarifier quelques choses qui ne sont pas abordées dans la réponse acceptée.

L'article intéressant et utile d'Elias utilise "value noise" et non "Perlin noise". Le bruit de valeur implique l'ajustement de courbe de points aléatoires. Le bruit de Gradient (dont le bruit de Perlin est un exemple primaire) crée un réseau de 0 points de valeur et donne à chacun un gradient aléatoire. Ils sont souvent confondus avec une de l'autre!

http://en.wikipedia.org/wiki/Gradient_noise

Deuxièmement, utiliser une troisième valeur comme une graine est coûteux. Si vous souhaitez terrain aléatoire, envisager de traduire votre origine un montant aléatoire à la place. Les appels 3D seront plus chers que les appels 2D. Et tout ce que vous faites est d'utiliser la valeur z pour sélectionner une tranche particulière de bruit 2D.

Troisièmement, l'appel de fonction droite va retourner des valeurs qui sont assez lisses et roulantes dans l'ensemble, pas aussi craggy que le terrain réel, car son aléatoire est limité à une seule fréquence. Pour obtenir terrain plus craggier, une bonne technique est de faire la somme des appels multiples qui progressent à travers l'espace de bruit à des fréquences différentes, généralement définir une "fractale" valeurs.

Ainsi, par exemple, additionner noise(x, y) + (1/2)(noise(x*2, y*2) + (1/4)(noise(x*4, y*4)...

la somme résultante sera probablement souvent en dehors de l'intervalle -1 à 1, donc vous devrez normaliser le résultat avant que les valeurs soient utiles. J'aimerais suggérez de configurer la série de facteurs (1, 1/2, 1/4,etc.) de sorte que vous êtes garanti de rester à l'intérieur de [-1, 1] ce qui peut être fait par pondération progressive en fonction du nombre d '"octaves" que vous utilisez. (Mais je ne sais pas si c'est vraiment le moyen le plus efficace pour ce faire.)

Exemple avec quatre octaves: (1/15)(noise(x, y) + (2/15)(noise(2x, 2y) + (4/15)(noise(4x, 4y) + (8/15)(noise(8x, 8y)

puis, utilisez la normalisation du "bruit turbulent" de prendre la somme et de la faire = |sum| (c.-à-d. en utilisant la fonction abs). Cela donnera le terrain défini les crêtes angulaires de vallée au lieu d'être ondulées en douceur.

je travaille sur un visualiseur SimplexNoise, j'espère l'ouvrir sur GitHub éventuellement, comme un projet Java. Un premier jet du visualiseur peut être trouvé et exécuté via ce post à java-gaming.org: http://www.java-gaming.org/topics/simplex-noise-experiments-towards-procedural-generation/27163/view.html L'accent mis sur la première ébauche est plus tutoriel, avec des exemples de code générés (mais ils sont Java.)

excellent article sur le fonctionnement de SimplexNoise (et Perlin vs Gradient de fond)): http://staffwww.itn.liu.se stegu / simplexnoise / simplexnoise.

Stefan Gustavson a fait du Très bon travail!

19
répondu Phil Freihofner 2017-01-10 11:25:29

L'Amplitude contrôle à quel point le terrain est haut/bas, la fréquence à quel point il est fluide, la fréquence plus basse étant plus fluide.

Donc, si vous voulez dentelés paysage montagneux vous avez besoin de deux.

5
répondu wich 2011-01-20 21:56:00

voici un exemple de génération de surface que j'ai écrit il y a un certain temps en JavaScript en utilisant le bruit 3D Perlin. Puisque dans la surface les voxels sont présents ou non j'applique simplement le seuil après le calcul du cube de bruit de Perlin. Dans l'exemple, la probabilité de bruit est égale pour toutes les dimensions. Vous pouvez obtenir un paysage plus réaliste quand vous augmentez les valeurs aléatoires vers le sol et de le réduire vers le ciel.

http://kirox.de/test/Surface.html

WebGL doit être activé. Au moment d'écrire ceci je recommande D'utiliser Chrome pour une meilleure performance.

2
répondu oyophant 2013-08-27 16:32:18