13/17

6. Structure indexée

Considérons le cas où l’on souhaite afficher un quadrangle composé de 4 sommets aux positions respectives \(p_0=(0,0,0)\), \(p_1=(1,0,0)\), \(p_2=(1,1,0)\), \(p_3=(0,1,0)\). Ce quadrangle est composé lui-même de deux triangles \((p_0,p_1,p_2)\) et \((p_0,p_2,p_3)\).

Il est possible d’envoyer à la carte graphique l’information de ces deux triangles sous la forme de 6 coordonnées de sommets. Cependant, cette approche revient à dupliquer l’information des coordonnées de \(p_0\) et \(p_2\). Au delà de la redondance de données, cette structure ne tient pas compte du fait que les sommets \(p_0\) et \(p_2\) correspondent, en principe, à un seul et même point de l’espace. Ainsi, modifier la coordonnée \(p_0\) nécessite une modification à deux endroits différents du buffer. Plus généralement, les maillages possèdent des sommets partagés entre un nombre variable de triangles. Modifier ces coordonnées nécessite, avec cette approche, de gérer l’ensemble des duplications, ce qui est source d’erreur.

Une manière plus efficace d’encoder une structure de maillage est de séparer la notion de coordonnées géométrique de la connectivité. Cet encodage basé sur l’indexation (indexing) est géré de manière native par OpenGL. Un maillage est ainsi représenté par deux structures

  • La géométrie, correspondant à un vecteur de coordonnées contigües en mémoires (std::vector de float). Chaque coordonnée de sommet n’y est mentionnée qu’une seule fois. Les sommets ne suivent pas d’ordre particulier.

  • La connectivité, correspondant à un vecteur d’indices (std::vector de unsigned int). Chaque triplet consécutif indique les trois sommets d’un triangle. Chaque indice indique la position des coordonnées dans le vecteur de géométrie.

Application au quadrangle

Déclaration de la structure géométrie/connectivité

    // Geometry (vertex position)
    const std::vector<GLfloat> position = {
        -0.5f, -0.5f, 0.0f,
         0.5f, -0.5f, 0.0f,
         0.5f,  0.5f, 0.0f,
        -0.5f,  0.5f, 0.0f
    };

    // Connectivity (triangle index)
    const std::vector<GLuint> index = {
         0, 1, 2,
         0, 2, 3
    };


Envoie des données au GPU

Les indices définissant la connectivité doivent être stockés sur la mémoire du GPU en suivant la même procédure que les VBO de valeurs floats. Attention au fait que le type associé au VBO stockant les indices est GL_ELEMENT_ARRAY_BUFFER (et non pas GL_ARRAY_BUFFER).

    // Fill VBO for position
    GLuint vbo_position = 0;
    glGenBuffers(1, &vbo_position);
    glBindBuffer(GL_ARRAY_BUFFER, vbo_position);
    glBufferData(GL_ARRAY_BUFFER, position.size()*sizeof(GLfloat), &position[0], GL_STATIC_DRAW );
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    // Fill VBO for index
    glGenBuffers(1, &vbo_index);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_index);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, index.size()*sizeof(GLuint), &index[0], GL_STATIC_DRAW );
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);


Demande d’affichage réalisée à l’aide de la fonction glDrawElements

    // Draw call
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_index);
    glDrawElements(GL_TRIANGLES, 3*2, GL_UNSIGNED_INT, nullptr);
  • glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices)

    • mode: Indique le type de primitive à afficher. OpenGL est capable d’afficher différents types de primitives. Dans notre cas, GL_TRIANGLES interprète chaque triplet d’indice comme un triangle distinct (manière la plus générique de définir un ensemble de triangles). D’autres interprétations sont des triangles sous la forme STRIP ou FAN pouvant être utilisés afin de limiter l’utilisation mémoire et d’accélérer le rendu sur GPU. Finalement, des segments (appelés line en OpenGL) peuvent également être rendus.

    • count: Nombre total de sommets à être rendu. Dans le cas présent: 6 sommets (3 sommets par triangles \(\times\) 2 triangles)

    • type: Le type de données utilisé pour stocker les indices (unsigned integer ici).

    • indices: L’offset (sous forme de pointeur) indiquant le premier élément à être rendu (ici offset à 0).


Compilez et exécutez le code correspondant à l’archive suivante

quadrangle

Ajoutez un triangle supplémentaire afin d’obtenir la figure suivante

extra triangle