TP - Shaders et pipeline

Partie 1 : shader == fonction...

compilez shader_kit pour tester vos shaders.

comme abordé en cours, un shader est une fonction très classique, sa seule particularité est de s'exécuter (en parallèle) sur les processeurs de la carte graphique.
ses paramètres lui sont transmis en suivant une convention particulière, ce sont des variables globales (déja déclarées)

// fragment shader
// in int gl_PrimitiveID;   // (entree) indice du triangle
// in vec4 gl_FragCoord;    // (entree) coordonnees du fragment dans l'espace image
// in bool gl_FrontFacing;  // (entree) orientation du triangle

out vec4 fragment_color;    // (resultat obligatoire) couleur du fragment

void main( )
{
    fragment_color= vec4(1, 1, 1, 1);
}
testez ce shader avec shader_kit
bin/shader_kit shader1.glsl

remarque : enregistrez le shader dans le repertoire gkit2light, au meme endroit que premake4.lua

vous aller obtenir une image blanche, le fragment shader renvoie la valeur vec4(r= 1, g= 1, b= 1, a= 1) qui represente une couleur, le blanc opaque pour chaque pixel pour lequel il est exécuté. les coordonnées de ce pixel (exprimées dans le repère image) sont dans le paramètre gl_FragCoord.xy. elles varient de (0, 0) en bas à gauche de l'image jusqu'à (largeur, hauteur) en haut à droite de la fenetre.

vous pouvez modifier cette couleur (ouvrez le source dans votre editeur), enregistrez la modification, et appuyez sur la touche R dans la fenetre de shader_kit, le shader est rechargé, recompilé et exécuté.

indication : les couleurs sont des reels entre 0 et 1...


il y a un autre shader dans le fichier, un vertex shader bien sur, le pipeline graphique a besoin de ces 2 shaders / fonctions pour s'exécuter.

que fait le vertex shader ?
// in int gl_VertexID           // (entree) indice du sommet

// out vec4 gl_Position;        // (resultat obligatoire) coordonnees (transformees) du ieme sommet de l'objet

void main( )
{
    vec2 positions[3]= vec2[3]( vec2(-1,-3), vec2(3, 1), vec2(-1, 1) );
   
    gl_Position= vec4(positions[gl_VertexID], 0, 1);
}
le vertex shader est utilisé 3 fois, pour transformer / produire les coordonnées des 3 sommets d'un triangle. chaque appel de la fonction / du vertex shader, doit produire les coordonnées du sommet d'indice gl_VertexID (qui varie de 0 a 2 inclu). Ces coordonnées sont simplement stockées dans le tableau positions[].

quelques remarques sur la syntaxe du langage GLSL : ça ressemble à du C, ça ressemble aussi à du C++, mais ce n'est ni l'un, ni l'autre... un passage rapide dans shader et GLSL donne les différences à connaitre. (par exemple le passage des parametres... et les tableaux de vecteurs...)

exercice 1 :

modifiez les coordonnées des sommets du triangle dans le vertex shader, pour dessiner un triangle entièrement visible.
modifiez la couleur "calculée" dans le fragment shader



exercice 2 :

dessinez / coloriez un disque de centre c et de rayon r. comment evaluer la distance entre le pixel et le centre ? dans le fragment shader ?

remarque : les fonctions de la librairie math des shaders est documentée ici, sur opengl.org
par exemple, float distance( vec2 a, vec 2 b );

rappel: les coordonnées des fragments / pixels sont dans le repère de la fenetre. vous pouvez utiliser le paramètre optionnel de shader_kit pour récupérer les dimensions de la fenetre. cf uniform vec2 viewport;

rappel: les paramètres optionnels fournis par shader_kit sont décrits sur cette page.



par exemple, il est possible d'utiliser la position de la souris... (déclarez en global dans le fragment shader uniform vec3 mouse;)



ou de charger un objet 3d, mais il faudra modifier le vertex shader pour transformer les sommets de l'objet...




exercice 3 :

affichez un objet 3d, data/bigguy.obj ou data/robot.obj par exemple, que faut-il modifier pour afficher un objet (composé de plusieurs triangles...) ?
bin/shader_kit shader.glsl data/bigguy.obj

comment remplir chaque triangle avec un cercle différent ? placé au centre du triangle (ou ailleurs... ) ? comment calculer les coordonnées du centre du triangle ?
indications :
    quel shader connait les coordonnées du sommet ?
    dans quel repère ?
    peut-on calculer les coordonnées du sommet dans l'image ?
    comment le fragment shader peut-il connaitre cette information ?
    comment calculer les coordonnées du centre du triangle dans le fragment shader ?

rappel : le vertex shader peut déclarer des résultats supplémentaires, en plus de gl_Position, et le fragment peut récupérer la valeur... cf le résumé de GLSL

   


pour les curieux:
si vous voulez continuer à jouer avec ce type d'affichage, thebookofshaders.com est particulierement bien écrit et illustré (et traduit en français aussi).
Les pages d'Inigo Quilez sont assez incontournables également, ainsi que shadertoy.com

Partie 2 : affichage et transformations

avant de vous lancer dans l'écriture de shaders plus complets et de l'application qui va avec (rappel : elle est responsable d'affecter une valeur à chaque paramètre utilisé par les shaders...), il est peut etre nécessaire de reprendre les bases : écrire une application, dessiner un objet avec draw(objet, model, camera), manipuler les transformations pour afficher un ou plusieurs objets. si vous ne l'avez pas deja fait, relisez attentivement et jouez avec tuto_transformations...

Partie 3 : et pour de vrai ?

Avant de modifier votre application pour utiliser des shaders, il est plus simple de les écrire et de les tester avec shader_kit. Mais il faut respecter 3 conventions :

rappel: les paramètres fournis par shader_kit sont décrits sur cette page...

Le pipeline openGL utilise un vertex shader et un fragment shader, par exemple, au minimum

#version 330

#ifdef VERTEX_SHADER
layout(location= 0) in vec3 position;    //!! attention layout(location= 0) obligatoire pour shader_kit et Mesh !!
uniform mat4 mvpMatrix;                  //!! attention mat4 mvpMatrix obligatoire pour shader_kit !!

void main( )
{
    gl_Position= mvpMatrix * vec4(position, 1);
}
#endif

#ifdef FRAGMENT_SHADER
out vec4 fragment_color;

void main( )
{
    fragment_color= vec4(1, 0.6, 0, 1);
}
#endif

Une fois que les shaders fonctionnent dans shader_kit, que faut-il faire pour les utiliser dans l'application ? cf tuto9
(et éventuellement tuto10 pour utiliser directement openGL...)

Modifiez votre application tube pour décorer les tubes calculés dans le tp précédent. par exemple, en fonction de la position par rapport à la courbe, son abscisse entre le premier point et le dernier calculé.