On va (enfin) apprendre à dessiner des points, des carrés, des rectangles...
Ils sont définis en OpenGL, à l'aide de listes de sommets (vertex), encadrés par glBegin() et terminés par un glEnd(), comme l'illustre le programme triangle1.py suivant :
from OpenGL.GL import * from OpenGL.GLUT import * from OpenGL.GLU import * import sys def display(): glClearColor(0,0,0,0) glClear(GL_COLOR_BUFFER_BIT) glBegin(GL_TRIANGLES) glVertex2f(-0.5,-0.5) glVertex2f(0.5,0.0) glVertex2f(0.0,0.5) glEnd() glFlush() def main(): glutInit(sys.argv) glutInitDisplayMode(GLUT_RGB) glutInitWindowPosition(200,200) glutInitWindowSize(250,250) glutCreateWindow("Un triangle") glutDisplayFunc(display) glutMainLoop() main()
Le résultat est :
Le système de coordonnées marche ainsi : en l'absence d'un déplacement du point de vue, l'origine est au centre de la fenêtre graphique, le coin supérieur droit a pour coordonnées (1;1), quand le coin inférieur gauche a pour coordonnées (-1;-1).
On place la définition de ce triangle dans la fonction qui sera appelée par glutDisplayFunc(). Les sommets du triangle sont donc écrits à la suite, entre le glBegin() et le glEnd(), en utilisant glVertex2f().
Le 2f de glVertex2f() signifie que l'on va travailler avec deux flottants. Ainsi, glVertex3i(), par exemple, existe aussi : on lui passera cette fois-ci trois arguments, correspondant aux trois coordonnées (entières) X,Y et Z, du sommet introduit.
L'argument de glBegin() est le type de figure à tracer : il renseigne OpenGL sur la manière de relier les différents sommets. Ainsi, glBegin(GL_TRIANGLES) signifie que les sommets qui suivent représentent des triangles plein, alors que l'exemple suivant ne demande d'afficher que les points (les sommets) :
def display():
glClear(GL_COLOR_BUFFER_BIT) glColor3f(1.0,1.0,1.0) glPointSize(4) glBegin(GL_POINTS) glVertex2f(0.0,0.0) glColor3f(1.0,0.0,0.0) glVertex2f(0.5,-0.5) glColor3f(0.0,1.0,0.0) glVertex2f(-0.5,-0.5) glColor3f(0.0,0.0,1.0) glVertex2f(-0.5,0.5) glEnd() glFlush()
La fonction glPointSize() fixe la taille du point à afficher (en pixels), quand glColor3f() indique la nouvelle couleur courante (toutes les figures qui seront tracées par la suite, le seront dans cette couleur).
Cette fonction nécessite 3 arguments flottants : les valeurs de la couleur choisie dans le système RGB. Ainsi, glColor3f(1.0,0.0,0.0) donnera du rouge, glColor3f(0.0,1.0,0.0) donnera du vert, etc.
Comment procéder si l'on a plusieurs triangles à tracer ? On peut utiliser GL_TRIANGLES, GL_TRIANGLE_STRIP, ou GL_TRIANGLE_FAN, selon nos besoins :
Dans l'exemple suivant :
def display():
glClear(GL_COLOR_BUFFER_BIT) glColor3f(1.0,1.0,1.0) glPointSize(4) glBegin(GL_TRIANGLES) glVertex2f(0.0,0.7) glColor3f(1.0,0.0,0.0) glVertex2f(-0.6,0.1) glColor3f(0.0,1.0,0.0) glVertex2f(-0.3,-0.2) glColor3f(0.0,0.0,1.0) glVertex2f(0.1,-0.3) glColor3f(1.0,1.0,0.0) glVertex2f(0.6,-0.1) glColor3f(0.0,1.0,1.0) glVertex2f(0.5,0.3) glColor3f(1.0,0.0,1.0) glEnd() glFlush()
OpenGL tracera le triangle correspondant aux trois premiers sommets, puis le triangle correspondant aux trois suivants.
Si on remplace GL_TRIANGLES par GL_TRIANGLE_STRIP, il reliera les sommets 1-2-3, puis les sommets 2-3-4, puis les sommets 3-4-5, et enfin les sommets 4-5-6, en superposant les quatre triangles ainsi obtenus.
Enfin, si on remplace GL_TRIANGLES par GL_TRIANGLE_FAN, OpenGL reliera les sommets 1-2-3, puis les sommets 1-3-4, puis les sommets 1-4-5, et enfin les sommets 1-5-6, c'est-à-dire que les triangles auront le premier sommet en commun.
Le programme triangle2.py permet de mieux se rendre compte des différents types de triangles, qui ont leur utilité du fait que tout objet (2d ou 3d) s'obtient par la juxtaposition de triangles élémentaires.
Dans ce programme, on gère le clavier :
GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_POINTS, et toutes les autres variables que l'on peut mettre dans glBegin(), sont en fait de type GLenum.
D'où l'idée pour faire le programme triangles.py : on a créé une variable globale, appelée mode, et de type GLenum, qui change de valeur selon les touches pressées par l'utilisateur (ce changement est logiquement géré dans la fonction clavier()).
On constate qu'OpenGL a ses propres types. On trouve ainsi les types GLint, GLdouble, etc.
glFlush() est nécessaire à la fin de la fonction s'occupant de l'affichage : elle force toutes les commandes à être exécutées, et envoi le flux de données vers le buffer d'affichage.
D'autres part, pour que la pression d'une touche soit suivie d'un effet, il faut préciser (dans la fonction qui gère les interactions avec ledit clavier) à OpenGL qu'il doit redessiner la scène, avec la fonction glutPostRedisplay(), comme il a été fait dans triangles.py.