"program" ? C'est quoi ce nouveau mot ?
C'est comme cela que l'on appelle les
program de shader en OpenGL. Un peu flou ?

Nous venons de voir comment créer un shader, mais ce shader n'est hélas pas exécutable, la seule chose qui soit exécutable est un
program.
Un program recueille un ou deux shaders, et il est exécutable par la carte graphique.
Créer un objet de program
Et oui, nous revoilà à nouveau avec des objets OpenGL, eux aussi différents de tous ceux que vous aurez pu manipuler jusqu'à maintenant.
Rassurez-vous toutefois, leur fonctionnement est très simple

La base des objets OpenGL est d'ailleurs reprise, un objet de program est un GLuint (entier non signé) :
Code : C
La création d'un program est simple, il suffit d'appeler la fonction
glCreateProgram() qui ne prend aucun paramètre, et retourne simplement l'identifiant du program créé :
Code : C1 | program = glCreateProgram();
|
Jusque là, pas de difficulté majeure
Idem pour la suppression d'un program, cela se fait en toute simplicité avec la fonction
glDeleteProgram(). Un exemple ? :
Code : C1 | glDeleteProgram(program);
|
Pour l'instant, notre program ne fait rien, il va donc falloir lui donner des shaders à exécuter.
Association d'un ou plusieurs shaders à un program
Oui, vous avez bien lu.
Comme je l'ai déjà dit, un program est un recueil de un ou deux shaders. On peut y mettre soit un vertex shader, soit un pixel shader, soit les deux.
Pour clarifier vos idées dès à présent, je vous ai préparé un petit schéma

Voici la procédure à suivre pour créer un program exécutable :
- créer un vertex shader, nous allons l'appeler VS;
- créer un pixel shader, nous allons l'appeler PS;
- créer un program, nous l'appellerons Program;
- incorporer (je fais de la cuisine là
) le vertex et le pixel shader dans le program;
- rendre le program exécutable en le liant (nous verrons plus bas en quoi cela consiste);
- le program étant lié, nous pouvons supprimer nos shaders VS et PS si nous en avons plus besoin;
- utiliser le program à notre guise

- détruire les shaders (si ce n'est pas déjà fait) et le program.
Et voici le schéma correspondant

:
Ici, trois notions qui vous sont encore inconnues ont été introduites, il s'agit de "attach", "construction" et "utilisation".
Nous allons sans plus tarder nous intéresser à "attach".
Bon, nous voulons donc associer un ou plusieurs shaders à un program. Il existe pour cela une fonction très simple,
glAttachShader() :
Code : C1 | void glAttachShader(GLuint program, GLuint shader);
|
- program : il s'agit là du program qui recevra le shader.
- shader : c'est le shader à associer à program.
Son utilisation est tellement simple qu'on en pleurerait
Je vous épargne l'exemple de code pour cette fonction, je pense que ça serait inutile.
A l'inverse de
glAttachShader(), il existe une fonction qui a l'effet contraire.
Supposez que vous vouliez mettre à jour un shader, il faudra d'abord que vous le détachiez du program auquel il a été attaché via
glAttachShader(), utilisez pour cela
glDetachShader() :
Code : C1 | void glDetachShader(GLuint program, GLuint shader);
|
Je pense qu'elle se passe de commentaire
Bien, nous savons à présent comment créer un program exécutable, comment lui assigner des shaders, mais nous ne savons toujours pas comment l'utiliser. Toutefois avant de pouvoir utiliser un program, il est important de le rendre opérationnel en le liant.
Liage d'un program de shader
Le liage (ou linking) d'un program peut se comparer à la compilation des shaders car il se décompose également en deux étapes :
- le liage en lui même;
- la vérification du succès de ce liage.
Oui mais qu'est-ce que le liage d'un program ?
Et bien en fait lorsqu'on lie un program de shader on demande à OpenGL de
lier le vertex shader avec le pixel shader. Cette liaison peut échouer dans la mesure où les deux shaders peuvent être incompatibles entre eux.
Nous verrons dans une autre partie de ce tutoriel en quoi consiste réellement le linking et ce qu'il apporte.
Bien, passons à présent au vif du sujet : nous voulons lier un program de shaders.
Pour ce faire, il existe une simple fonction appelée
glLinkProgram() :
Code : C1 | void glLinkProgram(GLuint program);
|
- program : c'est le program que l'on souhaite lier.
Comme je l'ai dit plus haut, l'étape suivante consiste à vérifier que le liage a bien fonctionné. La méthode employée est très similaire à celle des shaders :
- on vérifie si une erreur est présente;
- s'il y en a une, on récupère la taille de la chaîne contenant le message d'erreur;
- on alloue un espace mémoire de cette taille;
- on indique l'adresse de cette mémoire à OpenGL pour qu'il puisse y écrire le message d'erreur.
Voici la fonction permettant (entre autres) de savoir si une erreur a été levée :
Code : C1 | void glGetProgramiv(GLuint program, GLenum type, GLint *result);
|
Son fonctionnement est identique à glGetShaderiv pour les shaders, je vais donc passer les descriptions minutieuses. Tout ce que vous devez savoir ici, c'est la constante à passer à
type pour obtenir ce que l'on cherche. Nous cherchons l'état du précédent liage du program, pour cela nous allons passer la constante GL_LINK_STATUS. La vérification de la valeur de
result fonctionne de la même façon que pour les shaders, si elle est différente de GL_TRUE, alors une erreur a été levée.
Etape suivante, nous voulons récupérer la longueur du message d'erreur, nous allons utiliser pour cela glGetProgramiv avec comme paramètre
type la constante GL_INFO_LOG_LENGTH (comme pour les shaders).
Enfin, pour récupérer le message d'erreur, nous avons à notre disposition la fonction glGetProgramInfoLog :
Code : C1 | void glGetProgramInfoLog(GLuint program, GLsizei max_size, GLsizei *longueur, char *log);
|
Son fonctionnement est lui aussi identique à la fonction glGetShaderInfoLog pour les shaders.
Un exemple de code complet sera disponible à la fin du chapitre.
Utiliser un program de shader
Ah ! Nous voici arrivés à la partie la plus intéressante
Résumons une fois de plus si vous le voulez bien :
- nous savons créer un shader;
- nous savons créer un program exécutable;
- nous savons attribuer à ce program des shaders à exécuter;
- nous ne savons pas comment rendre ce program actif

Utiliser un program, c'est le rendre actif pour tous les prochains rendus jusqu'à ce que l'on demande explicitement l'arrêt de l'utilisation d'un quelconque program de shader, ou jusqu'à ce que l'on active un autre program de shader.
Comme toujours, je vais reprendre mon exemple sur la similitude avec les textures. Vous savez certainement comment on rend une texture active ?
Code : C1 | glBindTexture(GL_TEXTURE_2D, tex_id);
|
Ce code rend la texture
tex_id active pour tous les prochains rendus jusqu'à ce qu'un appel de glBindTexture avec un identifiant de 0 soit effectué :
Code : C1
2 | /* desactive la precedente texture active */
glBindTexture(GL_TEXTURE_2D, 0);
|
Vous noterez que pour les textures, l'emploi de glDisable(GL_TEXTURE_2D) est préférable, mais pour utiliser un program de shader, aucun état OpenGL n'est à activer.
Bien, il est temps de vous présenter la fonction qui permet de rendre un program actif pour le rendu

:
Code : C1 | void glUseProgram(GLuint program);
|
- program : il s'agit là de l'identifiant du program que l'on souhaite activer, si cet identifiant est de 0, OpenGL désactivera l'utilisation des programs de shaders.
Allez, un exemple pour le fun

:
Code : C1
2
3
4
5
6 | glUseProgram(program);
/* super rendus avec des effets de la mort qui tue */
/* desactive l'utilisation des shaders, OpenGL retourne en mode normal (passe par le FFP) */
glUseProgram(0);
|
Tout comme les textures, vous pouvez charger plusieurs shaders en début de programme et ensuite les utiliser simultanément, comme ceci :
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | glUsePogram(prog1);
/* rendus utilisant le program prog1 */
glUsePogram(prog2);
/* rendus utilisant le program prog2 */
glUsePogram(prog3);
/* rendus utilisant le program prog3 */
glUseProgram(0);
/* fin des rendus utilisant les shaders */
|
Attention toutefois, comme avec les textures, il est impossible d'utiliser deux programs à la fois. Ici, le second appel à glUseProgram défini prog2 comme étant actif
à la place de prog1.