Tuto 1 : principes de GLUT
Si vous navez jamais fait encoredopengl sur Visual Studio.NET, lancez ce wizard fait par NeHe pour quilinstalle tout le nécessaire : http://nehe.gamedev.net/counter.asp?file=files/appwizards/nehe_opengl_appwizard_vs_net.zip
Puis, mettez le fichier de GLUT(glut.h) que vous pouvez trouver ici : http://www.xmission.com/~nate/glut/glut-3.7.6-bin.zip dans le dossier Program Files\MicrosoftVisual Studio 8\VC\PlatformSDK\Include\gl\ et vous serez capables de programmeren glut sur visual studio !!!
Lancez Visual studio et créez unnouveau projet (empty project) ou ouvrez le projet que je vous envoie.
Si votre projet est nouveau,allez dans les options du projet et dans configuration properties/linker/input,tapez : glu32.lib opengl32.lib glut32.lib dans additional dependencies.Toujours dans linker, allez dans debugging et dans generate debug info,choisissez Yes(\DEBUG) ettout en bas, choisissez : Runtime tracking anddisable optimizations (/ASSEMBLYDEBUG). Si vous ne voulez pas que la console saffichelorsque vous lancez votre programme, ajoutez la ligne : /SUBSYSTEM:WINDOWS/ENTRY:mainCRTStartup dans commandline de linker.
La première étape consiste en ladéclaration et implémentation de main(), qui se placera à la fin du code, ouaprès la déclaration des prototypes. Avec GLUT, il faut utiliser un main()normal et non pas WinMain(), contrairement à lopengl normal sous windows.Analysons cette fonction :
int main(int arg, char **argc)
{
//ici, on vainitialiser les propriétés de la fenetre que l'on va créer
//commen on vaafficher (le '|' permet d'utiliser plusieurs éléments dans
//un seulargument grace au code binaire: chaque élément a le '1' dans une rangéé,
//et avec '|'(or), plusieurs rangées auront des 1 pour avoir plusieurs fonctionnalités
glutInitDisplayMode( GLUT_DOUBLE |GLUT_DEPTH | GLUT_RGBA | GLUT_MULTISAMPLE );
//ici, ondéfinit les coordonnées de la position de la fenetre dans l'écran
glutInitWindowPosition( 0, 0 );
//ici, la taillede la fenetre
glutInitWindowSize( 800, 600 );
//ici oninitialize le tout par rapport aux ressources (args) et on crée la fenetre
glutInit(&arg,argc);
glutCreateWindow("première fenetre glut");
//ici, on appelle la fonction InitGL(), que l'on va implémenter plus tard
// et qui vacontenir toutes les initialisations de l'opengl: emplacement des textures,
//des 'lights',du 'blending' etc...
InitGL();
//ici, on dit àglut quelles fonction il devra utiliser dans la boucle principale (glutMainLoop())
//en mettantcomme argument le pointeur de la fonction écrite. Elles seront appelées dans laboucle
//principalequand il y en aura besoin
glutDisplayFunc(DrawGLScene); //fonctiond'affichage: c'est le code principal de votre programme
glutReshapeFunc(glutResize); //appelée en cas de'resize' de la fenetre
glutKeyboardFunc(keydown); //appeléequand l'utilisateur appuie sur une touche normale
glutKeyboardUpFunc(keyup); //relache latouche normale
glutSpecialFunc(specdown); //appuie surune touche spéciale (F1, flèches...)
glutSpecialUpFunc(specup); //relache latouche spéciale
glutMainLoop(); //boucle du jeu
return 1;
}
Maintenant, on va regarder le code de InitGL(), qui est déjàassez commentée ;). Cette fonction lancée au début du programme sert àinitialiser et régler tous les paramètres opengl que lon va utiliser pendantle programme. Par exemple, si on veut mettre en place une lumière, ou le alphablending, effacer lécran, mettre en place une display list etc.
Les fonctions de couleurs comme par exemple glClearColor()et glColor3f() utilisent comme arguments la quantité de chaque couleur :le rouge, le vert et le bleu (RGB) et parfois RGBA où le A correspond au niveaualpha (la transparence).
glColor3f(floatred, float green, float blue) ;
les arguments de cette fonction sont des float qui vont de0.0f à 1.0f où 0.0f est le plus foncé et 1.0f le plus clair. Pour ne pasgaspiller du temps à faire des conversions implicites, on met directement le.0 qui différencie des int et le f qui différencie des double. Enchangeant les arguments de glClearColor(), vous pouvez vous amuser à changer lacouleur de le fenêtre !
int InitGL(GLvoid) // All Setup For OpenGL Goes Here
{
//glShadeModel(GL_SMOOTH); //Enable Smooth Shading
//glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background
//glClearDepth(1.0f); //Depth Buffer Setup
//glEnable(GL_DEPTH_TEST); //Enables Depth Testing
//glDepthFunc(GL_LEQUAL); //The Type Of Depth Testing To Do
//glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really Nice Perspective Calculations
glMatrixMode(GL_PROJECTION);
// Set the persepctive. View angel 45, aspect ration 1, near plance 1
// and the far clipping plane at 200. The last two mean to start drawing
// from 1 unit in front of you up to 200 units far.
gluPerspective(45, 1.3, 0.1, 200.0);
// Change to the modelview matrix.
glMatrixMode(GL_MODELVIEW);
glClearColor(0.0f, 0.0f, 0.0f, 0.5f); //Cette fonction permetd'effacer
//l'écran avec la couleur spécifiée
returnTRUE; // Initialization Went OK
}
Lutilisation dun type de retour int peut être utile pourvérifier le fonctionnement de cette fonction : dans main, on pourraitmettre :
if ( !InitGL()){
MessageBox(...) ; //pour dire où est le problème
return 0 ; //sortir de main() et lexécution du programme
}
glutResize() est une sorte de InitGL, qui remet en place leseffets, la perspective etc. lorsquon modifie les dimensions de la fenêtre.
void glutResize(int width, int height)
{
glViewport(0,0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
/* modify this line to change perspective values */
gluPerspective(45.0,(float)width/(float)height,1.0, 300.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
Maintenant, nous allons passer aux autres fonctionsCALLBACK, c'est-à-dire les fonctions qui sont prises en compte parglutMainLoop() après les avoir spécifiées dans les fonctions du typeglut
Func(void (*)(/*arguments nécessaires à respecter*/) func) ;
Les fonctions que jai appelées keydown() et keyup() sontcelles qui soccupent de lappui de touches avec un code ASCII, c'est-à-direles lettre, chiffres, entrée, ESC etc
sauf les flèches, les fonctions spéciales(F1, F2
) et dautres. Jai déclaré en variable globale un tableau de 256éléments de type bool pour mettre cette touche en true lorsquelutilisateur appuie sur la touche et en false quand il la relâche, comme çail pourra appuyer sur plusieurs touches en même temps. Si vous avez besoin dunappui simple sur une touche (donc quil suffit dappuyer une seule fois, parexemple pour changer le statut de la lumière quand on appuie) il vaut mieux demettre directement un switch(key) dans la fonction et y spécifier ce quil fautfaire. Ceci attendra un peu après lappui pour éviter que le changementlumière/pas lumière ne soit pas trop rapide et difficile à bien gérer
De même, pour specdown() et specup(), sauf que les touchessont des touches spéciales (F1, F2, flèches, pgDown etc
). Ici, le tableau quejai déclaré est skeys[].
Pour ensuite accéder aux touches, vous ferez parexemple : if (keys[l]) {
} ou if ( !keys[l]) {
}. Dans windows.h,il y a des macros (#define) pour les touches normales : par exemplekeys[VK_ESC]. Pour les spéciales, il y a des macros : GLUT_KEY_F1 ouGLUT_KEY_LEFT etc
Dans le tableau skeys, je ne sais pas vraiment le nombre detouches quil y a donc jai mis 256 éléments :o|.
Les types des arguments sont à respecter !! dans lakeyboardFunc, key est un unsigned char alors que dans specialFunc ununsigned int !! Les autres arguments x et y correspondent aux coordonnéesde la positions de la souris au moment de lappui.
void keydown(unsigned char key, int x, int y)
{
keys[key] = true;
switch(key)
{
case VK_ESCAPE: exit(0); break;
}
}
void keyup(unsigned char key, int x, int y)
{
keys[key] = false;
}
void specdown(int key, int x, int y)
{
skeys[key] =true;
switch(key)
{
case GLUT_KEY_F1:
glutFullScreen();
}
}
void specup(int key, int x, int y)
{
skeys[key] =false;
switch(key)
{
}
}
Nous passons maintenant à la displayFunc, que jai appeléeDrawGLScene() . Cest la fonction appelée à chaque boucle de jeu et oùlon dessine le tout. glLoadIdentity() sert à remettre à zéro la matrice etannuler toute translation ou rotation exécutée préalablement. glutSwapBuffers()sert à changer de buffer : il y a 2 buffers (back buffers), quand luntravaille, lautre affiche : ce sont des tampons mémoire qui salternent.
void DrawGLScene(GLvoid) // Here's Where We Do All The Drawing
{
glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer
glLoadIdentity(); //on 'reset' lamatrice de l'écran: origine au centre de la fenetre
glutSwapBuffers(); //changement des buffers
glutPostRedisplay(); //reaffiche leplan
}
Et voilà !!! Notre fenêtre est créée et il nous resteplus quà dessiner !!!
Premiers affichages
Dans ce tutoriel, on va commencer à dessiner dans lafenetre.
Pour dessiner des triangles et dautres polygones, onutilise le système glBegin() ; glEnd() ; . Entre ces parties de code,nous allons dessiner en utilisant des Vertex. Voici un exemple :
glBegin(GL_TRIANGLES);
glVertex3f( 0.0f, 1.0f, 0.0f);
glVertex3f( 1.0f, 1.0f, 0.0f);
glVertex3f(-1.0f,-1.0f, 0.0f);
glEnd();
largument de glBegin() est un GLenum qui indique ce quelon veut dessiner, par exemple GL_TRIANGLES pour des triangles, GL_QUADS pourdes quads (c'est-à-dire des quadrilatères), GL_POLYGON pour _un_ polygone etc
GL_POLYGON ne dessinera quun polygone, ayant le nombre de points situés entreglBegin() et glEnd(). Parcontre, GL_TRIANGLES dessinera un triangle tous les 3points présents dans le bloc et GL_QUADS un quadrilatère tous les 4 points.
Note : le 3f dans glVertex indique le type deparamètres utilisés : 3 indique quil a 3 coordonnées (il existe aussiavec 2 et 4 coordonnées, mais comme je capte pas trop lutilisation de la 4ècoordonnée, jen parle pas) et f quil sagit dun float (avec v, on met unseul argument qui est un tableau (v pour vector) du nombre déléments requis).
Pour ajouter des couleurs, on utilise glColor3f() ou 4f silon veut le alpha. Quand on appelle cette fonction, elle sera gardée jusquàce que lon ne change la couleur à nouveau. Ici, par exemple, on va dessinerune pyramide en couleur :
glBegin(GL_TRIANGLES);
glColor3f(1.0f, 0.0f, 0.0f); glVertex3f( 0.0f, 1.0f, 0.0f);
glColor3f(0.0f, 1.0f, 0.0f); glVertex3f(-1.0f,-1.0f, 1.0f);
glColor3f(0.0f, 0.0f, 1.0f); glVertex3f( 1.0f,-1.0f, 1.0f);
glColor3f(1.0f, 0.0f, 0.0f); glVertex3f( 0.0f, 1.0f, 0.0f);
glColor3f(0.0f, 0.0f, 1.0f); glVertex3f( 1.0f,-1.0f, 1.0f);
glColor3f(0.1f, 0.1f, 0.1f); glVertex3f( 1.0f,-1.0f,-1.0f);
glColor3f(1.0f, 0.0f, 0.0f); glVertex3f( 0.0f, 1.0f, 0.0f);
glColor3f(0.1f, 0.1f, 0.1f); glVertex3f( 1.0f,-1.0f,-1.0f);
glColor3f(0.0f, 0.0f, 1.0f); glVertex3f(-1.0f,-1.0f,-1.0f);
glColor3f(1.0f, 0.0f, 0.0f); glVertex3f( 0.0f, 1.0f, 0.0f);
glColor3f(0.0f, 0.0f, 1.0f); glVertex3f(-1.0f,-1.0f,-1.0f);
glColor3f(0.0f, 1.0f, 0.0f); glVertex3f(-1.0f,-1.0f, 1.0f);
glEnd();
Maintenant, on va ajouter des translations et des rotationspour faire des animations.
Pour voir un peux plus loin et pas trop grand, on effectueau début une translation de 6.0f sur laxe z :
glTranslatef(0.0f,0.0f,-6.0f);
Cette fonction admet comme argument les coordonnées duvecteur de translation.
Si lon voulait par exemple zoomer et dézoomer la pyramide,on peut utiliser une variable qui change lorsque lon appuie sur les touchespage up et page down. Pour cela, on déclare un variable globale au début duprogramme :
float deep = -6.0f ;
Et ensuite on la change avec lappuie des touches (ce codepeut être mis dans DrawGLScene()) :
if (skeys[GLUT_KEY_PAGE_UP])
{
deep -= 0.02f;
}
if (skeys[GLUT_KEY_PAGE_DOWN])
{
deep += 0.02f;
}
et on appellerait glTranslatef avec cette variable :
glTranslatef(0.0f,0.0f, deep) ;
Voilà la fonction en entier. La forme de la pyramide est unpeu étrange à cause de la perspectice, et parce quil lui manque le coté endessous que vous pouvez dessiner avec GL_QUADS (noubliez pas la variableglobale deep !!) :
void DrawGLScene(GLvoid) // Here's Where We Do All The Drawing
{
if (skeys[GLUT_KEY_PAGE_UP])
{
deep-= 0.02f;
}
if (skeys[GLUT_KEY_PAGE_DOWN])
{
deep+= 0.02f;
}
glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer
glLoadIdentity(); //on 'reset' lamatrice de l'écran: origine au centre de la fenetre
glTranslatef(0.0f, 0.0f, deep);
glBegin(GL_TRIANGLES);
glColor3f(1.0f,0.0f, 0.0f); glVertex3f( 0.0f, 1.0f,0.0f);
glColor3f(0.0f,1.0f, 0.0f); glVertex3f(-1.0f,-1.0f,1.0f);
glColor3f(0.0f,0.0f, 1.0f); glVertex3f( 1.0f,-1.0f,1.0f);
glColor3f(1.0f,0.0f, 0.0f); glVertex3f( 0.0f, 1.0f,0.0f);
glColor3f(0.0f,0.0f, 1.0f); glVertex3f( 1.0f,-1.0f,1.0f);
glColor3f(0.1f,0.1f, 0.1f); glVertex3f(1.0f,-1.0f,-1.0f);
glColor3f(1.0f,0.0f, 0.0f); glVertex3f( 0.0f, 1.0f,0.0f);
glColor3f(0.1f,0.1f, 0.1f); glVertex3f( 1.0f,-1.0f,-1.0f);
glColor3f(0.0f,0.0f, 1.0f); glVertex3f(-1.0f,-1.0f,-1.0f);
glColor3f(1.0f,0.0f, 0.0f); glVertex3f( 0.0f, 1.0f,0.0f);
glColor3f(0.0f,0.0f, 1.0f); glVertex3f(-1.0f,-1.0f,-1.0f);
glColor3f(0.0f,1.0f, 0.0f); glVertex3f(-1.0f,-1.0f, 1.0f);
glEnd();
glutSwapBuffers(); //changement des buffers
glutPostRedisplay(); //reaffiche leplan
}
Maintenant, de même, on va ajouter des rotations avec lafonction glTranslatef(). Cette fonction a comme arguments langle de rotationet les coordonnées du vecteur autour duquel on fait la rotation. Par exemple :
glRotatef(xrot, 1.0f, 0.0f, 0.0f); //faire une rotation de xrot degrés sur levecteur de
//coordonnées (1, 0, 0) donc laxe (Ox)
On va ajouter de la rotation à la pyramide sur lappui desflèches. Pour cela, on va utiliser deux variables globales : xrot et yrotpour les rotations respectives sur x et sur y et on va ajouter le code suivantaprès les autres if :
if (skeys[GLUT_KEY_LEFT])
yrot -= 0.04f;
if (skeys[GLUT_KEY_RIGHT])
yrot += 0.04f;
if (skeys[GLUT_KEY_UP])
xrot -= 0.04f;
if (skeys[GLUT_KEY_DOWN])
xrot +=0.04f;
Et celui-là après le glTranslatef() :
glRotatef(yrot, 0.0f, 1.0f, 0.0f);
glRotatef(xrot, 1.0f, 0.0f, 0.0f);
Une autre possibilité est celle dajouter de la vitesse derotation lorsquon appuie sur ces touches:
Variables globales à ajouter : float xspeed =0.0f ; float yspeed = 0.0f ;
On modifie les if précédents pour quil change lavitesse de rotation (si cest trop rapide sur votre ordinateur, changezlaugmentation):
if (skeys[GLUT_KEY_LEFT])
yspeed -=0.005f;
if (skeys[GLUT_KEY_RIGHT])
yspeed +=0.005f;
if (skeys[GLUT_KEY_UP])
xspeed -=0.005f;
if (skeys[GLUT_KEY_DOWN])
xspeed +=0.005f;
Et enfin, on ajoute ce code àla fin de DrawGLScene() pour qua chaque boucle la pyramide fasse une rotationrelavive à la vitesse :
xrot +=xspeed;
yrot += yspeed;
Voilà le code final :
void DrawGLScene(GLvoid) // Here's Where We Do All The Drawing
{
if (skeys[GLUT_KEY_PAGE_UP])
{
deep-= 0.02f;
}
if (skeys[GLUT_KEY_PAGE_DOWN])
{
deep+= 0.02f;
}
if (skeys[GLUT_KEY_LEFT])
yspeed-= 0.005f;
if (skeys[GLUT_KEY_RIGHT])
yspeed+= 0.005f;
if (skeys[GLUT_KEY_UP])
xspeed-= 0.005f;
if (skeys[GLUT_KEY_DOWN])
xspeed+= 0.005f;
glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer
glLoadIdentity(); //on 'reset' lamatrice de l'écran: origine au centre de la fenetre
glTranslatef(0.0f, 0.0f, deep); //translation (zoom)
glRotatef(yrot,0.0f, 1.0f, 0.0f);
glRotatef(xrot,1.0f, 0.0f, 0.0f);
glBegin(GL_TRIANGLES);
glColor3f(1.0f,0.0f, 0.0f); glVertex3f( 0.0f, 1.0f,0.0f);
glColor3f(0.0f,1.0f, 0.0f); glVertex3f(-1.0f,-1.0f,1.0f);
glColor3f(0.0f,0.0f, 1.0f); glVertex3f( 1.0f,-1.0f,1.0f);
glColor3f(1.0f,0.0f, 0.0f); glVertex3f( 0.0f, 1.0f,0.0f);
glColor3f(0.0f,0.0f, 1.0f); glVertex3f( 1.0f,-1.0f,1.0f);
glColor3f(0.1f,0.1f, 0.1f); glVertex3f(1.0f,-1.0f,-1.0f);
glColor3f(1.0f,0.0f, 0.0f); glVertex3f( 0.0f, 1.0f,0.0f);
glColor3f(0.1f,0.1f, 0.1f); glVertex3f(1.0f,-1.0f,-1.0f);
glColor3f(0.0f,0.0f, 1.0f); glVertex3f(-1.0f,-1.0f,-1.0f);
glColor3f(1.0f,0.0f, 0.0f); glVertex3f( 0.0f, 1.0f,0.0f);
glColor3f(0.0f,0.0f, 1.0f); glVertex3f(-1.0f,-1.0f,-1.0f);
glColor3f(0.0f,1.0f, 0.0f); glVertex3f(-1.0f,-1.0f,1.0f);
glEnd();
glBegin(GL_QUADS);
glColor3f(0.0f,0.0f, 1.0f); glVertex3f( 1.0f,-1.0f,1.0f);
glColor3f(0.1f,0.1f, 0.1f); glVertex3f(1.0f,-1.0f,-1.0f);
glColor3f(0.0f,0.0f, 1.0f); glVertex3f(-1.0f,-1.0f,-1.0f);
glColor3f(0.0f,1.0f, 0.0f); glVertex3f(-1.0f,-1.0f,1.0f);
glEnd();
xrot +=xspeed;
yrot +=yspeed;
glutSwapBuffers(); //changementdes buffers
glutPostRedisplay(); //reaffiche leplan
}
Pour vous entrainer, vouspouvez faire en sorte que le cube se remette en position initiale quand onappuie sur une touche.