Aller au menu - Aller au contenu

Icône Communiquer avec l'application : attributs et « uniforms »

Par Avatar Yno
Mise à jour : 08/11/2008
1 574 visites depuis 7 jours, dont 78 sur ce chapitre classé 82/786
Bienvenue dans la seconde partie de ce tutoriel consacré aux shaders en GLSL ! :)

Nous allons la commencer sans plus tarder en abordant deux façons de transmettre des informations au shader, à partir de l'application. Ces deux façons sont relativement simples à mettre en œuvre, et puisqu'elles font intervenir toutes les deux des exemples en langage C (langage utilisé pour notre application), j'ai décidé de les fusionner dans un seul chapitre.

  • Nous allons tout d'abord étudier les variables de type uniform. Ce type de variable du GLSL vous permettra de recevoir des variables provenant de l'application. Vous pourrez ainsi changer dynamiquement la valeur d'une variable au sein du shader, lorsque votre application sera en cours.
  • Nous apprendrons ensuite à utiliser les attributs de sommet. Ce sont en fait des informations qui sont différentes pour chaque sommet, un peu comme la couleur ou la position ;) Cela nous permettra de rajouter des données à nos sommets.

Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire Chapitre suivant

Les variables de type uniform

Principe



L'idée est simple : envoyer une variable de l'application au shader. En réalité ce n'est pas un véritable envoi, mais plutôt une copie de valeur. Nous allons pour cela créer une variable dans notre shader, en lui assignant un type particulier, puis, à partir de notre application, localiser cette variable dans notre shader pour y demander la copie d'une valeur.

Vous pouvez bien sûr transmettre différents types de variables, comme des entiers, des flottants, des booléens, des vecteurs et même des matrices.


Le type « uniform »



Dans un shader, pour créer des variables capables de recevoir leur valeur à partir de l'application appelante, il faut les rendre globales et leur assigner le préfixe uniform.

Rendre une variable globale ? o_O

Cela veut dire, comme en C, rendre une variable accessible par tout le programme. Créer une variable globale en GLSL revient à faire ceci :

Code : Autre
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int variable; // 'variable' est globale



void main(void)

{

    ...

}



...


Et le type « uniform » dans tout ça ?

Il vient se placer devant le type de la variable, comme ceci :

Code : Autre
1
uniform int variable;

Déclarer une variable avec uniform revient à dire : "je veux que la valeur de cette variable soit indiquée par mon application".
Et voilà, c'est tout ce qu'on aura à faire dans notre shader pour indiquer à OpenGL les variables dont la valeur proviendra de l'application :)


Implémentation côté API



Une affaire d'identifiants



Dites donc, on dirait qu'ils aiment bien les identifiants chez OpenGL :D
Avant de pouvoir transmettre une valeur à une variable de notre shader, il est important de préciser à OpenGL quelle variable recevra la valeur.

Ben, c'est celle qu'on a créée avant avec uniform non ?

Eh bien non, pas forcément, car il est possible de créer plusieurs variables uniform dans un même shader. Si par exemple vous en créez deux, comment OpenGL saura à quelle variable il doit transmettre la valeur ? Il est donc important de récupérer l'ID de notre variable avant de lui envoyer une valeur.

Récupérer un ID



Il n'existe pas 36 façons de localiser une variable dans un shader (et n'importe où d'ailleurs...), il va falloir donner à OpenGL le nom de notre variable, pour qu'il nous retourne son identifiant, nous nous servirons ensuite de celui-ci pour envoyer une valeur à notre variable.

Voici la fonction OpenGL permettant de récupérer l'ID d'une variable dans un shader :

Code : C
1
GLint glGetUniformLocation(GLuint program, const char *nom);

  • program : c'est l'identifiant du program tout entier dans lequel on voudra rechercher la variable.
  • nom : le nom de la variable dont on veut récupérer l'identifiant.


Attention : si vous créez une variable uniform qui a le même nom dans le vertex et le pixel shader, les deux variables seront affectées par la valeur que vous spécifierez.


La valeur retournée par cette fonction est l'identifiant de votre variable de shader nommée "nom", si la fonction n'a pas trouvé votre variable, ou qu'elle a échoué pour une raison x ou y, elle renvoie -1.

Dans quelle mesure cette fonction peut "échouer" ?

Si votre program n'a pas été lié par exemple, rappelez-vous dans le second chapitre de la fonction glLinkProgram().

Assigner une valeur



Nous voici enfin parvenus à l'étape finale : l'envoi d'une valeur à la variable de notre shader :)

Pour cela, nous avons besoin de 3 choses :
  1. l'identifiant de notre variable, récupéré avec glGetUniformLocation() ;
  2. une valeur à attribuer à cette variable ;
  3. et surtout, que notre program ait été défini comme actif pour le rendu, c'est-à-dire activé via glUseProgram() !

La dernière condition est très importante, si votre program n'a pas été activé, une erreur OpenGL de type GL_INVALID_OPERATION sera levée.

Les erreurs OpenGL sont récupérables via la fonction glGetError(), et peuvent être transformées en chaînes de caractères avec gluErrorString() comme ceci :
Code : C
1
const char *err = gluErrorString(glGetError());


Allez, il est temps que je vous présente la fonction permettant d'envoyer une valeur à une variable de notre shader :) :

Code : C
1
void glUniform*(GLint id, TYPE val);

  • id : c'est l'ID de notre variable, récupéré via glGetUniformLocation().
  • val : la valeur que l'on souhaite envoyer à notre variable.


Cette fonction agit directement sur le program actif, d'où la nécessité de l'avoir activé au préalable avec glUseProgram().

Lorsque glUniform*() est appelée, la variable désignée par l'identifiant prend alors la valeur demandée, et garde cette valeur jusqu'à ce que le program soit à nouveau lié ou supprimé. Par conséquent, si vous souhaitez envoyer une valeur constante (un paramètre de démarrage par exemple), n'invoquez glUniform*() qu'une seule fois.


Les différentes formes de glUniform*()



Comme vous l'aurez remarqué, j'ai mis une petite étoile « * » au nom de la fonction, c'est pour dire qu'elle a été définie sous plusieurs formes, comme pour les fonctions glVertex*(), glTexCoord*(), etc...
Cela permet d'envoyer différents types de variables, comme je l'ai dit plus haut ; des vecteurs, des matrices, etc...

Envoi d'une simple variable



La fonction glUniform*() a été définie sous de nombreuses formes. Toutefois, le nombre de types de variable qu'elle supporte est toujours limité à deux :

  • les entiers (int) ;
  • les flottants (float).

Du côté du GLSL, nous remarquons la présence d'un type supplémentaire : le type bool. Rassurez-vous cependant, vous pourrez affecter une variable bool en passant par la forme entière de glUniform*().

Voici un premier exemple de code illustrant le simple envoi d'une variable à notre program nommé prog :

Code : Autre
1
uniform int var; // n'oublions pas de declarer 'var' globale


Code : C
1
2
3
4
5
6
7
8
/* on recupere l'ID */
int id = glGetUniformLocation(prog, "var");

/* on defini notre program actif */
glUseProgram(prog);

/* on envoie notre variable (ici nous envoyons la valeur 2) */
glUniform1i(id, 2);


Notez qu'il n'est pas obligatoire de récupérer l'identifiant à chaque fois que vous voudrez envoyer une valeur à une variable de votre shader, l'ID est invariable, sauf si vous liez à nouveau votre program. Donc, dans la mesure où la recherche d'un identifiant est plutôt lourde (analyse d'une chaîne de caractères), on essayera si possible de stocker au préalable tous les identifiants dans des variables.


Envoi d'un simple vecteur



Si vous voulez envoyer un vecteur à 3 dimensions (par exemple) à votre shader, vous pouvez faire comme ceci :

Code : Autre
1
uniform vec3 vecteur;


Code : C
1
2
3
4
5
6
7
8
/* on recupere l'ID */
int id = glGetUniformLocation(program, "vecteur");

/* on defini notre program actif */
glUseProgram(program);

/* on envoie notre vecteur */
glUniform3f(id, 1.7, 5.2, 3.6);


Cette méthode peut poser un petit problème ; elle est plutôt "lourde" et peut flexible, il faut spécifier chaque composante du vecteur une par une, et si l'on souhaite subitement envoyer un vecteur à deux dimensions à la place, il faudra non seulement changer le nom de la fonction, mais aussi le nombre de ses paramètres. Pour remédier à ce problème, vous pouvez utiliser la version vectorielle de glUniform*().

Étude de la version vectorielle de glUniform*()



Afin d'envoyer un groupe de données (comme un vecteur), la fonction glUniform*v() vous propose de lui spécifier un pointeur vers ces données. De plus, elle bénéficie d'un nouveau paramètre :

Code : C
1
void glUniform*v(GLint id, GLsizei count, TYPE *val);

  • id : ...
  • count : nombre de groupes de données.
  • val : ...

Voyons tout d'abord comment modifier le code précédent pour spécifier la valeur de notre vecteur par le biais d'un tableau :

Code : Autre
1
uniform vec3 vecteur;


Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/* le vecteur que l'on veut envoyer en parametre */
float vec[3] = {1.7, 5.2, 3.6};

/* on recupere l'ID */
int id = glGetUniformLocation(program, "vecteur");

/* on defini notre program actif */
glUseProgram(program);

/* on envoie notre vecteur */
glUniform3fv(id, 1, vec);


Vous pouvez voir ici que j'ai spécifié le paramètre count de glUniform*v() à la valeur 1. Cela veut dire que j'ai souhaité envoyer un vecteur, un vecteur à 3 composantes ( glUniform3fv() ).
En effet, le chiffre contenu dans le nom de la fonction glUniform*() (qui peut varier de 1 à 4 inclus) nous informe du type de la variable GLSL (nombre de composantes du vecteur), et par conséquent du nombre de données que la fonction glUniform*() va aller chercher dans notre pointeur.

Donc : faites bien attention aux débordements mémoire.
Mais attendez, vous n'avez pas encore tout vu. Eh bien oui ; qu'advient-il de notre paramètre count ? Il ne faut pas l'oublier.

Le paramètre count vient complexifier la chose en vous permettant d'envoyer des tableaux de vecteurs. Avec ce paramètre, vous allez pouvoir spécifier le nombre de vecteurs que contient votre tableau.
Prenons tout de suite un exemple :

Code : Autre
1
uniform vec3 vecteurs[2]; // tableau de deux vecteurs


Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/* les vecteurs que l'on veut envoyer en parametre */
float vecs[2][3] =
{
    {0.8, 2.1, 1.3},
    {1.9, 3.2, 1.7}
};

/* on recupere l'ID */
int id = glGetUniformLocation(program, "vecteurs");

/* on defini notre program actif */
glUseProgram(program);

/* on envoie nos deux vecteurs */
glUniform3fv(id, 2, vecs);


Comme vous le voyez, côté GLSL vous pouvez remarquer que j'ai créé un tableau de vecteurs, tout comme je l'ai fait dans l'exemple en C. J'ai également placé le paramètre count à la valeur 2 dans glUniform*v(), pour indiquer que je souhaite envoyer deux vecteurs.

Prenez garde : glUniform*v() attend une suite de données, donc un tableau ou un tableau de tableaux, mais en aucun cas un pointeur de pointeurs ! La solution suivante est envisageable et produira le même effet :
Code : C
1
float vec[2*3] = {0.8, 2.1, 1.3, 1.9, 3.2, 1.7};


Au final, le nombre de variables qui seront lues dans votre tableau sera égal au chiffre du nom de la fonction glUniform*v() multiplié par count (dans notre cas, 2*3).

Et si je veux envoyer un simple tableau, je fais comment ?

Invoquez glUniform1*v(), elle ira prendre exactement count données dans votre tableau et ira les loger dans votre shader dans un tableau de type bool, int ou float. Eh oui, un vecteur à une composante ( glUniform1*v() ) n'est rien d'autre qu'une simple variable ;)

Et les matrices ?



À partir du moment où vous avez compris le fonctionnement de la version vectorielle de glUniform*(), les matrices vous paraîtront tout aussi simple ; en fait, le principe est identique, sauf qu'une matrice a plus de composantes qu'un vecteur ;)

Afin d'envoyer une matrice à votre shader, chose qui risque d'arriver assez rarement dans la mesure où les matrices de projection, de texturage et de visualisation sont déjà à votre disposition en GLSL, la fonction glUniform*() prend une autre... forme :p :

Code : C
1
void glUniformMatrix*fv(GLint id, GLsizei count, GLboolean transpose, const float *val);

  • id : ...
  • count : ...
  • val : les valeurs doivent être contenues dans un pointeur sur des flottants de type float ;
  • transpose : positionné à GL_TRUE, les matrices envoyées seront transposées avant d'arriver dans le shader.

Comme vous le voyez, cette version laisse moins de libertés au programmeur, le forçant à utiliser des matrices codées sur des flottants de type float. Toutefois, elle lui permet :

  • de spécifier l'ordre de sa matrice, via le paramètre transpose ;
  • d'indiquer la taille de sa matrice, via le nom de la fonction ( glUniformMatrix2fv() pour une matrice 2*2, glUniformMatrix3fv() pour une matrice 3*3, etc... ).

Pour plus d'informations sur la transposée d'une matrice, vous pouvez consulter la FAQ de Developpez.com sur les matrices.

Voici un bref exemple pour illustrer une utilisation possible :

Code : Autre
1
uniform mat3 matrix;


Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
/* la matrice que l'on veut envoyer en parametre */
float mat[3][3] =
{
    {1.2, 0.0, 0.0},
    {0.0, 2.5, 0.0},
    {0.0, 0.0, 1.0}
};

/* on recupere l'ID */
int id = glGetUniformLocation(program, "matrix");

/* on defini notre program actif */
glUseProgram(program);

/* on envoie notre matrice */
glUniformMatrix3fv(id, 1, 0, mat);



Et voilà, vous savez tout sur les variables uniform des shaders :) Servez-vous en pour envoyer des informations supplémentaires, de couleur par exemple, ou bien des données envoyées par l'utilisateur ; eh oui, aucun dialogue direct n'est possible entre l'utilisateur et le shader, servez-vous donc de votre application comme un tiers.

Un exemple complet est disponible en téléchargement à la fin de ce chapitre.

Les attributs de sommet

Qu'est-ce qu'un attribut ?



Comme indiqué à l'introduction de ce chapitre, les attributs de sommet sont des données supplémentaires, différentes pour chaque sommet, à l'instar de la position, de la coordonnée de texture, etc...

Ainsi, les attributs de sommet ne sont accessibles que par les vertex shaders, là où les informations de sommets sont disponibles en lecture (et en lecture seulement).

Comment accéder aux attributs de sommet dans le vertex shader ?

Les données de position du sommet par exemple, sont disponible par défaut dans une variable du GLSL : gl_Vertex. Pour les attributs de sommet, il va falloir créer cette variable, mais pas n'importe comment.


Le type « attribute »



À l'instar des variables uniform, que nous avons vue à l'instant, il va à nouveau falloir attribuer un type différents à nos attributs de sommet.
Tout comme les uniform, les attributs doivent être des variables globales, mais préfixées cette fois-ci avec le mot clé attribute :

Code : Autre
1
attribute vec3 donnee_supplementaire;


Ici, j'ai décidé de rajouter une variable de type vec3.
En gros, ce bout de code peut se traduire : « je rajoute une donnée à mes sommets qui seront dessinés avec ce vertex shader ».

Supposons que nous voulions dessiner un triangle, nous allons donc spécifier trois sommets :

Image utilisateur


Chaque sommet possède son lot de données en tout genre : position, normale, couleur, etc...
Eh bien imaginez que vous vouliez en rajouter ; vous pouvez !

Image utilisateur


C'est ce qui se passe lorsque vous créez une variable avec le type attribute :)


Côté API



Bien, voyons maintenant comment spécifier la valeur de ces données en plus que sont les attributs de sommet.

Globalement, le principe est le même que pour les variables uniform :

  • on récupère l'ID (plus couramment appelé index dans le cas des attributs de sommet) de notre attribut ;
  • on s'en sert pour localiser nos attributs et ainsi leur envoyer la valeur de notre choix.


Récupérer l'index



Bon, comme vous avez déjà pris l'habitude avec les uniforms, je vais aller un peu plus vite dans la pratique. Pour récupérer l'index d'un attribut de sommet dans un shader GLSL, invoquez glGetAttribLocation() :

Code : C
1
GLint glGetAttribLocation(GLuint program, const char *name);

  • program : désigne le program dans lequel nous voulons rechercher l'index.
  • name : c'est le nom de l'attribut dont on souhaite obtenir l'index.


Une fois que nous avons notre index, il nous suffira de le spécifier à la fonction qui permet de définir les valeurs des attributs de sommet, et ces valeurs étant différentes pour chaque sommet, il nous faudra appeler cette fonction pour chaque sommet que nous définirons.

Spécifier la valeur d'un attribut



Prenons un exemple ; supposons un triangle, créé de la façon suivante avec OpenGL :

Code : C
1
2
3
4
5
glBegin(GL_TRIANGLES);
    glVertex2f(0.9, -0.9);
    glVertex2f(-0.9, -0.9);
    glVertex2f(0.0, 0.9);
glEnd();


Avec ce code, nous demandons la création de trois sommets (pour former un triangle). Pour chaque sommet traité, le vertex shader actif sera invoqué et sa variable gl_Vertex sera affectée aux valeurs que nous avons spécifiées à la fonction glVertex*(). Il en va de même pour chaque variable d'entrée du vertex shader (couleur, normale, ...), y compris son attribut.
Ainsi, pour affecter un attribut d'un sommet, il suffit de spécifier sa valeur pour chaque création de sommet avec la fonction glVertexAttrib*(), en spécifiant bien quel attribut nous souhaitons affecter via son index :

Code : C
1
2
3
4
5
glBegin(GL_TRIANGLES);
    glVertexAttrib3f(index, 0.0, 0.0, 0.0); glVertex2f(0.9, -0.9);
    glVertexAttrib3f(index, 0.0, 0.0, 0.0); glVertex2f(-0.9, -0.9);
    glVertexAttrib3f(index, 0.0, 0.0, 0.0); glVertex2f(0.0, 0.9);
glEnd();


Et voici son prototype :

Code : C
1
void glVertexAttrib*(GLuint index, TYPE vals);

  • index : il s'agit là de placer l'index que l'on a récupéré avec glGetAttribLocation().
  • vals : ce sont les valeurs auquels on souhaite positionner notre attribut, le nombre de valeurs peut varier de 1 à 4 inclus.


La fonction glVertexAttrib*() permet de spécifier la valeur de l'attribut de chaque sommet, comme vous venez de le voir.
Voici un exemple d'utilisation des attributs de sommet :

Code : Autre
1
attribute float numero_sommet; // representera le numero du sommet


Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
/* on recupere l'index de notre attribut de sommets */
int index = glGetAttribLocation(program, "numero_sommet");

...

/* on active notre program */
glUseProgram(program);

glBegin(GL_TRIANGLES);
    glVertexAttrib1f(index, 0.0); glVertex2f(0.9, -0.9);
    glVertexAttrib1f(index, 1.0); glVertex2f(-0.9, -0.9);
    glVertexAttrib1f(index, 2.0); glVertex2f(0.0, 0.9);
glEnd();

/* on desactive les shaders */
glUseProgram(0);


Vous noterez que la forme de glVertexAttrib*() utilisée doit correspondre au type de l'attribut dans le shader, sinon vous risquez de vous retrouver avec une valeur d'attribut erronée dans votre shader (en gros ça fera comme si vous faisiez un cast GLSL : Attrib = TypeDeAttrib(TypeDeLaFonction) ).

Je vous conseille d'aller faire un petit tour sur la page de manuel de glVertexAttrib*(), vous risquez d'y trouver quelques informations complémentaires intéressantes ; cette fonction vous permet notamment de normaliser le vecteur que vous lui envoyez.

Les tableaux d'attributs

Si vous connaissez les vertex arrays, et que vous êtes un flemmard (quelqu'un de normal quoi), vous vous êtes probablement demandé : mais existe-t-il une alternative pour envoyer mes attributs de sommets par le biais de tableaux ? Eh bien la réponse est oui :)

Cette section est bien sûr facultative, elle ne vous aidera pas à mieux comprendre le fonctionnement des attributs de sommet, elle est plutôt réservée aux connaisseurs des vertex arrays.



Activation des tableaux d'attributs



À l'instar des vertex arrays simples, les tableaux d'attributs ont besoin d'une activation qui leur est propre.
Chaque index d'attribut doit être activé indépendamment, car chaque index représente un type de données différent.

Que quoi ? Hein ??

Oui, chaque index d'attribut représente un type différent ; la différence qu'il y a entre l'index 0 et l'index 1 est la même qu'entre la position d'un sommet et sa normale : ce ne sont pas les mêmes données, elles sont identifiées différemment.
Je vous conseille d'aller jeter un oeil à ma petite définition de ce qu'est un sommet, dans mon tutoriel sur les vertex arrays.

Bien, revenons-en au sujet initial : l'activation. Étant donné que chaque type d'attribut est identifié différemment par OpenGL, il va falloir, comme pour chaque type de donnée d'un sommet, l'activer indépendamment des autres, et pour cela, il va nous falloir son index.
Avec la fonction glEnableClientState(), c'était facile, il nous suffisait de lui donner une constante au nom trivial et facile à retenir, et hop, elle activait le type de vertex array demandé.

La fonction glEnableVertexAttribArray() quant à elle, demande l'index de l'attribut à activer.

Code : C
1
void glEnableVertexAttribArray(GLuint index);

  • index : c'est l'index du type de l'attribut que l'on souhaite activer.

Avec les shaders GLSL, l'index d'un attribut de sommet se récupère comme nous l'avons vu plus haut : avec la fonction glGetAttribLocation().
Après vous être muni de cet index, vous n'aurez qu'à le donner à glEnableVertexAttribArray() pour que nous puissions exploiter les attributs de sommet demandés :)


Envoi d'un tableau



Allez, je ne vais pas passer par 36 chemins ; ici je considère que vous maîtrisez un minimum le concept des vertex arrays, je ne m'étalerai donc pas sur les détails.

Afin de spécifier un tableau d'attributs, utilisez la fonction glVertexAttribPointer() :

Code : C
1
void glVertexAttribPointer(GLuint index, int size, GLenum type, GLboolean norm, GLsizei stride, const void *data);

  • size, type, stride, data : référez-vous à mon tutoriel sur les vertex arrays, ces paramètres ont la même incidence que ceux des autres fonctions gl*Pointer().
  • index : il s'agit d'indiquer ici le type de l'attribut, c'est-à-dire son index récupéré à l'aide de glGetAttribLocation().
  • norm : positionné à GL_TRUE, les vecteurs contenus dans votre tableau de données seront normalisés. Je vous conseille personnellement de laisser ce paramètre à 0 (ou GL_FALSE), ainsi OpenGL ne touchera pas à vos données.

Comme vous le voyez, cette fonction demande à ce qu'on lui fournisse le type d'attribut que représentera le tableau qu'on lui envoie.

Comment ça se fait ? Pourquoi elle a besoin de savoir ça, alors qu'on a déjà donné l'index lorsqu'on a activé les tableaux ?

Car vous pouvez avoir activé plusieurs tableaux d'attributs à la fois (c'est possible sachez-le), il est donc important de spécifier explicitement quel est le type d'attribut que vous lui envoyez.

Le reste du fonctionnement est le même que pour les vertex arrays (les tabeaux d'attributs sont en fait des vertex arrays un peu différents sur certains points) : après avoir appelé cette fonction, le tableau sera envoyé à la carte graphique pour traitement lorsque vous appelerez une fonction de dessin comme glDrawArrays() ou glDrawElements().

Et les VBOs ?



Ah ! J'allais les oublier ceux-là.
Les tableaux t'attributs ayant un système de fonctionnement identique aux vertex arrays, vous pouvez également héberger vos tableaux dans un VBO, puis utiliser l'adresse 0 avec glVertexAttribPointer() pour spécifier l'emplacement de votre VBO actif, et utiliser les données directement à partir de la mémoire de la carte graphique.

« VBO » ? Avons-nous été présentés ?

Vous ne connaissez pas les VBOs ? Eh bien alors il est grand temps que vous fassiez leur connaissance !
Les VBOs sont un sujet assez vaste, c'est pourquoi je vous ai concocté un petit tutoriel à leur sujet. Pour faire court ; les VBOs sont une sorte d'amélioration du système des vertex arrays, ils servent à augmenter considérablement leur performance (pour peu qu'ils soient bien utilisés) ;)


Désactivation des tableaux d'attributs



Je vous l'avais dit que les tableaux d'attributs étaient des vertex arrays :p Un peu différents toutefois, la fonction pour désactiver les tableaux d'attributs est la suivante :

Code : C
1
void glDisableVertexAttribArray(GLuint index);

  • index : encore une fois, il est nécessaire d'indiquer quel type d'attribut on souhaite désactiver.



Exemple d'utilisation



Un petit exemple ne fera pas de mal je pense. Vous vous rappelez de l'exemple qui présentait une utilisation possible des attributs de sommet, en spécifiant les attributs un à un ? Je vous en propose ici l'adaptation qui utilise les tableaux d'attributs :


Code : Autre
1
attribute float numero_sommet;


Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/* notre index d'attribut */
int index;

/* tableau des positions */
float pos[6] =
{
    0.9, -0.9,
    -0.9, -0.9,
    0.0, 0.9
};
/* tableau t'attributs */
float attribs[3] =
{
    0.0, 1.0, 2.0
};

...

/* on recupere l'index de notre attribut de sommets */
index = glGetAttribLocation(program, "numero_sommet");

...

/* on active les tableaux */
glEnableClientState(GL_VERTEX_ARRAY);
glEnableVertexAttribArray(index);

/* on specifie nos donnees */
glVertexPointer(2, GL_FLOAT, 0, pos);
glVertexAttribPointer(index, 1, GL_FLOAT, 0, 0, attribs);

glUseProgram(program);

/* dessin */
glDrawArrays(GL_TRIANGLES, 0, 3);

glUseProgram(0);

/* on desactive les tableaux */
glDisableVertexAttribArray(index);
glDisableClientState(GL_VERTEX_ARRAY);

Ce chapitre touche à sa fin ; nous y avons vu deux méthodes pour transmettre des informations à nos shaders : l'une où l'on envoyait une "simple" variable, allant de l'entier à la matrice 4×4, et l'autre où l'on pouvait envoyer des données différentes à chacun de nos sommets.


Téléchargement



Comme promis, voici un programme exemple, afin que vous voyez comment on peut mélanger efficacement tout ce qu'on vient de voir.



Comme d'habitude, l'exemple ne présente rien d'extraordinaire, toutefois ne vous inquiétez pas ; nous verrons dans la prochaine partie de ce tutoriel comment réaliser différents effets graphiques grâce aux shaders, afin d'embellir nos scènes 3D :)
Chapitre précédent Sommaire Chapitre suivant

Partager

11 commentaires pour "Communiquer avec l'application : attributs et « uniforms »"
Note moyenne : 3.93 / 4 (14 votes)
Pseudo Commentaire
Hors ligne Yno # Posté le 06/01/2008 à 00:08:33
Avatar
Flux RSS

Si tu es si impatient, tu peux commencer à apprendre tout seul, maintenant que tu as les bases ça sera un jeu d'enfant (le tuto sur les vecteurs te sera très utile, ils sont tout le temps utilisés dans les shaders).
Voici quelques liens où tu trouveras des exemples en GLSL, histoire de t'habituer au style de coding (ça m'a fait bizarre au début personnellement ; j'y comprenais rien :-° ) :

http://ozone3d.net/tutorials/glsl_lighting_phong.php
http://ozone3d.net/tutorials/glsl_texturing.php
http://ozone3d.net/tutorials/image_filtering.php
http://ozone3d.net/tutorials/glsl_fog/
 
Hors ligne culte # Posté le 06/01/2008 à 14:58:02
Avatar

Études : ENIB (Ecole Nationale d'Ingénieurs de Brest) - Bretagne

Cool merci :) .
Je l'avais déjà repéré ce site, mais je pensais que ça allait être trop compliqué pour moi, je vais voir ce que j'arrive à faire...

Et Sinon tu vas reprendre la rédaction bientôt ou pas ? :)


merci encore.
++

Je préfère aider (quand je peux :-° ), que me faire aider :D .
C++
 
Hors ligne Yno # Posté le 06/01/2008 à 20:39:32
Avatar
Flux RSS

Citation : culte
Et Sinon tu vas reprendre la rédaction bientôt ou pas ?

Bof, on verra après le BAC (si je l'ai) :-°
 
Hors ligne Ptinic # Posté le 10/04/2008 à 12:43:41

Salut!

Merci pour ce tuto, après m'avoir apprit les VBOs, me voilà formé sur les Shaders grâce à toi!

Une petite remarque: Dans l'exemple complet du chapitre sur les notions supplémentaires, le main contient:
gl_Position = gl_Vertex;
à la place de
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;

par contre c'est un bon moyen de vérifier qu'on suit bien :D

Bref encore merci et bon courage pour la rédaction de la suite que j'attend également avec impatience.
Hors ligne sim254 # Posté le 10/11/2011 à 20:46:50

Merci pour ce super tuto ! :)

Par contre tu pourrais réuploader les fichiers d'exemple à la fin, les liens sont morts :(

Merci d'avance

Voir tous les commentaires