Programmer en C++
OpenGL - Animation

1. Timer ou pas timer ?

Pour réaliser une animation, on pourrait simplement mettre le "renduScene()" en boucle, et changer progressivement certaines valeurs de positions, ou d'angles. Cette méthode possède l'inconvénient de pousser la carte graphique à 100% de ses capacités.
J'utiliserai plutôt un timer (un genre de chronomètre) qui décidera quand le rendu devra être réactualisé. Cette façon de faire est plus économe en énergie, et donne aussi de très bon résultats.
La déclaration du timer se fait avec la fonction :

glutTimerFunc(40, onRelance, 1);

• Le premier argument correspond au temps d'attente qui est ici de 40ms.
• Le deuxième argument est le nom de la fonction qui sera appelée lorsque le temps sera écoulé.
• le dernier argument est juste un indice dans le cas d'utilisation de plusieurs timers. On le laisse ici à 1.
Cette fonction sera à placer juste avant "glutMainLoop()" appartenant à la fonction "main()".

Ecrivons maintenant la fonction "onRelance()" :

void onRelance(int i)
{
    glutPostRedisplay();
    glutTimerFunc(40, onRelance, i);
}

Au déclenchement du timer, la fonction "glutTimerFunc()" transmet en argument l'indice du timer, à la fonction "onRelance()". On aura donc ici : i = 1.
La fonction "glutPostRedisplay()" lance un nouveau rendu en réactivant la fonction "glutDisplayFunc(renduScene)" se trouvant dans "main()". C'est justement ce que nous souhaitions !
Pour terminer on relance le même timer avec la même durée que précédemment. Vous remarquerez surement la façon astucieuse de transmettre l'indice.
A l'issue du temps écoulé, on recommence et ainsi de suite ...

2. Modification de la fonction "renduScene()" pour créer l'animation

Juste avant de dessiner notre triangle, nous ajoutons :

glLoadIdentity();

Elle permet une réinitialisation de la matrice de projection.
Nous ajoutons également à suivre :

gluLookAt(0.0, 0.0, 6.0,
               0.0, 0.0, 0.0,
               0.0, 1.0, 0.0);

Cette nouvelle fonction permet de positionner la caméra qui visionne la scène.

Découvrons maintenant la signification de ces arguments :

• Les trois premier indique la position (x1, y1, z1) de la caméra. Avec le choix z1 = 6.0, on voit que nous avons éloigné la caméra de 6 unité, de la position du triangle.

• Les trois arguments suivants (x2, y2, z2) indique le point vers où la caméra regarde. C'est donc ici le centre de notre scène.

• Les trois derniers (x3, y3, z3) permettent d'incliner la caméra autour de son axe de vision. Ces coordonnées représente en fait un vecteur P traversant de part en part la caméra de bas en haut. Incliner ce vecteur conduit donc a incliner la caméra. Le fait d'écrire (x3 = 0, y3 = 1, et z3 = 0), force la caméra à rester debout bien droite.

L'animation arrive maintenant grace à la fonction :

glRotatef(angle, 0.0, 1.0, 0.0);

Cette fonction entraine la rotation de notre scène de la valeur donnée par la variable "angle". Cette variable est en degrès. Vous l'avez surement compris, c'est en faisant évoluer cette variable que nous allons animer notre triangle.

On parle ici de rotation, mais une rotation autour de quel axe ?
Bonne question ! En fait ce sont les trois derniers paramètres qui vont définir un vecteur autour duquel se fera la rotation. Comme nous avons choisi y = 1, c'est donc un vecteur vertical que nous aurons.
Il reste le sens. Avec le choix d'une valeur positive pour "angle" on aura donc forcément un sens trigonométrique, ce qui donnera lorsque nous regarderons la scène par le dessus, un sens de rotation inverse des aiguilles d'une montre.

Nous avons terminé, il ne reste plus qu'à incrémenter notre variable "angle" et de tester notre programme.

angle += 5.0;     // En degrès

En tête de programme il ne faudra pas oublier de déclarer la variable "angle" de cette façon :

float angle = 0.0;     // En degrès

3. Le code complet

#include <GL/glut.h>

float angle = 0.0;     // En degrès

void renduScene()
{
    glClearColor(1.0, 0.8, 0.4, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glLoadIdentity();     // Réinitialisation de la matrice

    // Positionnement de la caméra
    gluLookAt(0.0, 0.0, 6.0,
                   0.0, 0.0, 0.0,
                   0.0, 1.0, 0.0);

    glRotatef(angle, 0.0, 1.0, 0.0);     // On applique une rotation

    glBegin(GL_TRIANGLES);
                glColor3f(1.0, 0.0, 0.0);     // De la couleur
                // Coordonnées des vertex (des points)
                glVertex3f(-1.5, -1.5, 0.0);
                glVertex3f(1.5, 0.0, 0.0);
                glVertex3f(0.0, 1.5, 0.0);
    glEnd();

    angle += 5.0;     // En degrès

    glutSwapBuffers();
}

void changeDimensions(int w, int h)
{
    // On se protège d'une division par zéro, et on calcul le rapport W/H.
    // W = Width = Largeur de la fenêtre, h = hauteur de la fenêtre
    if(h == 0)
            h = 1;
    float rapportWsurH = 1.0 * w / h;

    // Choix d'une matrice de calcul pour la projection des points
    glMatrixMode(GL_PROJECTION);

    // Remplacement de la matrice actuelle par la matrice unité.
    glLoadIdentity();

    // Paramétrage de la fenêtre d'affichage
    glViewport(0, 0, w, h);

    // Paramétrage de l'observation, perspective, ...
    gluPerspective(45, rapportWsurH, 1, 50);

    // Retour à la vue initiale
    glMatrixMode(GL_MODELVIEW);
}

void onRelance(int i)
{
    glutPostRedisplay();
    glutTimerFunc(40, onRelance, i);
}

int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(300, 200);
    glutCreateWindow("Triangle en rotation");

    glutDisplayFunc(renduScene);
    glutReshapeFunc(changeDimensions);

    glutTimerFunc(40, onRelance, 1);

    glutMainLoop();

    return 0;
}