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