Introduction au GLSL
Date de publication : 11/10/2007 , Date de mise à jour : 11/10/2007
Par
Antony Martin (Autres articles)
Cette série d'articles est destinée à vous faire connaître le langage de programmation de shaders pour OpenGL nommé le GLSL. Vous y découvrirez également quelques techniques de rendu qui utilisent les shaders afin de simplifier leur programmation.
Introduction
1. Qu'est-ce qu'un shader ?
1.1. La programmation GPU
1.1.1. En assembleur ?
1.1.2. Plusieurs types de shaders
2. Que permettent-ils de faire ?
2.1. Qu'est-ce que le traitement d'un vertex et d'un pixel ?
2.2. Un exemple concret ?
3. Et techniquement parlant ?
3.1. Quel IDE pour la programmation de shaders ?
Conclusion
Liens
Remerciements
Introduction
Bienvenue dans la première partie de cette suite de tutoriels consacrés aux shaders en GLSL. Je vais tenter dans ce premier article de vous présenter les généralités sur les shaders, en vous présentant leur utilité ainsi que leurs avantages par rapport à un rendu simple. Bonne lecture !
1. Qu'est-ce qu'un shader ?
À l'origine pensés par
Pixar pour ses animations vidéos, les shaders ont fait leur apparition dans le domaine du jeu vidéo assez récemment et sont aujourd'hui indispensables pour qui veut réaliser des effets graphiques un tant soit peu évolués.
Le GLSL est un langage de programmation de shaders conçu par OpenGL, pour OpenGL. Sa syntaxe est basée sur celle du langage C, langage que j'utiliserai d'ailleurs pour mes exemples de code lorsque nous devrons faire intervenir l'API OpenGL dans la gestion des shaders.
1.1. La programmation GPU
Le GPU est le processeur de votre carte graphique, c'est lui qui calcule vos rendus 3D. La programmation GPU revient donc à programmer nos calculs de rendus 3D.
Les shaders servent à programmer, et donc à contrôler, le
pipeline de rendu par défaut de votre carte graphique, appelé le
FFP.
Les shaders sont des programmes, ils possèdent donc un code source, une compilation et une exécution.
Ils se différencient toutefois des programmes écrits en C, C++ ou autre langage réservé à une exécution CPU, de par leur compilation et leur exécution.
La compilation d'un shader est effectuée lors du lancement de votre application, et l'exécution se passe au niveau du GPU, contrairement à vos applications habituelles qui elles sont traitées par le CPU, et c'est ce qui fait la puissance et la flexibilité des shaders.
1.1.1. En assembleur ?
Quand on parle de la programmation d'un processeur, on pense généralement à l'assembleur. Il existe effectivement des langages assembleur pour programmer un GPU autant chez DirectX que chez OpenGL, mais nous ne les traiterons pas dans ce tutoriel.
Le GLSL a été développé par l'
ARB pour l'API graphique OpenGL afin d'offrir une plus grande souplesse dans la programmation de shaders pour OpenGL. D'autres langages haut niveau ont également été développés, autant chez DirectX que chez nVidia.
Le HLSL, pour High Level Shading Language, est le langage haut niveau développé par Microsoft pour sa bibliothèque graphique. Ce langage est connu sous divers noms de versions ; Shader model 1.0, Shader model 2.0, ... la dernière version à ce jour est la 4.0, supportée uniquement par DirectX 10.
De son côté, nVidia a voulu en faire autant et a ainsi donné naissance à son propre langage haut niveau ; le Cg. Le Cg, pour C for Graphics, possède un énorme avantage ; il peut être utilisé indépendamment de l'API choisie pour le rendu, c'est ainsi une puissante forme de normalisation, bien qu'il semblerait, sans grand étonnement, que certaines machines dotées d'une carte ATI soient moins performantes avec le Cg.
Pour plus d'informations sur le fonctionnement ainsi que sur l'implémentation de ce langage, je vous invite à lire
l'article de Laurent Gomila traitant des shaders dans son tutoriel sur la réalisation d'un moteur 3D.
Voici un petit exemple de ce à quoi ressemble le langage GLSL :
void main(void)
{
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = gl_Vertex * gl_ModelViewMatrix;
}
|
Comme vous pouvez le constater, la syntaxe est très proche de celle du C, pour ne pas dire identique.
1.1.2. Plusieurs types de shaders
Il est important de distinguer deux types de shaders différents, remplissant chacun des fonctions bien définies et aucunement identiques :
- les vertex shaders : ce sont des shaders qui interviennent lors du traitement de chaque sommet. Dans les vertex shaders vous pourrez modifier le calcul des différents attributs de vos sommets ;
- les pixel shaders : aussi appelés fragment shaders, ils permettent de traiter le rendu de chaque pixel qui s'affichera à l'écran. Plus précisément, ils traitent le pixel avant qu'il ne soit dessiné à l'écran, lorsqu'il n'est encore qu'un fragment.
Les shaders vous permettent donc de programmer le traitement de chaque sommet ainsi que de chaque pixel que vous dessinerez. Référez-vous à la
présentation de l'organisation du pipeline graphique afin de vous faire une meilleure idée de la place qu'occupent respectivement les vertex et les pixel shaders dans le déroulement d'un rendu.
| Une nouvelle génération de shaders vient de faire son apparition avec le G80 (GeForce8) de nVidia, les geometry shaders. Cette technologie étant assez nouvelle et peu répandue, je n'en parlerai pas.
|
2. Que permettent-ils de faire ?
Les shaders permettent donc de programmer la fonction de traitement d'un vertex ainsi que d'un pixel. Mais à quoi cela rime-t-il ? Quel utilité cela peut-il bien avoir ?
2.1. Qu'est-ce que le traitement d'un vertex et d'un pixel ?
Voilà une bien bonne question.
Quand vous envoyez des données de sommet à OpenGL via les commandes glVertex*(), glColor*(), glTexCoord*(), glNormal*(), ... (ou par des tableaux via gl*Pointer() ) OpenGL les stocke et les associe à un ou plusieurs triangles bien définis.
Le traitement de ce triangle se décompose par le traitement de ses 3 sommets, puis par une interpolation des données de ses sommets pour obtenir un triangle plein. Ce sont ces deux dernières étapes qui sont traitées respectivement par le vertex et le pixel shader.
Le traitement d'un vertex consiste à lui appliquer des transformations matricielles pour obtenir les données des sommets définitives qui seront ensuite interpolées. Le pixel shader reçoit des données interpolées de couleur et autres attributs traités dans le vertex shader, il définit ensuite grâce aux informations reçues quelle sera la couleur finale du pixel.
Quel est l'intérêt des shaders ? Je dirais qu'il y a en fait deux grands avantages à utiliser les shaders ;
Le premier est qu'ils permettent une énorme flexibilité de rendu. En gérant le rendu de chaque pixel vous avez un contrôle quasi absolu sur vos rendus 3D et vous pouvez ainsi réaliser beaucoup d'effets impressionnants sans trop vous compliquer la vie.
Le second intérêt réside dans leur principe même. Étant exécutés par la carte graphique, les shaders n'utilisent pas le CPU, ce dernier peut alors souffler un peu et se concentrer sur la gestion du reste de votre application. Dans cette optique de décharger le processeur, les shaders sont particulièrement adaptés si l'on utilise un langage assez "gourmand" en CPU, comme le C# ou le Java, qui sont moins rapides que le C par exemple.
Enfin, outre les avantages techniques que présente l'utilisation des shaders, il faut savoir que les APIs tentent de plus en plus à "forcer" leur utilisation au profit de la suppression des fonctions de manipulation du pipeline fixe ; l'utilisation des shaders tend à devenir une obligation en programmation 3D.
2.2. Un exemple concret ?
Il est temps de vous montrer à quoi ressemble un rendu utilisant les shaders, bien que vous en ayez déjà probablement vu.
Voici deux captures d'écran de la même scène, l'une utilisant le FFP standard, l'autre, des shaders.
Ici un rendu en mode normal, un simple texturage et une lumière OpenGL.
Cette image présente la même scène, mais agrémentée d'un vertex et d'un pixel shader de
bump mapping ; notez l'effet de relief des pierres, donnant au rendu un aspect beaucoup plus réaliste et saisissant. Ici, l'éclairage est intégralement contrôlé par les shaders.
(source du modèle 3D et des textures : http://mdeverdelhan.developpez.com/tutoriel/dynamiclight/tutoriel6/)
3. Et techniquement parlant ?
Malgré quelques explications en début de chapitre, il est important que vous sachiez avec un peu plus de précision comment fonctionne l'implémentation théorique des shaders au sein d'une application OpenGL.
Il est impératif de distinguer deux choses :
- la programmation d'un shader en GLSL ;
- son utilisation au sein d'une application OpenGL.
Ces deux choses sont totalement différentes, il est primordial de bien faire leur distinction.
Nous avons d'une part la programmation du shader lui-même, c'est-à-dire le codage de la fonction qu'il remplira, l'effet graphique qu'il produira, et d'autre part la définition de son utilisation, quand et comment l'utiliser dans notre application, tout cela via des appels à l'API graphique.
Nous pouvons comparer cela à l'utilisation des textures, nous avons d'une part la création de la texture, soit la création de l'image via un logiciel de dessin 2D, et d'autre part le chargement et l'utilisation de cette texture dans notre programme.
L'idée est la même pour les shaders, nous allons tout d'abord programmer un shader, nous placerons son code source dans un fichier, puis nous programmerons notre application OpenGL et nous chargerons via celle-ci le code source de notre shader afin de l'utiliser à notre convenance au sein de notre application.
3.1. Quel IDE pour la programmation de shaders ?
Il n'existe pas beaucoup d'éditeurs de texte qui colorent le langage GLSL.
On peut toutefois citer
Kate, qui bénéficie du support de la coloration syntaxique du GLSL, il active automatiquement celle-ci pour tout fichier ayant pour extension .vert pour les vertex shaders, ou .frag pour les fragment shaders (ou pixel shaders).
Il existe également
Conclusion
Pour les adeptes des spécifications, voici
celles du langage GLSL.
Après cette courte introduction, je vous propose dans le prochain article de nous intéresser à l'implémentation des shaders avec OpenGL.
Liens
Je voudrais signaler l'existence de ces quelques ressources, qui m'ont aidées à bâtir cet article.
Tout d'abord un
excellent article sur
Icare3D, informant des nouveautés d'OpenGL 3.0, et enfin le livre
OpenGL 2.0 Guide Officiel, grâce auquel j'ai appris le langage GLSL.
Remerciements
Je tiens à remercier khayyam90 pour son soutien tout au long de la rédaction de cet article, ainsi que koala01 pour ses corrections orthographiques.
Cette création est mise à disposition sous un contrat Creative Commons.