M2 - Images


TP2 - openGL et shaders


Partie 1 : application interactive.

reprennez le tuto1 sur la construction d'une application openGL.

vous pouvez choisir, soit la version C qui utilise les fonctions de window.h, soit dériver la classe App de base, cf app.h

ajoutez un nouveau projet dans premake4.lua et compilez votre premiere application. cf section "premake et les projets" dans installation.


rappel : pour dessiner, il faut s'assurer que le pipeline est correctement configure, au minimum, il faut :

glBindVertexArray(); // un format de sommet
glUseProgram();      // un shader program
glUniform();         // affecter une valeur a chaque uniform declare par les shaders du program
glDrawArrays(primitives, nombre de sommets);

+ les options globales, dans quelle image dessiner, la couleur et la profondeur par defaut, l'orientation des faces visibles, etc. repassez dans "intro pipeline graphique", si vous avez un doute, et dans tuto3GL pour les fonctions openGL correspondantes.

exercice 1 : charger, compiler et linker les shaders.

vous pouvez le faire directement comme indique dans tuto2GL (version window.h), tuto2GL_app (version App) et tuto1GL.cpp ou utiliser les utilitaires de program.h, cf read_program().
(rappel : a faire dans la partie init() de votre application. il ne faudra pas non plus oublier de detruire les shaders et le program avec glDeleteShader() et glDeleteProgram() dans quit(), ou en utilisant release_program() de program.h)


dans un premier temps, pour verifier que tout fonctionne, utilisez les shaders fournis avec tuto1GL : tuto1GL_vertex.glsl et tuto1GL_fragment.glsl.
ces shaders permettent de ne dessiner qu'un seul triangle, il faut donc un glDrawArrays(GL_TRIANGLES, 3) pour les utiliser.


n'hesitez pas a relire "shaders et GLSL" pour une presentation de la syntaxe de GLSL, le langage permettant d'ecrire les shaders.

Partie 2 : et avec des shaders ? et des parametres uniforms ?

exercice 2 : translation et uniform

déclarez un paramètre uniform dans le vertex shader, une translation sur x, par exemple et modifiez la position du sommet en fonction.

avant de dessiner le triangle, il faut completer l'application en affectant une valeur au nouvel uniform du shader, cf tuto3GL, section "uniforms".
l'affectation d'une valeur à un uniform est faite par la famille de fonctions glUniform(). mais il faut connaitre l'identifiant de l'uniform, cf glGetUniformLocation().


exercice 3 : matrice de transformation

modifiez l'uniform pour utiliser une matrice 4x4, cf mat4 dans GLSL. comment transformer la position d'un sommet en utilisant la matrice ?
il faut egalement modifier l'application, l'affectation d'un uniform de type matrice utilise glUniformMatrix().

vous pouvez utiliser une rotation sur l'axe Z pour faire tourner le triangle dans l'image. cf RotationZ() dans mat.h

exercice 4 : interactions clavier

modifiez l'application pour reagir aux evenements claviers, faites tourner le triangle vers la gauche lorsque vous appuyez sur la touche 'j', par exemple, et dans l'autre sens si vous appuyez sur 'k'. cf key_state() et clear_key_state() de window.h.


remarque : vous pouvez aussi utiliser la souris, si vous le souhaitez, cf SDL_GetMouseState() et SDL_GetRelativeMouseState(), cf gestion des evenements de SDL2.


exercice 5 : transformations standards

modifiez votre shader pour utiliser les transformations standards, modifiez egalement l'application en fonction. Vous pouvez déclarer 3 uniforms ou composer les transformations model, view et projection et ne déclarer qu'un seul uniform.


exercice 6 : camera et orbiter

utilisez un Orbiter de gkit pour deplacer librement la camera autour de l'objet / du triangle. utilisez la souris pour controler l'orbiter. cf tuto5


bonus : vous pouvez également construire le votre. le principe d'un orbiter est de toujours observer le centre de l'objet en restant à une certaine distance. La position de l'orbiter est représentée par 2 angles, c'est un point sur une sphere (centrée sur l'objet et de rayon fixe). Il faut prévoir de pouvoir déplacer l'orbiter sur la sphere et de calculer la transformation View (passage repere Monde vers Camera).

Partie 3 : et avec plus de geometrie ?

exercice 7 : une grille de points

le vertex shader est execute une fois par sommet a traiter pour dessiner des primitives. sa seule fonction dans le pipeline est de fournir la position du sommet d'indice gl_VertexID, peu importe comment il obtient les informations necessaires, en utilisant un tableau local, un tableau uniform, un buffer, ... ou par calcul.

ecrivez un shader permettant de dessiner une grille de points, 10x10, par exemple. dans l'application, il faudra demander a dessiner le bon nombre de points, avec glDrawArrays(GL_POINTS, n);

les points ne sont dessines sur un seul pixel par defaut, on peut les faire grossir avec glPointSize(3.5), pour parametrer le pipeline. il suffit de le faire une seule fois dans la partie init() de votre application.

utilisez un orbiter et la souris pour deplacer le point de vue pour afficher la grille. comment initialiser l'orbiter (cf Orbiter(center, size)) ?


exercice bonus : c'est un peu plat, non ? modifiez la position des points pour afficher une surface differente. utilisez le temps pour animer votre surface, cf global_time() de window.h




exercice 8 : et pour de vrai ?

les exercices precedents permettent de comprendre un peu mieux le fonctionnement du pipeline, mais ne montrent pas l'utilisation classique : charger la description d'un objet depuis un fichier, construire un ou plusieurs buffers et configurer les attributs.

c'est la classe Mesh (cf mesh.h) et read_mesh() de wavefront.h qui permettent de charger et de representer un maillage au format wavefront '.obj'. vous pouvez utilisez Blender pour convertir depuis plusieurs autres formats (cf file/export wavefront .obj).

les coordonnees des sommets doivent etre stockees dans un buffer alloue dans la memoire de la carte graphique, c'est l'objet buffer d'openGL qui permet de faire cette allocation et de transferer les donnees.
il faut egalement decrire le contenu du buffer au pipeline pour qu'il puisse fournir les coordonnees des sommets aux vertex shader.

remarque : n'oubliez pas de declarer l'attribut dans le shader... cf in vec3 position; par exemple.

relisez tuto4GL et affichez un objet, le repertoire data de gKit contient un cube et un bonhomme, bigguy.