Correction examen
de TD n°1 2006-2007

RETOUR

25 octobre 2006 - 1h20

Question 1: Questions de cours

1) La fonction glBegin marque le début de la définition d'une primitive graphique (une exécution de la fonction glEnd est associée à tout exécution d'un glBegin pour marquer la fin de la primitive en cours). Les seuls objets affichables de la librairie GL (l'une des librairies standards d'OpenGL) sont les primitives graphiques.
Une primitive graphique sera définie par un ensemble de sommets spécifiés individuellement au moyen de la fonction glVertex entre les appels à glBegin et glEnd.
L'unique paramètre de glBegin est à choisir parmi 10 alternatives définissant 10 objets affichables différents. Les constantes d'énumérations utilisables sont:

2) Une lumière ponctuelle est une lumière placée en un point particulier, émettant une énergie lumineuse généralement colorée depuis ce point dans toutes les directions. L'énergie reçue par un objet o éclairé par une lumière ponctuelle lp est inversement proportionnelle au carré de la distance entre l et o.
Une lumière directionnelle est une lumière uniquement caractérisée par sa direction d'émission, émettant une énergie lumineuse généralement colorée. Tout point illuminé par une telle lumière le sera sous cette direction d'éclairage. Le parallélisme de tous les rayons lumineux conduirait à placer la source d'émission (la position de la lumière) à l'infini et donc à ne pas la placer réellement.

3) La fonction générique de configuration des sources lumineuses en OpenGL est glLightfv().
Le premier paramètre est le numéro de lumière (de GL_LIGHT0 à GL_LIGHTi).
Le deuxième paramètre est la constante d'énumération  GL_POSITION tant pour configurer une lumière ponctuelle que pour configurer une lumière directionnelle.
Le troisième paramètre est un tableau de 4 GLfloat (variable réelle sur 32 bits) où la 4ème valeur prendra soit (1) la valeur 0.0F, soit (2) autre chose que 0.0F (typiquement 1.0F). Dans le premier cas, une lumière directionnelle est définie et les 3 premières valeurs du tableau définissent la direction inverse de la direction d'incidence de la lumière. Dans le second cas, une lumière ponctuelle est définie et les 3 premières valeurs définissent la position de la lumière.

Exemples:

4) Les normales sont importantes car elles sont utilisées dans un nombre important de cas de figure en Informatique graphique.
L'utilisation la plus fondamentale est généralement réalisée dans le cadre des calculs d'ombrage.
Une normale étant orthogonale à la surface pour laquelle elle est définie, est caractéristique de l'orientation de cette surface dans l'espace. Ce vecteur peut donc être utilisé dans toute formule d'évaluation de la quantité de lumière touchant une surface (par unité de surface) en provenance d'une source lumineuse (exemple: formule de Lambert).
Un autre exemple d'utilisation est les calculs de réflexion spéculaire et de transmission en particulier pour l'algorithme du lancer de rayons.
un dernier exemple d'utilisation est que la normale en un point sur une surface est un moyen de savoir quelle est la face de cette surface que l'on visualise en ce point. Si la normale est orientée vers l'observateur, c'est la face "avant" qui est visualisée, sinon c'est la face "arrière" qui est vue. Si on considère que la caméra possède un axe de visualisation orientée en -z, si la normale à un z positif, elle est orientée vers la caméra. A contrario, si la normale à un z négatif, elle n'est pas orientée vers la caméra. Ce critère peut être utilisé pour améliorer la vitesse d'affichage par élimination de toutes les facettes orientées vers l'arrière pour les volumes facettisés. Pour ces objets on sait que ces facettes seront obligatoirement recouvertes par des facettes orientées vers l'avant.

5) La fonction gluPerspective est une des deux fonctions du mode GL_PROJECTION utilisable pour configurer une visualisation en perspective. La caméra ainsi configurée est placée en position (0.0, 0.0, 0.0) et est orientée selon l'axe -z. Elle visualise une portion d'espace définit par une pyramide tronquée dont la pointe serait la caméra et -z l'axe d'orientation. Les 4 paramètres définissent la taille de cette pyramide.
Le premier paramètre (o) est l'angle d'ouverture verticale qui définit la portion d'espace ajustée verticalement dans la fenêtre d'affichage.
Le deuxième paramètre (r) est le "ratio" de la caméra. r*o est l'angle d'ouverture horizontale de la caméra qui définit la portion d'espace ajustée horizontalement dans la fenêtre d'affichage. -> r doit être égal à w/h où w et h sont les résolutions en x et en y de la fenêtre d'affichage pour que la caméra ne déforme pas (i.e. n'étire pas) les objets qu'elle affiche.
Le troisième paramètre (v1) est la distance en -z en dessous de laquelle tout objet ou portion d'objet est éliminé.
Le quatrième paramètre (v2) est la distance en -z au delà de laquelle tout objet ou portion d'objet est éliminé.

Question 2: Animation en OpenGL

Les deux angles de rotation (la terre autour du soleil, la lune autour de la terre étant liés, une seule variable globale est nécessaire pour gérer cette animation. On choisit l'angle de rotation de la terre autour du soleil. Il est défini sous la forme d'une variable float initialisée à 0.0.

static float r = 0.0F;

La fonction idle a pour but d'incrémenter cet angle de la valeur de rotation entre deux images (0.2° dans l'implantation) et de "poster" un évènement rafraîchissement de la fenêtre d'affichage.

void idle(void) {
  r += 0.2F;
  glutPostRedisplay();
}

La fonction display réalise le dessin d'une seule image de l'animation. Elle inclut la ou les instructions glClear de vidage des tampons couleur et éventuellement profondeur, l'appel à une fonction dédiée au dessin de la scène (appel réalisé entre glPushMatrix() et glPopMatrix() pour isoler un dessin de scène du dessin réalisé à l'image précédente), l'appel à glFlush destiné à signifier qu'une image est terminée et peut être affiché et un éventuel appel à glutSwapBuffers sur un double buffer GLUT est utilisé.

void display(void) {
  glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) ;
  glPushMatrix();
  scene();
  glPopMatrix();
  glFlush();
  glutSwapBuffers();
}

La fonction scene est dédiée au dessin de la scène. Elle utilise la variable globale d'animation à sa valeur directe pour l'angle de rotation de la terre autour du soleil, multipliée par 12 pour l'angle de rotation de la lune autour de la terre.
Ces deux rotations sont réalisées autour de l'axe z pour que les mouvements de rotation soient effectués dans le plan xy.
La fonction scene utilise la fonction glMaterial pour configurer le matériel des 3 objets.
Il est nécessaire de configurer systématiquement la couleur de réflexion spéculaire au blanc pour la terre et au noir pour les 2 autres objets. En effet, pour la sphère soleil, on ne sait pas quelle est cette valeur héritée d'avant le lancement de la fonction scène. Pour la sphère lune, elle doit être placée au noir pour ne pas rester au blanc de la sphère terre.
La remarque est identique pour la valeur de réflectivité de la sphère terre qui est placée arbitrairement à 12.0 dans l'implantation proposée.

void scene(void) {
  GLfloat noir[4] = { 0.0F,0.0F,0.0F,1.0F };
  GLfloat blanc[4] = { 1.0F,1.0F,1.0F,1.0F };
  GLfloat jaune[4] = { 1.0F,1.0F,0.0F,1.0F };
  GLfloat bleu[4] = { 0.0F,0.0F,1.0F,1.0F };
  GLfloat gris[4] = { 0.5F,0.5F,0.5F,1.0F };
  glPushMatrix();
  glMaterialfv(GL_FRONT,GL_DIFFUSE,jaune);
  glMaterialfv(GL_FRONT,GL_SPECULAR,noir);
  glutSolidSphere(5.0,72,72);
  glRotatef(r,0.0F,0.0F,1.0F);
  glTranslatef(10.0F,0.0F,0.0F);
  glMaterialfv(GL_FRONT,GL_DIFFUSE,bleu);
  glMaterialf(GL_FRONT,GL_SHININESS,12.0F);
  glMaterialfv(GL_FRONT,GL_SPECULAR,blanc);
  glutSolidSphere(1.0,72,72);
  glRotatef(12.0F*r,0.0F,0.0F,1.0F);
  glTranslatef(3.0F,0.0F,0.0F);
  glMaterialfv(GL_FRONT,GL_DIFFUSE,gris);
  glMaterialfv(GL_FRONT,GL_SPECULAR,noir);
  glutSolidSphere(0.5,72,72);
  glPopMatrix();
}

Implantation complète

Question 3: Mathématiques

1) Une méthode pour déterminer la normale à une facette triangulaire constituée des trois sommets P1, P2 et P3 consiste:

  1. à calculer les vecteurs P3P1 et P3P2 (ces deux vecteurs ne peuvent pas être colinéaires si la facette n'est pas dégénérée),
  2. à calculer le produit vectoriel de ces deux vecteurs (le vecteur obtenu ne peut pas être nul, ces deux vecteurs n'étant pas colinéaires),
  3. à normer le vecteur obtenu.

2) Le développement est ici réalisé en java.
Trois classes sont développées:

La classe Direction contient un constructeur permettant de calculer le vecteur défini entre deux objets Position.
La classe Direction contient une méthode permettant de normaliser this.
La classe Triangle contient la méthode de calcul de la normale de this qui retourne un objet Direction.

public class Position {
  public float x;
  public float y;
  public float z;

  public Position() {
    x = y = z = 0.0F;
  }

  public Position(float x,float y,float z) {
    this.x = x;
    this.y = y;
    this.z = z;
  }

  public Position(Position p) {
    x = p.x;
    y = p.y;
    z = p.z;
  }

  public String toString() {
    return("["+x+","+y+","+z+"]");
  }
}

Position.java

public class Direction {
  public float dx;
  public float dy;
  public float dz;

  public Direction() {
    dx = dy = 0.0F;
    dz = 1.0F;
  }

  public Direction(float dx,float dy,float dz) {
    this.dx = dx;
    this.dy = dy;
    this.dz = dz;
  }

  public Direction(Direction d) {
    dx = d.dx;
    dy = d.dy;
    dz = d.dz;
  }

  public Direction(Position p1,Position p2) {
    dx = p2.x-p1.x;
    dy = p2.y-p1.y;
    dz = p2.z-p1.z;
  }

  public float norme() {
    return((float) Math.sqrt(dx*dx + dy*dy + dz*dz));
  }

  public void normalise() {
    float norme = norme();
    if ( norme != 0 ) {
      dx /= norme;
      dy /= norme;
      dz /= norme; }
  }

  public Direction produitVectoriel(Direction d) {
    return(new Direction(dy*d.dz - dz*d.dy,
                         dz*d.dx - dx*d.dz,
                         dx*d.dy - dy*d.dx));
  }
  
  public String toString() {
    return("["+dx+","+dy+","+dz+"]");
  }
}

Direction.java

public class Triangle {
  public Position p1;
  public Position p2;
  public Position p3;

  public Triangle() {
    p1 = new Position();
    p2 = new Position();
    p3 = new Position();
  }

  public Triangle(Position p1,Position p2,Position p3) {
    this.p1 = new Position(p1);
    this.p2 = new Position(p2);
    this.p3 = new Position(p3);
  }

  public Triangle(Triangle t) {
    p1 = new Position(t.p1);
    p2 = new Position(t.p2);
    p3 = new Position(t.p3);
  }
  
  public Direction normale() {
    Direction v1 = new Direction(p3,p1);
    Direction v2 = new Direction(p3,p2);
    Direction n = v1.produitVectoriel(v2);
    n.normalise();
    return(n);
  }
  
  public String toString() {
    return("["+p1+",\n "+p2+",\n "+p3+"]");
  }
}

Triangle.java

public class ExamTD120062007Exo3 {
  public static void main(String [] args) {
    Position s1 = new Position(1.0F,6.0F,3.0F);
    System.out.println(s1);
    Position s2 = new Position(8.5F,8.0F,2.0F);
    System.out.println(s2);
    Position s3 = new Position(6.5F,2.0F,1.0F);
    System.out.println(s3);
    Triangle t = new Triangle(s1,s2,s3);
    System.out.println(t);
    Direction n = t.normale();
    System.out.println(n);
  }
}

ExamTD120062007Exo3.java

Question 4:

1) L'utilisation de la fonction gluLookAt est recommandée lorsque le problème du placement à un endroit spécifique et de l'orientation d'une caméra de visualisation se pose dans un programme OpenGL.
En effet, les caméras OpenGL sont obligatoirement placées à l'origine et orientées selon l'axe -z. Lorsqu'une caméra doit être placée et orientée devant une scène (avec une position et une orientation définies dans le repère de la scène) la seule solution consiste à placer et orienter la scène devant la caméra (dans le repère associé à la caméra) pour donner l'illusion que c'est la caméra qui est placée et orientée vis à vis de la scène.

2) Dans l'ordre d'exécution du code OpenGL, l'appel à gluLookAt est à réaliser entre la définition de la caméra de visualisation et le dessin de la scène de manière à porter sur la scène sans porter sur la caméra. C'est un appel réalisé en mode MODELVIEW.
Dans le cadre de l'utilisation de GLUT, l'appel à gluLookAt pourra être placé dans la fonction reshape après la définition de caméra ou bien dans la fonction display avant le dessin de la scène.

3) Les 9 paramètres d'entête sont constitués de 3 triplets successifs:

Quelques indications sur l'évaluation

L'évaluation a été réalisée sur 25 pour tenir compte de la longueur du sujet. Le barème est 10, 5, 5, 5 pour les exercices 1 à 4.

Pages trouvées

Remarques, erreurs
nicolas.janey@univ-fcomte.fr