Unite Europe 2016

La puissance des modèles procéduraux

L'auteur

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Introduction

Alexander Birke (fondateur du studio Out Of Bounds Games) a donné une conférence lors du Unite Europe 2016 sur la génération procédurale de modèle 3D.

II. Vidéo


Unite Europe 2016 - La puissance des modèles procéduraux


III. Résumé

Au cours de cette conférence, Alexander Birke montre la puissance de la génération procédurale de modèle 3D. En effet, son jeu (premier jeu du studio Out Of Bounds Games) utilise grandement cette technique.

III-A. Pourquoi choisir la génération procédurale de modèle ?

Alexander Birke présente les intérêts de la génération procédurale de modèle en trois catégories :

  • le contenu généré par le joueur (Spore) ;
  • des mécanismes de jeu unique ;
  • la génération procédurale de niveau/monde.

III-B. Les bases

Un modèle est constitué de points (sommets) connectés en triangles. Suivant le matériel que vous voulez appliquer sur votre modèle, vous devez aussi fournir des informations pour les normales, les coordonnées de textures… pour chaque sommet.

Dans Unity, vous avez des composants MeshFilter (stockage du modèle) et MeshRenderer (affichage du modèle), et un script pour générer le modèle.

III-B-1. Créer un triangle

Voici le code pour générer un triangle :

 
Sélectionnez
void Start()
{
    Mesh mesh = new Mesh();
    
    // assigner les sommets
    Vector3[] vertices = new Vector3[3];
    vertices[0] = new Vector3(-1, -1, 0);
    vertices[1] = new Vector3(0, 0.8f, 0);
    vertices[2] = new Vector3(1, -1, 0);
    mesh.vertices = vertices;
    
    // assigner les triangles
    int[] triangles = new int[3] {0, 1, 2};
    mesh.triangles = triangles;
    
    GetComponent<MeshFilter>().mesh = mesh;
}

Ensuite, si vous souhaitez indiquer au moteur quelle est l'orientation de votre modèle (utile pour l'ombrage), vous devez lui indiquer les normales des sommets :

 
Sélectionnez
Vector3[] normals = new Vector3[3];
normals[0] = Vector3.back; normals[1] = Vector3.back; normals[2] = Vector3.back;
mesh.normals = normals;

Évidemment, vous pouvez rajouter des couleurs :

 
Sélectionnez
void Update()
{
    Color[] colors = new Color[3];
    
    float offset = Time.time * speed;
    
    colors[0] = ColorFromHue(0 + offset);
    colors[1] = ColorFromHue(120 + offset);
    colors[2] = ColorFromHue(240 + offset);
    
    mesh.colors = colors;
}

Vous pouvez aussi utiliser des octets à la place des nombres flottants. Cela peut vous permettre d'améliorer les performances en compactant les données à envoyer au GPU.

Toutefois, les couleurs de sommets ne sont pas courantes. On préférera certainement utiliser des textures. Pour cela, il faut indiquer à Unity comment appliquer la texture à notre modèle :

 
Sélectionnez
// assigner des coordonnées de texture
Vector2[] uvs = new Vector2[4];
uvs[0] = new Vector2(0, 0);
uvs[1] = new Vector2(0, 1);
uvs[2] = new Vector2(1, 1);
uvs[3] = new Vector2(1, 0);
mesh.uv = uvs;

Finalement, votre modèle sera constitué de plusieurs triangles. Pour cela, il faut indiquer comment les sommets sont connectés pour former des triangles à l'aide des indices. Voici le code pour générer les indices d'un plan :

 
Sélectionnez
void GenerateTriangles()
{
    int[] triangles = new int[(width - 1) * (width - 1) * 6];
    
    int triangleIndex = 0;
    for(int x = 0; x < width - 1; x++)
    {
    for(int y = 0; y < width - 1; y++)
    {
        int vertexIndex = x * width + y;
        
        triangles[triangleIndex + 0] = vertexIndex;
        triangles[triangleIndex + 1] = vertexIndex + width;
        triangles[triangleIndex + 2] = vertexIndex + width + 1;
        
        triangles[triangleIndex + 3] = vertexIndex;
        triangles[triangleIndex + 4] = vertexIndex + width + 1;
        triangles[triangleIndex + 5] = vertexIndex + 1;
        
        triangleIndex += 6;
    }
    }
    
    mesh.triangles = triangles;
}

III-C. Exemples de génération procédurale

À l'aide de la génération procédurale, vous pouvez :

  • créer un monde 2D (jeu de plates-formes) s'enroulant sur lui-même ;
  • dessiner des lasers qui rebondissent sur les murs ;
  • gérer des lumières à travers un modèle et une texture ;
  • générer des niveaux aléatoirement (triangulation de Delaunay).

III-D. Débogage

Pour déboguer votre modèle, faites en sorte que vous puissiez le voir dans le moteur. Pour cela, vous pouvez utiliser les classes Gizmos et Debug. Aussi, il est probable que si vous ne voyez pas votre modèle, c'est qu'il n'est pas positionné là où vous le souhaitez : tournez la caméra. Finalement, passez en mode fil de fer.

III-E. Optimisation

III-E-1. Modèle statique

La fonction Mesh.Optimize n'a aucun effet. Vous devez gérer vous-même l'optimisation du modèle.

Une technique pour optimiser le rendu est d'utiliser le batching à travers StaticBatchingUtility. Ainsi vous rassemblerez le rendu de plusieurs modèles en un appel de rendu.

De même, si vos modèles ne se cachent pas les uns des autres, vous pouvez combiner vos modèles en un seul avec Mesh.CombineMeshes.

III-E-2. Modèle dynamique

Avec la fonction Mesh.MarkDynamic, vous pouvez indiquer à la couche de rendu bas niveau que vos modèles vont souvent être modifiés.

Aussi, il est possible d'accélérer le rendu en créant un double tampon pour les modèles. En contrepartie, cela utilise plus de mémoire.

Il est possible d'envoyer directement des Lists au travers de Mesh.SetVertices pour définir les propriétés des modèles. Cela est plus rapide que d'utiliser des tableaux.

N'oubliez pas le frustum culling : n'affichez pas les choses hors de l'écran.

Si vos modèles se déplacent beaucoup, il peut être préférable d'utiliser un skinned modèle.

Avec les CPU multicœurs, il est évident de penser à générer les modèles en utilisant l'ensemble des cœurs de la machine. Toutefois, la bibliothèque Unity n'est pas fiable en multithread. Mais, il est possible de contourner cela pour la génération des sommets et des informations associées. Ensuite, vous renvoyez les données au thread principal afin que celui-ci les envoie à la classe Mesh de Unity.

Les plates-formes modernes (OpenGL 4.3, DirectX 11, OpenGL ES 3.1, PS4, XBone) supportent les compute shaders et peuvent être utilisés pour des tâches parallèles exécutées sur le GPU.

IV. Ressources

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 © 2016 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.