Perlin génération de bruit pour le terrain
j'essaie d'implémenter un code source que j'ai trouvé en ligne pour générer une carte de hauteur en utilisant le bruit Perlin. J'ai réussi à obtenir la carte de hauteur en utilisant la fonction noise3, avec la troisième coordonnée étant une "graine" aléatoire, pour permettre des cartes de hauteur aléatoires.
mon problème est que le terrain généré est plutôt terne - je veux des montagnes et je vais avoir des prairies mobiles. J'ai fait quelques lectures sur le bruit Perlin (surtout ici). En raison de la code source que j'ai trouvé évidemment pas écrit avec lisibilité à l'esprit et ma faible emprise sur le concept de bruit Perlin en général, je ne peux pas comprendre ce que je dois modifier dans le code (amplitude et fréquence?) pour créer un terrain plus drastique.
un peu plus d'informations sur la génération de cartes de hauteur en utilisant le bruit Perlin, le bruit Perlin en général, ou même un code plus déchiffrable serait également le bienvenu.
EDIT: je comprends (en quelque sorte) Comment fonctionne le bruit Perlin, par exemple, en ce qui concerne l'amplitude et la fréquence, je me demande juste quelles variables changer dans le code que j'ai relié ci-dessus, qui sont utilisés pour ces deux aspects.
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);
}
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!
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.
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.