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

Correction des trous

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 - Correction des trous


III. Résumé

Les morceaux de terrain ayant un niveau de détail différent ne s'alignent pas parfaitement. Par conséquent, le terrain peut avoir des trous. Ce dernier chapitre de la série apporte une solution à ce problème.

III-A. Implémentation

Pour régler les soucis, les morceaux de terrain auront leur bordure avec le niveau de détail maximal.
Toutefois, cette solution rend visible la bordure lorsque les morceaux de terrains sont du plus bas niveau de détail. En effet, les bordures auront un niveau de détail bien plus grand que le reste de la surface. Par contre, c'est négligeable car cela ne sera visible que sur des morceaux très loin du joueur.

Pour implémenter cette correction, des sommets supplémentaires seront générés, en bordure du modèle (comme cela l'a été pour les normales). Le premier changement consiste à modifier le calcul du nombre de sommets par ligne :

MeshSettings.cs
Sélectionnez
public int numVertsPerLine {
        get {
            return supportedChunkSizes [(useFlatShading) ? flatshadedChunkSizeIndex : chunkSizeIndex] + 5;
        }
}

Ensuite, il est nécessaire de modifier la fonction MeshGenerator::GenerateTerrainChunk() pour modifier la génération des sommets. La première étape est de clarifier le code en supprimant les variables borderedSize, meshSize, meshSizeSimplified et verticesPerLine. Ces variables sont remplacées par :

GenerateTerrainMesh.cs
Sélectionnez
int numVertsPerLine = meshSettings.numVertsPerLine;

La variable meshSimplificationIncrement est renommée skipIncrement, IsBorderVertex est renommée isOutOfMeshVertex et borderVertexIndex devient outOfMeshVertexIndex.

Ensuite, il faut trouver une formule pour savoir si le sommet est à ignorer ou non :

 
Sélectionnez
bool isSkippedVertex = x > 2 && x < numVertsPerLine - 3 && y > 2 && y < numVertsPerLine - 3 && ((x - 2) % skipIncrement != 0 || (y - 2) % skipIncrement != 0);

D'origine, l'incrément utilisé pour les boucles de parcours des sommets est calculé grâce au niveau de détail. Maintenant, l'incrément est de 1..

Ensuite, il faut déterminer un procédé pour savoir quel sommet fait partie de quel sous- ensemble. De plus, il faut déterminer s'il y a besoin de construire un triangle pour tel ou tel sommet :

 
Sélectionnez
bool isOutOfMeshVertex = y == 0 || y == numVertsPerLine - 1 || x == 0 || x == numVertsPerLine - 1;
bool isMeshEdgeVertex = (y == 1 || y == numVertsPerLine - 2 || x == 1 || x == numVertsPerLine - 2) && !isOutOfMeshVertex;
bool isMainVertex = (x - 2) % skipIncrement == 0 && (y - 2) % skipIncrement == 0 && !isOutOfMeshVertex && !isMeshEdgeVertex;
bool isEdgeConnectionVertex = (y == 2 || y == numVertsPerLine - 3 || x == 2 || x == numVertsPerLine - 3) && !isOutOfMeshVertex && !isMeshEdgeVertex && !isMainVertex;



bool createTriangle = x < numVertsPerLine - 1 && y < numVertsPerLine - 1 && (!isEdgeConnectionVertex || (x != 2 && y != 2));

L'utilisation d'un brouillon est plus que recommandé pour résoudre un tel problème et trouver les bonnes formules.

Évidemment, il va falloir revoir la méthode pour prendre les bons sommets pour construire les triangles :

 
Sélectionnez
int currentIncrement = (isMainVertex && x != numVertsPerLine - 3 && y != numVertsPerLine - 3) ? skipIncrement : 1;

int a = vertexIndicesMap [x, y];
int b = vertexIndicesMap [x + currentIncrement, y];
int c = vertexIndicesMap [x, y + currentIncrement];
int d = vertexIndicesMap [x + currentIncrement, y + currentIncrement];
meshData.AddTriangle (a, d, c);
meshData.AddTriangle (d, a, b);

En outre, il faut revoir la création des tableaux contenant les sommets et les triangles :

 
Sélectionnez
public MeshData(int numVertsPerLine, int skipIncrement, bool useFlatShading) {
    this.useFlatShading = useFlatShading;

    int numMeshEdgeVertices = (numVertsPerLine - 2) * 4 - 4;
    int numEdgeConnectionVertices = (skipIncrement - 1) * (numVertsPerLine - 5) / skipIncrement * 4;
    int numMainVerticesPerLine = (numVertsPerLine - 5) / skipIncrement + 1;
    int numMainVertices = numMainVerticesPerLine * numMainVerticesPerLine;

    vertices = new Vector3[numMeshEdgeVertices + numEdgeConnectionVertices + numMainVertices];
    uvs = new Vector2[vertices.Length];

    int numMeshEdgeTriangles = 8 * (numVertsPerLine - 4);
    int numMainTriangles = (numMainVerticesPerLine - 1) * (numMainVerticesPerLine - 1) * 2;
    triangles = new int[(numMeshEdgeTriangles + numMainTriangles) * 3];

    outOfMeshVertices = new Vector3[numVertsPerLine * 4 - 4];
    outOfMeshTriangles = new int[24 * (numVertsPerLine-2)];
}

Pour que la connexion soit correcte entre les niveaux de détail, il faut repositionner les sommets à l'intersection des deux niveaux de détail :

 
Sélectionnez
if (isEdgeConnectionVertex) {
    bool isVertical = x == 2 || x == numVertsPerLine - 3;
    int dstToMainVertexA = ((isVertical)?y - 2:x-2) % skipIncrement;
    int dstToMainVertexB = skipIncrement - dstToMainVertexA;
    float dstPercentFromAToB = dstToMainVertexA / (float)skipIncrement;

    float heightMainVertexA = heightMap [(isVertical) ? x : x - dstToMainVertexA, (isVertical) ? y - dstToMainVertexA : y];
    float heightMainVertexB = heightMap [(isVertical) ? x : x + dstToMainVertexB, (isVertical) ? y + dstToMainVertexB : y];

    height = heightMainVertexA * (1 - dstPercentFromAToB) + heightMainVertexB * dstPercentFromAToB;
}

IV. Code source

Vous pouvez consulter le code de ce chapitre sur le GitHub de Sebastian Lague.

V. 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 © 2018 Unity Technologies. Aucune reproduction, même partielle, ne peut être faite de ce site ni 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.