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

Modèle 3D du terrain

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 - Modèle 3D du terrain


III. Résumé

Dans cet épisode, vous allez créer un modèle 3D à partir de la carte des hauteurs afin de créer le relief de votre terrain.

III-A. Implémentation

III-A-1. Explications de la méthode

Le modèle est créé à partir de triangles. Pour constituer les triangles, vous devez passer à Unity une liste (tableau à une dimension) des sommets que vous souhaitez afficher.

Si vous avez un tableau de neuf cases comme suit :

 
Sélectionnez
0 1 2
3 4 5
6 7 8

Le premier triangle sera :

 
Sélectionnez
0, 4, 3

L'ordre des sommets est important. En effet, l'ordre par défaut est un ordre allant dans le sens des aiguilles d'une montre. En réalité, cette convention est utilisée par la carte graphique afin de connaître quelle est la face avant et la face arrière. Aussi, si vous utilisez la suppression de la face arrière (« backface culling »), l'ordre est d'autant plus important à suivre, sans quoi vous ne verrez pas votre triangle.

Le deuxième triangle sera :

 
Sélectionnez
4, 0, 1

Et ainsi de suite.

Aussi, vous devez être en mesure de calculer :

  • le nombre de sommets : largeur * hauteur ;
  • le nombre de triangles : (largeur -1) * (hauteur -1) * 6.

III-A-2. Script

La création du modèle de terrain se fait dans un nouveau script, appelé MeshGenerator.cs :

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

public static class MeshGenerator {

    public static MeshData GenerateTerrainMesh(float[,] heightMap) {
        int width = heightMap.GetLength (0);
        int height = heightMap.GetLength (1);
        // Permet de centrer le modèle
        float topLeftX = (width - 1) / -2f;
        float topLeftZ = (height - 1) / 2f;

        MeshData meshData = new MeshData (width, height);
        int vertexIndex = 0;

        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {

                meshData.vertices [vertexIndex] = new Vector3 (topLeftX + x, heightMap [x, y], topLeftZ - y);
                meshData.uvs [vertexIndex] = new Vector2 (x / (float)width, y / (float)height);

                if (x < width - 1 && y < height - 1) {
                    meshData.AddTriangle (vertexIndex, vertexIndex + width + 1, vertexIndex + width);
                    meshData.AddTriangle (vertexIndex + width + 1, vertexIndex, vertexIndex + 1);
                }

                vertexIndex++;
            }
        }

        return meshData;

    }
}

Évidemment, la fonction de génération GenerateTerrainMesh() prend en paramètre la carte des hauteurs. Elle produit les sommets du terrain ainsi que les triangles pour l'affichage.

La fonction génère aussi les coordonnées de texture pour le terrain, ici appelés uvs. Leurs valeurs sont comprises entre 0 et 1.

Pour des soucis de simplicité, une classe MeshData est créée afin de regrouper les sommets et les triangles :

 
Sélectionnez
public class MeshData {
    public Vector3[] vertices;
    public int[] triangles;
    public Vector2[] uvs;

    int triangleIndex;

    public MeshData(int meshWidth, int meshHeight) {
        vertices = new Vector3[meshWidth * meshHeight];
        uvs = new Vector2[meshWidth * meshHeight];
        triangles = new int[(meshWidth-1)*(meshHeight-1)*6];
    }

    public void AddTriangle(int a, int b, int c) {
        triangles [triangleIndex] = a;
        triangles [triangleIndex + 1] = b;
        triangles [triangleIndex + 2] = c;
        triangleIndex += 3;
    }

    public Mesh CreateMesh() {
        Mesh mesh = new Mesh ();
        mesh.vertices = vertices;
        mesh.triangles = triangles;
        mesh.uv = uvs;
        mesh.RecalculateNormals ();
        return mesh;
    }

}

Celle-ci permet de facilement créer les tableaux qui seront passés à Unity, ajouter des triangles et de créer le modèle à partir de ceux-ci.

C'est un MeshData qui est retourné par la fonction GenerateTerrainMesh() afin de permettre l'implémentation du multithread par la suite et ainsi permettre de ne pas bloquer le jeu.
Aussi, il n'est pas possible de créer un modèle (Mesh) au sein d'un thread et de l'utiliser dans un autre.

III-A-3. Affichage

Pour permettre l'affichage du modèle, nous complétons l'énumération DrawMode et ajoutons une fonction DrawMesh() :

 
Sélectionnez
public void DrawMesh(MeshData meshData, Texture2D texture) {
    meshFilter.sharedMesh = meshData.CreateMesh ();
    meshRenderer.sharedMaterial.mainTexture = texture;
}

Son appel, réalisé dans le fichier MapGenerator.cs est réalisé comme suit :

 
Sélectionnez
display.DrawMesh (MeshGenerator.GenerateTerrainMesh (noiseMap), TextureGenerator.TextureFromColourMap (colourMap, mapWidth, mapHeight));

Actuellement, il n'est pas possible d'afficher un modèle trop grand. Cela sera corrigé dans un prochain épisode, dans lequel la carte sera générée par morceau.

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.