TP2 Partie 2: OpenGL - vers la programmation GPU

Transcription

TP2 Partie 2: OpenGL - vers la programmation GPU
TP2 Partie 2: OpenGL - vers la programmation GPU
Pipeline graphique
1
Séquence de création de shaders coté CPU
Rappel du pipeline graphique
L’image de gauche montre une version simplifiée du pipeline graphique utilisé pour rendre nos scènes. Les
données brutes sont tout d’abord envoyées à la carte graphique puis une opération par sommet est réalisée
(vertex shader). Il s’agit de calculer l’éclairement et la position de chacun des sommets (notamment à l’aide des
matrices de projection et de modelview). Les primitives sont ensuites assemblées : le geometry shader consiste à
travailler individuellement sur ces primitives (par exemple des triangles) et éventuellement à en créer des nouvelles
(stream output stage). Après cette étape, certaines parties de la géométrie sont éliminées (les faces cachées,
les parties cachées et les parties hors champs) pour éviter de travailler sur des données qui ne seront pas affichées.
La rastérisation consiste à transformer les triangles en fragments (un fragment peut être assimilé à un pixel ; il
peut néanmoins y avoir plusieurs fragments par pixels). Dans le fragment shader, une opération par fragment
est réalisée pour déterminer la couleur finale du pixel. D’autres opérations sont enfin utilisées (brouillard, test de
visibilité) avant de dessiner les pixels. Dans la plupart de ces modules, il est possible d’accéder à la mémoire de
la carte graphique (buffers, textures, ...).
Jusqu’à présent, nous avons utilisés le pipeline par défaut fournit par OpenGL pour afficher nos scènes, mais depuis
quelques années, certains modules sont devenus programmables : les vertex shader et les fragment shader
depuis 2001 et les geometry shader depuis 2007. Ces parties programmables offrent une très grande flexibilité
pour éclairer, afficher et créer des effets sur une scène : il est désormais possible de travailler indépendemment sur
chaque sommet, chaque primitive et chaque pixel durant le rendu. Comme il existe des langages de programmation
pour programmer sur le CPU (C, C++, JAVA, python, ...), il existe des langages pour programmer sur le GPU
(seulement depuis 2002 : avant, il fallait programmer en assembleur). Parmis ces langages, on peut citer HLSL
pour DirectX, GLSL pour OpenGL, et Cg qui peut être utilisé pour DirectX et OpenGL. Ces 3 langages se
rapprochent du C. Nous utiliserons GLSL (OpenGL Shading Language1 ) dans les exercices qui suivent et
pour les prochains TPs.
1 http
://www.opengl.org/registry/doc/GLSLangSpec.Full.1.20.8.pdf
1
2
Liens entre OpenGL et GLSL
Pour programmer un shader (que ce soit un vertex, un geometry ou un fragment), il est necessaire de l’insérer
au sein d’une application OpenGL. Nous utiliserons des fichiers textes pour y placer le code des shaders. ces
fichiers seront ensuite chargés dans une application OpenGL, puis compilés (dynamiquement) avant de pouvoir
être utilisés. Toutes ces opérations se font grâce à des fonctions OpenGL qui permettent de créer les liens avec la
carte graphique : il faut expliquer à OpenGL qu’il ne faut pas utiliser le pipeline fixe comme on l’a fait jusqu’à
maintenant, mais qu’il faut passer par nos shaders personnalisés.
Dans ce TP, nous nous limiterons à la programmation par sommet et par pixel (vertex et fragment shader)
Le schéma de droite montre les différentes opérations à réaliser pour créer des shaders au sein d’un application
OpenGL. On y distingue principalement 2 objets : les shaders et les programs. Le premier concerne les shaders
eux même, qui contiennent le code de nos programmes (le code minimal des fichiers “shader.vert” et “shader.frag”
sera utilisé ici). Le second est un programme qui lie les différents shaders. C’est lui qui sera exécuté lors du rendu.
Comme pour les objets que nous avons vus précédemment (VBOs, textures), la manipulation d’un program ou
d’un shader se fait à l’aide d’un identifiant (GLuint). L’identifiant d’un shader se crée avec la fonction glCreateShader dans laquelle il faut spécifier si il s’agit d’un vertex shader ou d’un fragment shader. Il faut ensuite associer
le shader au code source (qui doit avoir la forme d’une chaı̂ne de caractères) avec la fonction glShaderSource. Il
faut enfin compiler le shader à l’aide de la fonction glCompileShader. Comme pour un shader, l’identifiant d’un
program est créé avec la fonction glCreateProgram. Un shader est associé au program avec la fonction glAttachShader (nous en aurons 2 : opération par vertex et par fragment). Il faut ensuite lier le program pour vérifier
que les shaders associés sont bien compatibles (fonction glLinkProgram). Enfin, l’utilisation d’un program se fait
en général lors du rendu avec la fonction glUseProgram (tout simplement parce qu’il est possible d’utiliser et
de switcher entre plusieurs programs pour un seul affichage). Notez que glUseProgram(0) permet de revenir au
pipeline fixe d’OpenGL.
Le code qui vous est fourni charge un objet en paramètre et l’affiche avec des VBOs2 (fichier “exo1.c”), comme
dans les TPs précédents. La seule différence est que nous voulons utiliser le pipeline programmable à la place du
pipeline fixe. Pour éviter de surcharger le code, les interfaces “shader” et “program” vous sont fournies.
Familiarisez vous avec les fichiers et les structures de données qui permettent de créer les shaders.
Complétez les fonctions shader create et shader delete du fichier “shader.c”. Pour cela, vous disposez d’une fonction load source qui permet de convertir un fichier en chaı̂ne de caractères, et de la fonction compilation status
qui permet d’afficher les erreurs (éventuelles) de compilation des fichiers GLSL.
Complétez les fonctions program create et program delete du fichier “program.c” (vous disposez aussi d’une fonction link status qui permet de savoir si le lien s’est bien passé).
Complétez la fonction display du fichier “exo1.c” pour utiliser le program lors du rendu.
Regardez les programmes GLSL utilisés ici. Que représentent les variables gl FrontColor, gl Position et gl Color
du fichier “shader.vert” ? Même question pour les variables gl Frag Color et gl Color du fichier “shader.frag”.
Que fait la fonction ftransform() ? Remplacez cette fonction par un calcul entre gl Vertex et les matrices de
modelview et de projection pour obtenir le même résultat (voir la spec pour les types, les variables, et les fonctions). Modifiez la couleur de l’objet (en modifiant la couleur sur chaque sommet ou en modifiant la couleur sur
chaque pixel). Modifiez la position de chacun des sommets de manière à ce que l’objet soit plat (par exemple
en mettant la coordonnée z à 0). Essayez d’autres effets simples et familiarisez vous avec le langage en utilisant la spécification et des tutoriaux sur internet (par exemple http ://www.lighthouse3d.com/opengl/glsl/ ou
http ://www.siteduzero.com/tutoriel-3-8894-opengl-les-shaders-en-glsl.html.
2 compilation :gcc meshLoader.c shader.c program.c exo1.c -o exo1.exe -framework OpenGL -framework GLUT
-lm.
2

Documents pareils