Générer un terrain grâce au Perlin Noise

a marqué ce sujet comme résolu.

Bonjour à tous,

je me suis lancé dans un projet de faire un script pour créer un terrain via script sous Unity. Le script a donc pour but de créer l'objet terrain, de générer un relief aléatoire grâce à la fonction Perlin Noise, puis de le texturer dans un second temps en fonction de l'altitude de chaque vertex, de la pente du terrain, etc, grâce à un système de poids sur un tableau de textures.

J'en suis actuellement à l'étape de la génération de relief aléatoire et j'arrive à quelque chose d'intéressant (voir image ci dessous) mais pas satisfaisant à mon goût.
Perlin Noise Terrain

En effet, le terrain obtenu grâce à mon petit script me donne un terrain qui ressemble fortement a des dunes de sable. En jouant sur la hauteur maximal du terrain je peux obtenir un relief plus ou moins haut et grâce à un coefficient je peux obtenir une fréquence plus importante mais je voudrais obtenir plus d'aléatoire.
Voici le lien d'une image d'un terrain que je souhaiterai pouvoir générer
Je voudrais pouvoir générer des petites bosses sur les versant de mes collines, pouvoir faire en sorte que les collines ne soit pas espacées à intervalle régulier. Je voudrais pouvoir générer pourquoi pas des vallées dans un terrain montagneux comme ici.

Bref voici au point où j'en suis niveau code :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
  void generateNewTerrain()
  {
      //set the data of the terrain we will create
      tData = new TerrainData();
      tData.size = new Vector3(32, 10, 32);
      tData.heightmapResolution = 513;
      tData.SetDetailResolution(512, 8);
      HeightMap = tData.GetHeights(0, 0, tData.heightmapWidth, tData.heightmapHeight); //set the HeightMap tab to make it correspond to the actual height tab for our terrain

      int y = 0;
      while (y < tData.heightmapHeight)
      {
          int x = 0;
          while (x < tData.heightmapWidth)
          {
              float xCoord = Convert.ToSingle(x)  * scale/ Convert.ToSingle(tData.heightmapWidth); //need to convert everything to float in order for the Perling Noise function to work
              float yCoord = Convert.ToSingle(y)  * scale/ Convert.ToSingle(tData.heightmapHeight); // same
              float sample =  Mathf.PerlinNoise(xCoord, yCoord); //generate height value for a vertex of the terrain with the Perlin Noise function
              HeightMap[x,y] = sample ; //put the generated value in the corresponding table of heights
              x++;
          }
          y++;
      }

      tData.SetHeights(0, 0, HeightMap); //put the generated heights into our terrain data

//        //set the texture of the terrain
//        SplatPrototype[] tex = new SplatPrototype[1];
//        tex[0] = new SplatPrototype();
//        tex[0].texture = terrainTex;
//        tex[0].tileSize = new Vector2(1,1);
//        tData.splatPrototypes = tex;

      //create the terrain data object as an asset
      AssetDatabase.CreateAsset(tData, "Assets/testTerrain.asset");

      //create the terrain
      myTerrain = Terrain.CreateTerrainGameObject(tData);

  }

J'ai déjà essayé de rajouter un facteur random à la valeur obtenue grâce à la fonction Perlin Noise mais ça me fait une multitude de stalactites à la surface de mon terrain, ce qui n'est pas l'effet désiré. J'ai aussi essayé de rajouter une boucle qui rajoute un Perlin Noise par dessus le tableau de Heights obtenu mais cela ne fait qu’aplanir mon terrain au lieu de rajouter du bruit.
Je voudrais donc savoir si quelqu'un pouvait m'aider, avait une idée de comment je pourrait arriver au résultat désiré. Je posterai plus tard le script permettant de texturer le terrain, qui est plutôt bien avancé et marche plutôt bien sur un terrain créé à la main mais qui serait encore plus intéressant sur un terrain générer aléatoirement. :)

J'ai aussi essayé de rajouter une boucle qui rajoute un Perlin Noise par dessus le tableau de Heights obtenu mais cela ne fait qu’aplanir mon terrain au lieu de rajouter du bruit.

Avec la même fréquence, ce n'est pas surprenant, mais est-ce que tu as essayé d'additionner un bruit de basse fréquence et de haute amplitude (ce que tu as actuellement, pour le relief global) avec un autre de haute fréquence et de faible amplitude (pour les irrégularités) ?

Je pense vraiment que la solution d'Eusèbe est la bonne.

Si tu veux des vallées (ou chaînes de montagnes), tu perds un peu d'aléatoire, mais il faudra faire un traitement après je pense. Lisser les zones basses proches par exemple.

+1 -0

Hum ok ok. En fait le truc c'est que dans les fonctions de Perlin Noise que ecrites a la mains que j'ai vu sur le net, les parametres permettaient de faire varier la frequence et l'octave mais dans le cas de la fonction fournie pas Unity, il n'y a pas ces parametres la … Il faudrait alors que avant de remplir le tableau des heights avec le sample cree je lui ajoute un autre Perlin Noise cree avec des parametres differents ? Pouvez vous m'aiguiller sur ce point car je ne suis sur de bien comprendre.

Hum donc si je comprends bien, il faudrait que je génère un perlin noise avec un certain scale (qui ici correspond à la fréquence je pense puisque lorsque j'augmente celui-ci, la période diminue) puis un autre avec une fréquence plus grande et ensuite que je les ajoute ? Pour l'instant j'ai essayé de jouer la dessus mais les résultats ne sont pas très concluants, je n'obtiens pas des edges très prononcés. Ils restent arrondis et donc ressemblent à des dunes.

Si tu veux des algorithmes pour mélanger des bruits de manière à avoir des résultats intéressants, je t'invite à jeter un œil à mon dépôt github. Il y a des examples de Mixers, qui permettent de produire des terrains assez cools.

Un exemple de Mixer FBM, but... will it blend ?

Comme tu l'as bien compris, l'idée est de construire ton image finale en additionnant des échelles de plus en plus fines en leur donnant un poids dans l'image finale de plus en plus petits. Les échelles les plus petites apporteront donc du detail, tandis que les premières amèneront la forme globale du terrain.

Pour l'instant mon dépôt implémente deux mixers, le Fractal Brownian Motion (c'est pas moi qui ait inventé le nom héhé) et le Hybrid Multi Fractal.

Le plus gros challenge avec ce genre de mixers, c'est d'arriver a trouver la bonne échelle de base. Donc mon conseil, c'est d'augmenter l'echelle de ton Perlin, jusqu'à ce que sur ton terrain entier il génère une ou deux vallées et une ou deux montagnes, puis d'appliquer l'algorithme de mixage dessus.

Haaa ca fait plaisir d'avoir des retours. Je commencais a penser que cela n'interessait personne hehe. Effectivement j'essaye de mixer differents perlin noise d'amplitudes differentes mais je n'avais pas penser a y ajouter un poids. Je vais aller faire un tour sur les differents liens que tu as poste car cela a l'air tres interessant et je reviens tres bientot pour poster mon avancee sur le sujet.

Bonjour,

desole de l'absence mais j'ai ete tres pris par un projet professionnel ces derniers temps et comme ce defis de generation de terrain procedural est personnel c'est un peu passe au second plan pendant un moment.

Bon du coup je m'y suis remis aujourd'hui et apres avoir lu un article sur un tres bon blog, j'ai recommence mes tests. lien vers le site de l'article

je me heurte cependant a un nouveau probleme que j'avoue ne pas comprendre. J'ai decide d'ajouter un 60 niveau de perlin noise avec des frequences allant de 1 a 60 et donc le poids serait de 1/frequence. Je pense qu'avec des reglages de ce genre je pourrais obtenir quelque chose d'interessant mais pour le moment le terrain generer contient des grandes traces en diagonale. Je me demande vraiment ce qui pourrait causer cet effet.

proceduralterrain

voila si quelqu'un a une idee de ce qui pourrait provoquer cet effet, comment le resoudre … je suis preneur :)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
 using UnityEngine;
using System.Collections;
using UnityEditor;
using System;

public class HeightmapGen : MonoBehaviour {

    private float[,] HeightMap;
    private float[] weights = new float[60];//{ 1f, 1/4f, 1/16f, 1/64f, 1/256f, 1/1024f};//, 0.2f, 4f, 0.5f, 3f }; //new float[30];
    private float[] frequencies = new float[60];// = { 1f, 2f, 4f, 8f, 16f, 32f};// 2f, 4f, 8f, 16f, 32f }; //new float[30];
    private int xTerrainRes;
  private int yTerrainRes;
  private GameObject myTerrain;
  private TerrainData tData;
  public float xOrg = 0;
  public float yOrg = 0;
  public float frequency = 1.0F;
  private float previousFrequency = 0.0F;
  private int i = 0;
  private int j = 0;
  public Texture2D terrainTex;
  public int passes = 2;
  public float amplitude = 1.0F;  //terrain height
  private float previousAmplitude = 1.0F;

  // Use this for initialization
  void Start ()
  {
      previousFrequency = frequency;
      previousAmplitude = amplitude;
        for (int i = 0; i < 60; i++)
        {
            frequencies[i] = (i + 1);
            weights[i] = Mathf.Pow(frequencies[i], (-1f));
        }
        generateNewTerrain();
  }
      
  void generateNewTerrain()
  {
      //set the data of the terrain we will create
      tData = new TerrainData();
      tData.size = new Vector3(32, 500, 32);
      tData.heightmapResolution = 513;
      tData.SetDetailResolution(512, 8);
      HeightMap = tData.GetHeights(0, 0, tData.heightmapWidth, tData.heightmapHeight); //set the HeightMap tab to make it correspond to the actual height tab for our terrain


        
        for (int i = 0; i < passes; i++)
        {
            int y = 0;
            while (y < tData.heightmapHeight)
            {
                int x = 0;
                while (x < tData.heightmapWidth)
                {
                    float xCoord = Convert.ToSingle(x) / Convert.ToSingle(tData.heightmapWidth - 1); //need to convert everything to float in order for the Perling Noise function to work
                    float yCoord = Convert.ToSingle(y) / Convert.ToSingle(tData.heightmapHeight - 1); // same
                    float sample = Mathf.PerlinNoise(xCoord * frequencies[i] * frequency, yCoord * frequencies[i] * frequency); //generate height value for a vertex of the terrain with the Perlin Noise function
                    HeightMap[x, y] += sample * weights[i] * amplitude; //put the generated value in the corresponding table of heights
                    x++;
                }
                y++;
            }
        }

        int p = 0;
        while (p < tData.heightmapHeight)
        {
            int x = 0;
            while (x < tData.heightmapWidth)
            {
                HeightMap[x, p] = HeightMap[x, p] / SumWeights(); //put the generated value in the corresponding table of heights
                x++;
            }
            p++;
        }

        tData.SetHeights(0, 0, HeightMap); //put the generated heights into our terrain data

      //set the texture of the terrain
      SplatPrototype[] tex = new SplatPrototype[1];
      tex[0] = new SplatPrototype();
      tex[0].texture = terrainTex;
      tex[0].tileSize = new Vector2(1,1);
      tData.splatPrototypes = tex;

      //create the terrain data object as an asset
      AssetDatabase.CreateAsset(tData, "Assets/testTerrain.asset");

      //create the terrain
      myTerrain = Terrain.CreateTerrainGameObject(tData);
      myTerrain.transform.position = new Vector3(-250f, 0f, -250f);

  }
  
  // Update is called once per frame
  void Update ()
  {
      //if the scale has changed, then destroy the previous terrain and generate a new one with the current scale.
      if(frequency != previousFrequency || amplitude != previousAmplitude)
      {
          Destroy(myTerrain);
          generateNewTerrain();
          previousFrequency = frequency;
          previousAmplitude = amplitude;
      }
          
  }

    float SumWeights()
    {
        float sumWeights = 0f;
        for(int i = 0; i < weights.Length; i++)
        {
            sumWeights += weights[i];
        }

        return sumWeights;
    }
}

EDIT:
je n'ai pas trouve vraiment quel etait le probleme mais je viens de faire des tests avec des frequences correspondants a des puissances de 2 (ce qui est apparemment appele octaves dans les articles que j'ai lu sur le sujet) et cet effet de rayure a disparu. Peut etre que la fonction Perlin Noise marche mieux avec des octaves … Du coup en generant un terrain avec 5 passes, le resultat est plutot satisfaisant. Je vais maintenant essayer de rajouter un smooth sur les valeurs basses afin que les parties plaines soient un peu moins bosselees.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte