28 avril 2012

Petite introduction à OpenGL

 

En attendant le prochain article sur Newt-Puzzle, voici quelques explications à propos d’OpenGL. Pour ceux qui ne connaissent pas trop ce monde fascinant du rendu 3D, cet article me permettra par la suite de ne pas avoir à tout expliquer depuis la base, je renverrai simplement à cette page. Par contre, cela n’apprendra pas grand chose à ceux qui sont déjà familiarisés avec l’API OpenGL.

 

OpenGL, c’est quoi ?

OpenGL est une spécification  qui définit une API bas niveau1 pour le rendu d’images 3D ou 2D. Pour pouvoir l’utiliser, il faut donc une implémentation de ces spécifications. Ce sont généralement les constructeurs de matériels graphiques qui la fournissent, par l’intermédiaire des pilotes. On peut citer également une implémentation libre, Mesa 3D2, dont les Linuxiens ont probablement entendu parler : elle est utilisée par X.Org pour les pilotes graphiques libres.

La spécification OpenGL fournit une unique interface, qui ne dépend ni du système d’exploitation, ni du matériel, ce qui permet de développer des applications portables. Un système d’extensions permet aux constructeurs de matériels graphiques d’ajouter des fonctionnalités supplémentaires. Côté application, il faudra alors vérifier que l’extension est bien disponible pour pouvoir s’en servir. Certaines extensions sont intégrées au cœur d’OpenGL au fur et à mesure de son évolution.

 

Le pipeline de rendu


Le schéma ci-contre (cliquez dessus pour agrandir) illustre les différentes étapes du rendu avec OpenGL3 dans sa dernière version, la 4.2.

En (très) gros, on met d’un côté des données d’entrées comme la géométrie de la scène qu’on veut rendre : les  sommets des polygones avec leurs coordonnées, leurs normales ou tout autre information qui peut servir, et des listes d’indices qui vont dire comment relier ces sommets entre eux pour en faire des polygones4. On envoie également des images (textures) à plaquer sur ces polygones et diverses données pour décrire des informations comme l’éclairage (où sont les sources de lumière par exemple). Tout cela traverse joyeusement le pipeline5, et à la sortie on obtient une belle image qui pourra être affichée sur l’écran. Ou, si un vilain bug traine quelque part, un bazar incompréhensible, voire un écran désespérement tout noir.

Quelques explications sur certaines étapes du rendu :

  • Opérations sur les sommets (vertex program ou vertex shader) : tous les calculs au niveau des sommets. Typiquement, c’est à cette étape qu’on va calculer les coordonnées du sommet en fonction des transformations qu’il a pu subir et de la position de la « caméra ». A noter que chaque sommet est traité indépendamment, les calculs sont donc fait en parallèle.
  • Assemblage des primitives : on relie les sommets qui vont ensemble pour en faire des triangles.
  • Rasterization : on convertit les informations précédentes en fragments (en gros un fragment correspond à un pixel). Les données des sommets (comme la normale) pourront soit être interpolées sur chaque fragment, soit on ne prend que la moyenne sur les sommets qui forment le triangle.
  • Opérations sur les fragments (fragment program ou fragment shader) : c’est par exemple à cette étape qu’on va récupérer les données de textures pour habiller ses objets. Comme pour les sommets, chaque fragment est traité de manière indépendantes des autres, et tout est calculé en parallèle.
  • Différents tests (qui peuvent être désactivés si on le souhaite) qui vont dire si on envoie ou non le fragment à l’étape suivante. Par exemple, un test de profondeur pour regarder si le fragment n’est pas masqué par un autre plus proche de la « caméra » : si c’est le cas, le fragment n’est pas affiché (et on l’a calculé pour rien…).
  • Eventuellement des opérations de blending (mélange). Par exemple si on a des objets transparents qui se superposent, la couleur résultante sera un mélange des couleurs des différents objets.

 

Principales évolutions d’OpenGL

Depuis 1992, l’API a beaucoup évolué, pour tenir compte des nouvelles possibilités des cartes graphiques. La liste ci-dessous est très loin d’être exhaustive, je n’ai retenu que celles qui me paraissaient les plus marquantes. A noter que souvent, les fonctionnalités offertes par la version n d’OpenGL pouvaient souvent être déjà accessibles avec la version n-1 par l’intermédiaire d’extensions.

Avec OpenGL 1.x, le pipeline de rendu est fixe, aucune étape n’est programmable. On peut juste activer ou désactiver certains états. Par exemple, les calculs d’éclairage sont automatiquement faits au niveau des sommets, et interpolés sur les faces des polygones 6

Avec OpenGL 2 (sortie en 2004), les étapes de calculs sur les sommets et les fragments deviennent programmables, grâce aux shaders, ouvrant des possibilités quasiment infinies.7. Par exemple, une texture n’est plus nécessairement utilisée directement comme image qu’on va plaquer sur un objet tel un papier peint sur les murs de sa chambre, mais peut être considérée de manière plus générale comme un tableau contenant des données. Données qui peuvent représenter à peu près n’importe quoi : des normales pour faire du bump mapping, des coefficents indiquant si le matériau est plutôt mat ou brillant, etc.

Alors que jusque-là les versions d’OpenGL assuraient qu’une application resterait compatible avec la version suivante, OpenGL 3 (sortie en 2008) marque une rupture 8 avec une refonte de l’API. De nombreuses fonctions obsolètes sont supprimées : leur implémentation n’est plus assurée autrement que par une extension spécifique 9. C’est en particulier le cas des opérations sur les sommets ou les fragments du pipeline fixe : désormais, on doit obligatoirement passer par des shaders. OpenGL 3 rajoute entre autres dans le standard les geometry shaders, étape facultative sur les primitives qui a lieu juste après les opérations sur les sommets, ou les frame buffer objects qui permettent de faire un rendu dans une texture plutôt que directement à l’écran (intéressant pour certains effets, comme les ombres, les réflexions/réfractions ou encore des post-traitements de l’image).

Enfin, OpenGL 4 (2010) apporte entre autres nouveautés le support de la tesselation, opération qui consiste à subdiviser un polygone en polygones plus petits. L’intérêt est de pouvoir détailler davantage une géométrie à la demande, sans transition brutales (plutôt qu’avoir plusieurs modèles du même objet et choisir lequel on affiche suivant que la « caméra » est proche ou non).

 

Et mes projets de jeux dans tout ça ?

Pour Newt-Puzzle, je voulais initialement que le jeu soit compatible avec le plus grand nombre de machines possibles, y compris des ordinosaures. Puis, comme expliqué dans mon précédent article, j’ai découvert le monde merveilleux des shaders, alors j’ai voulu en rajouter… Maintenant que j’ai repris le jeu, je me suis fixé comme objectif de faire une version basique, compatible OpenGL 1.310, et de proposer aussi un rendu un peu plus élaboré avec quelques shaders, en utilisant des extensions OpenGL 2, qui ne pourront bien sûr être utilisées que si elles sont supportées.

Pour Maeryn, mon autre projet, j’ai l’intention de m’amuser davantage avec les effets graphiques. Il est en OpenGL 3.3 uniquement, et ne pourra pas tourner si OpenGL 3.3 n’est pas supporté (d’un point de vue matériel, cela correspond aux cartes graphiques compatibles DirectX 10 au moins, soit une carte milieu de gamme ayant moins de 4 ans). Cela m’évite d’avoir à faire plusieurs routines pour telle étape de rendu en fonction des extensions disponibles.

 

Si un point reste obscur, n’hésitez pas à demander des précisions dans les commentaires. Le prochain article sera plus concret, avec des screenshots de Newt-Puzzle pour illustrer, promis !

 

  1. Une API est un ensemble de fonctions mises à disposition des développeurs. Par bas niveau on entend proche du matériel, par opposition à haut niveau, proche de l’utilisateur.
  2. Il s’agit d’une implémentation non officielle, pour des raisons de licence.
  3. Ce n’est pas spécifique à OpenGL : les étapes sont les mêmes pour Direct3D 11.
  4. Rigoureusement, il faudrait parler de primitives, qui ne sont pas nécessairement des polygones. On peut aussi traiter des lignes ou des points.
  5. On appelle pipeline la suite d’opérations s’appliquant au flux de données qui le traverse
  6. En fait, il était possible de ne pas utiliser les lumières d’OpenGL et d’utiliser des textures (« lightmap ») à la place, pour un meilleur rendu. Je n’ai pas du tout creusé ces techniques, car elles ne sont plus vraiment d’actualité maintenant que l’on peut passer par des shaders.
  7. A nuancer quand même : le nombre d’instructions possibles est limité sur un shader, et cette limitation dépend de la carte graphique. Théoriquement, un shader non compatible ne doit pas compiler. Dans la pratique, avec des drivers en mousse, j’ai pu admirer des résultats assez psychédéliques, voire des plantages violents.
  8. Rigoureusement, la rupture est à OpenGL 3.1. OpenGL 3.0 se contente de marquer comme obsolètes certaines fonctions, mais les conserve quand même.
  9. Dans la pratique, cette extension est toujours là, mais rien n’interdirait à un driver récent de ne pas la fournir.
  10. Pas 1.0 quand même : il y a quelques fonctions dont je peux difficilement me passer.
Réactions : pas encore de commentaire
 

Commentaires

Vous pouvez poster un commentaire ou laisser un trackback à cette adresse.

Réagissez

Vous pouvez utiliser ces balises : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>
 
 
 

© 2012 Eldermaeblog | Joyeusement propulsé par WordPress