Le langage GLSL a beaucoup de points communs avec le langage C. Syntaxiquement, les deux langages sont quasiment identiques.
Les variables
Tout comme en C, il est possible de créer des variables en GLSL. Il existe une multitude de types de variables, chacun ayant une utilité bien précise.
Par exemple, si vous souhaitez créer une variable capable de stocker des nombres flottants, faites ceci :
Code : Autre
Et oui, comme en C

Notez également la présence d'un point-virgule, qui se place aux mêmes endroits qu'en C.
La création de deux variables à la fois est également autorisée, avec l'ajout d'une virgule entre les deux noms de variable :
Code : Autre
Les variables préfixées gl_ sont réservées au langage lui-même, il est donc interdit de créer une variable dont le nom commence par gl_ !
Il est possible de créer des variables des types suivants :
- int : entier ;
- float : flottant ;
- bool : booléen, peut valoir true ou false ;
- vec2 : vecteur à 2 composantes flottantes ;
- vec3 : vecteur à 3 composantes flottantes ;
- vec4 : vecteur à 4 composantes flottantes ;
- mat2 : matrice 2 * 2 de flottants ;
- mat3 : matrice 3 * 3 de flottants ;
- mat4 : matrice 4 * 4 de flottants ;
- ivec2 : vecteur à 2 composantes entières ;
- ivec3 : vecteur à 3 composantes entières ;
- ivec4 : vecteur à 4 composantes entières ;
- bvec2 : vecteur à 2 composantes booléennes ;
- bvec3 : vecteur à 3 composantes booléennes ;
- bvec4 : vecteur à 4 composantes booléennes.
Soudainement, les ressemblances avec le C s'arrêtent

Effectivement, le GLSL possède beaucoup de types de variables... qui ne sont finalement plus des variables mais des ensembles de variables. Nous pouvons toutefois les comparer à des structures, et nous allons voir pourquoi.
Lorsque vous créez un vecteur à 3 composantes par exemple (
vec3), GLSL vous permet d'accéder à une seule de ses composante de cette façon :
Code : Autre1
2
| vec3 direction;
direction.x = 0.2; |
La seconde instruction place la valeur 0.2 dans la composante X du vecteur
direction grâce à l'opérateur d'affectation =, qui fonctionne de la même façon qu'en C... ou presque.
Les opérateurs
Il est bien sûr possible de multiplier une variable par une autre, ou bien encore de soustraire une valeur à une variable. Les opérateurs en GLSL s'utilisent comme en C, voici un exemple :
Code : Autre1
2
| float var1 = 0.2, var2 = 3.0;
float resultat = (var1 - 0.1) * var2; |
Ici,
resultat vaudra (0.2 - 0.1) * 3.0 soit 0.3.
Et si je vous apprenais qu'on peut multiplier un vecteur par un vecteur, vous me répondriez quoi ?
La surcharge des opérateurs
Premier point commun avec le langage C++ : les opérateurs en GLSL sont surchargés.
Euh, et ça veut dire quoi ?
Je ne vais pas vous faire une description avancée de ce qu'est la surcharge des opérateurs, mais pour vous expliquer en deux mots, ça veut dire qu'on peut additionner, soustraire, multiplier et diviser tous les types de variable par tous les types de variable !
J'y comprend rien

Vous connaissez probablement la multiplication matricielle ? Et bien effectuer ce genre de multiplication en GLSL est un jeu d'enfant :
Code : Autre1
2
| mat4 a = ..., b = ...;
mat4 resultat = a * b; |
resultat vaut maintenant le résultat de la multiplication matricielle de
a par
b.
Et ça marche aussi pour les vecteurs !

:
Code : Autre1
2
3
4
| vec4 position = ...;
mat4 m = ...;
vec4 resultat = m * position; |
resultat vaut à présent la position que représente le vecteur
position transformé par la matrice
m.
Notez qu'ici l'ordre de la multiplication est important ! C'est d'abord la matrice, puis le vecteur. Si cela vous semble flou, n'hésitez pas à aller lire
le tutoriel de Kayl sur les matrices.
Allez, encore un exemple :
Code : Autre1
2
| vec3 vecteur = ...;
vecteur *= 2.0; |
La seconde ligne de ce code a pour effet de multiplier chaque composante du vecteur
vecteur par 2.
Les limites de la surcharge
Hé oui malheureusement cette surcharge a des limites, on ne peut pas réellement
tout faire comme je l'ai dit avant, il existe des exceptions. Ces exceptions sont toutefois logiques et n'ont rien de mystérieux comme nous allons le voir.
Par exemple, il est impossible de multiplier une matrice 3*3 par une matrice 4*4, si vous vous y risquez, OpenGL lèvera une erreur lors de la compilation de votre shader.
Autre cas typique : les vecteurs, il est impossible d'effectuer une quelconque opération entre deux vecteurs de type différents.
Enfin, il est également impossible de multiplier un vecteur à 2 composantes (
vec2) par une matrice autre qu'une 2*2 (
mat2), et il en va de même pour tous les autres types,
vec3 avec
mat3 et
vec4 avec
mat4.
Maintenant que je vous ai exposé pleins d'inconvénients dûs à la surcharge, je vais vous proposer
des une solution
Le cast
Tout comme en C, il est possible de forcer la conversion d'un type vers un autre. Ici, la syntaxe est différente que celle du C où l'on fait comme ceci :
Code : C1 | float flottant = (float)entier;
|
En GLSL, le cast d'une variable se fait ainsi :
Code : Autre1
| float flottant = float(entier); |
La conversion explicite de int vers float ou de float vers int n'est pas obligatoire, en revanche, si vous souhaitez convertir un
vec3 en
vec2, là il va falloir le demander explicitement.
Comment on peut convertir un vecteur à trois dimensions en un vecteur à deux dimensions ?
Je ne vais rien vous cacher, et une conversion de ce type amène forcément à une perte de donnée(s), nous allons uniquement conserver dans le vecteur à 2 dimensions deux composantes du vecteur à 3 dimensions. Mais avec le cast, c'est vous qui allez choisir quelles données vous souhaiterez supprimer et quelles données vous souhaiterez garder. (par donnée je sous-entend
composante d'un vecteur)
Étant donné qu'il existe un trop grand nombre de conversions possible (chaque type peut être converti en chaque type), je ne vais pas vous faire une démonstration pour chacune d'entre elles, je vais juste vous fournir la technique à utiliser, elle est logique et fonctionne de la même façon (ou presque) pour tous les types de conversion.
Nous allons prendre l'exemple de la conversion d'un
vec3 vers un
vec2, puis nous prendrons ensuite l'exemple inverse, à savoir
vec2 ->
vec3.
Pour convertir un vec3 en vec2, une méthode simple existe :
Code : Autre1
2
3
| vec3 v = ...;
vec2 v2 = vec2(v); |
Ce code est simple et pourrait se traduire de la façon suivante :
Code : Autre1
2
3
4
5
| vec3 v = ...;
vec2 v2;
v2.x = v.x;
v2.y = v.y; |
En réalité, le cast du premier code prend les
n premières composantes de la variable à caster et les places dans la variable finale, où
n représente le nombre maximal de variables stockables dans le type de la variable finale, dans notre exemple,
n vaut 2.
Vous pouvez également prendre d'autres composantes de
v pour les placer dans
v2 sans avoir à spécifier manuellement les composantes de
v qui recevrons la valeur. L'instruction suivante place les composantes de
v demandées et les places respectivement dans
v2.x et
v2.y :
Code : Autre1
| vec2 v2 = vec2(v.z, v.x); |
Voyons à présent comment convertir un
vec2 en
vec3, nous verrons cette voici qu'à l'inverse d'une conversion
vec3 ->
vec2, il y a un manque de données.
À priori, on pourrait se dire qu'une conversion comme ceci est correct :
Code : Autre1
2
| vec2 v2 = ...;
vec3 v3 = vec3(v2); |
Mais...
Code : Console | impossible de compiler le shader 'test.vert' :
(5) : error C1033: cast not allowed
(5) : error C1056: invalid initialization |
En effet, c'est une instruction non valide, refusée par OpenGL à la compilation du shader.
Afin de pouvoir effectuer un cast sans encombre, il va falloir donner à notre shader ce qu'il attend : la composante manquante. (en l'occurrence il s'agit de la composante z)
Il nous faut donc la spécifier explicitement, comme dans l'exemple qui suit :
Code : Autre1
2
| vec2 v2 = ...;
vec3 v3 = vec3(v2, 0.0); |
Ce code est équivalent à :
Code : Autre1
2
3
4
5
6
| vec2 v2 = ...;
vec3 v3;
v3.x = v2.x;
v3.y = v2.y;
v3.z = 0.0; |
Si vous le souhaitez, vous pouvez aussi faire ceci :
Code : Autre1
2
| vec2 v2 = ...;
vec3 v3 = vec3(0.0, v2); |
Mais ce code n'aura pas le même effet que le précédent, voici ce que l'on obtient en l'appelant :
Code : Autre1
2
3
4
5
6
| vec2 v2 = ...;
vec3 v3;
v3.x = 0.0;
v3.y = v2.x;
v3.z = v2.y; |
Initialiser le contenu d'un vecteur
Maintenant que vous savez comment fonctionne l'opérateur de cast, vous devriez comprendre facilement ce que fait ce bout de code :
Code : Autre1
| vec3 v = vec3(0.0, 1.0, 0.5); |
Allez cherchez un peu
...
Alors vous trouvez ?
...
Bon, je sens que vous avez fait bouillonner votre cerveau, c'est l'heure de votre récompense :
Code : Autre1
2
3
4
5
| vec3 v;
v.x = 0.0;
v.y = 1.0;
v.z = 0.5; |
Et voilà l'travail
Il est également possible d'initialiser toutes les composantes d'un vecteur d'un seul coup :
Code : Autre
Ce code place toutes les composantes du vecteur
v à 0.
Et les matrices... ?
Vous savez quoi ? On peut mettre des vecteurs dans des matrices

Si si je vous assure

Ainsi, ce code est tout à fait correct :
Code : Autre1
2
3
4
5
6
7
8
| vec4 a, b, c, d;
a = vec4(1.0, 0.0, 0.0, 0.0);
b = vec4(0.0, 1.0, 0.0, 0.0);
c = vec4(0.0, 0.0, 1.0, 0.0);
d = vec4(0.0, 0.0, 0.0, 1.0);
mat4 m = mat4(a, b, c, d); |
Ce code a pour effet de charger dans
m la matrice d'identité. Bien évidemment, il est inutilement lourd à cause de la création de 4 vecteurs, il n'est là qu'à titre indicatif
Il est important de noter ici que chaque vecteur représente une ligne de la matrice, c'est-à-dire que le premier vecteur ira se loger de m1,1 à m1,4
Tout comme pour les vecteurs, il est possible d'initialiser une matrice ainsi :
Code : Autre
Toutefois il y a une différence ici avec les vecteurs. L'initialisation d'une matrice comme nous l'avons fait place toutes les composantes de
la diagonale de la matrice à 1, et toutes les autres à 0. Avec 1, la matrice chargée est celle d'identité, avec une autre valeur, la matrice est une matrice de mise à l'échelle de la valeur envoyée.
Les commentaires
Tout comme en C, il est possible d'intégrer des commentaires en GLSL.
Ils ont la même forme ainsi que le même comportement :
Code : Autre1
2
| /* commentaire sur
plusieurs lignes */ |
Le GLSL accepte également les commentaires commençant par // comme en C99 :
Code : Autre1
| // ceci est un commentaire sur une seule ligne |
Les tableaux
Encore une similitude avec le C : les tableaux. Ils se définissent et s'utilisent comme en C.
Il est interdit de fournir une variable comme taille de tableau lors de sa déclaration, seules les constantes sont acceptées :
Code : Autre1
| float tab[3] = {0.0, 0.5, 1.0}; |
En revanche l'accès aux valeurs contenues dans un tableau est des plus simples, cette fois-ci les variables sont bien sûr acceptées :
Code : Autre1
2
| int case = 2;
float val = tab[case]; // val = 1 |
Les tableaux en GLSL commencent également à 0 : tab[0]
Vous souvenez-vous de la manière dont on accède à une composante d'un vecteur ? Nous faisions comme ceci :
Code : Autre1
2
| vec2 v = vec2(1.0, 0.0);
float vx = v.x; |
Et bien sachez qu'il est possible de considérer un vecteur comme un tableau !
Ainsi, ce code est
strictement identique :
Code : Autre1
2
| vec2 v = vec2(1.0, 0.0);
float vx = v[0]; |
Notez que l'on préfèrera la première méthode car elle est beaucoup plus légère et beaucoup plus lisible

.
Et pour les matrices ? Il existe aussi un tour de passe-passe ?
Les tableaux à deux dimensions
Rien qu'à la vue de ce titre, j'imagine que vous prévoyez déjà ce que je vais vous dire

Allons-y franchement : les matrices sont des tableaux à deux dimensions !
Si vous connaîssez les tableaux à deux dimensions en C, alors vous ne devriez pas avoir de problèmes.
Allez, un exemple de code vaudra sûrement mieux qu'un long discours :
Code : Autre1
2
3
4
5
6
7
8
9
| mat4 m = mat4(
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 5.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0);
float var = m[2][1];
// ici, var = 5.0 |
Dans
m[i][j] on a :
- i : la ligne (position en hauteur)
- j : la colonne (position en largeur)
Et voilà, rien de très compliqué, encore faut-il le savoir

.
Et comment on crée un tableau à deux dimensions ?
Comme en C :
Code : Autre1
2
3
4
5
6
7
8
9
10
| float tab[3][3] =
{
{0.0, 0.0, 0.0},
{0.0, 0.0, 2.0},
{0.0, 0.0, 0.0}
};
float var = tab[1][2];
// var = 2.0 |
Notez qu'il est impératif de définir la taille d'un tableau lors de sa déclaration en GLSL, contrairement au langage C qui est capable de déduire tout seul de la taille d'un tableau rien que par son contenu. Cette règle s'applique aussi bien aux tableaux 1D que 2D.