Unity - Génération procédurale de terrain

Octaves

L'auteur

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Introduction

Cette série explique comment générer de manière procédurale des continents dans Unity.
Vous pouvez retrouver les autres épisodes de cette série dans le sommaire dédié.

II. Vidéo


Unity - Génération procédurale de terrain - Octaves


III. Résumé

Dans la vidéo précédente, nous avons mis en place la génération d'une carte des hauteurs (« heightmap ») basique (une seule passe) et une méthode pour l'afficher au sein de l'éditeur. Comme vu en introduction, afin d'obtenir un terrain plus réaliste, nous réalisons plusieurs passes pour affiner la génération et augmenter les détails de la carte.

III-A. Implémentation

Pour cela, nous mettons à jour le script Noise.cs :

Noise.cs
Sélectionnez
using UnityEngine;
using System.Collections;

public static class Noise {

    public static float[,] GenerateNoiseMap(int mapWidth, int mapHeight, int seed, float scale, int octaves, float persistance, float lacunarity, Vector2 offset) {
        float[,] noiseMap = new float[mapWidth,mapHeight];

        System.Random prng = new System.Random (seed);
        Vector2[] octaveOffsets = new Vector2[octaves];
        for (int i = 0; i < octaves; i++) {
            float offsetX = prng.Next (-100000, 100000) + offset.x;
            float offsetY = prng.Next (-100000, 100000) + offset.y;
            octaveOffsets [i] = new Vector2 (offsetX, offsetY);
        }

        if (scale <= 0) {
            scale = 0.0001f;
        }

        float maxNoiseHeight = float.MinValue;
        float minNoiseHeight = float.MaxValue;

        // Permet que le paramètre noise provoque un zoom au centre de la carte
        float halfWidth = mapWidth / 2f;
        float halfHeight = mapHeight / 2f;


        for (int y = 0; y < mapHeight; y++) {
            for (int x = 0; x < mapWidth; x++) {
        
                float amplitude = 1;
                float frequency = 1;
                float noiseHeight = 0;

                for (int i = 0; i < octaves; i++) {
                    float sampleX = (x-halfWidth) / scale * frequency + octaveOffsets[i].x;
                    float sampleY = (y-halfHeight) / scale * frequency + octaveOffsets[i].y;

                    float perlinValue = Mathf.PerlinNoise (sampleX, sampleY) * 2 - 1;
                    noiseHeight += perlinValue * amplitude;

                    amplitude *= persistance;
                    frequency *= lacunarity;
                }

                // Garde une trace des valeurs minimale et maximale générées
                if (noiseHeight > maxNoiseHeight) {
                    maxNoiseHeight = noiseHeight;
                } else if (noiseHeight < minNoiseHeight) {
                    minNoiseHeight = noiseHeight;
                }
                noiseMap [x, y] = noiseHeight;
            }
        }

        // Normalisation
        for (int y = 0; y < mapHeight; y++) {
            for (int x = 0; x < mapWidth; x++) {
                noiseMap [x, y] = Mathf.InverseLerp (minNoiseHeight, maxNoiseHeight, noiseMap [x, y]);
            }
        }

        return noiseMap;
    }

}

Nous ajoutons trois paramètres :

  • le nombre d'octaves (de passes) ;
  • la persistance ;
  • la lacunarité ;

Pour chaque passe, l'amplitude et la fréquence sont modifiées afin de prendre en compte la persistance et la lacunarité. Ainsi, chaque passe aura un impact de moins en moins important mais une irrégularité de plus en plus forte.
Aussi, afin de permettre des valeurs négatives provenant du bruit de Perlin, nous multiplions la valeur retournée par la fonction par 2 et lui soustrayons 1. Sachant que ces valeurs sont comprises entre 0 et 1, après transformation nous obtiendrons des valeurs entre -1 et 1.

Toutefois, nous ne souhaitons pas avoir de valeurs négatives dans notre carte des hauteurs. Donc, après génération, nous effectuons une passe de normalisation. Pour cela, il faut connaître la plus petite et la plus grande valeur générée. La normalisation s'effectue avec la fonction Mathf.InverseLerp().

Pour tester le nouveau code, il faut modifier le script MapGenerator.cs afin de rendre visibles dans l'éditeur les nouveaux paramètres et de les passer lors de l'appel à la fonction GenerateNoiseMap().

Pour ajouter un aspect unique à notre génération, nous ajoutons un paramètre seed. Ainsi, nous pouvons générer des valeurs aléatoires utilisées pour décaler l'emplacement où l'échantillonnage du bruit de Perlin est effectué.

La fonction Mathf.PerlinNoise() possède une limite pour laquelle si vous passez des nombres trop grands (> 100000), elle retournera toujours la même valeur.

Dans le chapitre précédent, nous avons vu que la taille de la carte devait être différente de 0. Vous pouvez empêcher l'utilisateur d'utiliser une telle valeur grâce à la méthode onValidate() dans le script MapGenerator.cs :

 
Sélectionnez
using UnityEngine;
using System.Collections;

public class MapGenerator : MonoBehaviour {

    public int mapWidth;
    public int mapHeight;
    public float noiseScale;

    public int octaves;
    [Range(0,1)]
    public float persistance;
    public float lacunarity;

    public int seed;
    public Vector2 offset;

    public bool autoUpdate;

    public void GenerateMap() {
        float[,] noiseMap = Noise.GenerateNoiseMap (mapWidth, mapHeight, seed, noiseScale, octaves, persistance, lacunarity, offset);


        MapDisplay display = FindObjectOfType<MapDisplay> ();
        display.DrawNoiseMap (noiseMap);
    }

    void OnValidate() {
        if (mapWidth < 1) {
            mapWidth = 1;
        }
        if (mapHeight < 1) {
            mapHeight = 1;
        }
        if (lacunarity < 1) {
            lacunarity = 1;
        }
        if (octaves < 0) {
            octaves = 0;
        }
    }

}

IV. Commenter

Vous pouvez commenter et donner vos avis dans la discussion associée sur le forum.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 2017 Unity Technologies. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.