L3IF Images
année 2004-2005
http://www710.univ-lyon1.fr/~jciehl/
TD1 - Transformations/Visualisation 3D
Si une fonction ne donne pas le résultat prévu,
essayez un des tutoriaux interactifs de Nate Robin (version Windows)
version Linux
1. Quelques précisions : coordonnées et transformations openGL
Il y a deux méthodes pour placer des objets dans
une scène openGL. Soit spécifier directement les matrices
4x4 de changement de repère (transformation du repère local
de l'objet vers le repère de la scène), soit utiliser les fonctions
limitées à la description d'une translation, d'une rotation,
d'un changement d'échelle. Ces transformations élémentaires
sont composées par openGL afin de déterminer la matrice homogène
de changement de repère (cf. Annexes pour quelques
précisions sur le calcul des compositions).
openGL gère 2 repères : un repère
correspond à la scène et permet de décrire la position
des objets, le deuxième décrit la camera et détermine
la visualisation ainsi que la projection utilisée.
openGL calcule automatiquement la composition de transformations.
Pour rendre cette composition plus souple et permettre de décrire
de manière hiérarchique les différents repères,
openGL conserve une pile des transformations successives. La pile utilisée
pour placer les objets dans la scène, est identifiée par GL_MODELVIEW.
L'autre pile permet de spécifier la visualisation de la scène
et le type de projection utilisée, elle est identifiée par
GL_PROJECTION. Il est également possible de placer la matrice
identité sur le sommet de pile afin de démarrer la description
d'une nouvelle composition de transformations. Les opérations push
et pop sont également disponibles.
exemple : positionner un objet (transformation monde -> camera)
glMatrixMode(GL_MODELVIEW);
glLoadIdentity(); // pas de transformation
glTranslatef(tx, ty, tz); // translation
glRotatef(a, axe_x, axe_y, axe_z); // rotation de a degrés autour du vecteur axe_x, axe_y, axe_z
exemple : description hiérarchique.
Placer le bras d'un bonhomme par rapport à son squelette.
repere_tronc()
tracé_du_tronc();
glPushMatrix(); // conserver la matrice du repere global
glRotatef(40., 0,0,1); // tranformation locale pour le bras gauche (rotation / repere du tronc)
tracé_du_bras();
glPopMatrix(); // revenir au repere global
glPushMatrix(); // conserver la matrice du repere global
glRotatef(-40., 0,0,1); // transformation locale pour le bras droit (rotation / repere du tronc)
tracé_du_bras();
glPopMatrix(); // revenir au repere global
exemple : projection orthographique (cf. TD Introduction)
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-1.,1., -1.,1., 1.,-1.);
exemple : projection perspective (cf. TD Introduction)
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60., 1., 1., 100.);
Résumé des fonctions utiles:
description dans le manuel
de programmation (chapitre 3)
manpages
Quelques précisions sur les manipulations et l'ordre
de composition des transformations, tirées du manuel, cf. Annexe.
2. Décrire des objets
OpengGL est capable de dessiner un certain nombre de
primitives : des points, des lignes, des triangles, des quadrangles, des
polygones convexes. Toute géométrie est décrite comme
un ensemble de ces primitives. Elles sont elles-mêmes décrites
par des sommets et des propriétés associées : couleur,
normale à la surface, coordonnées de textures, transparence,
etc.
Les fonctions glVertex()
permettent de décrire la position d'un sommet dans le repère
courant. Les propriétés définies par les fonctions glColor,
glNormal, glMaterial, glTexCoord, etc. sont associées au sommet suivant
(n'oubliez pas que GL est une machine à état).
exemple : un sommet bleu
glColor3f(0.0, 0.0, 1.0);
glNormal3f(0.0, 1.0, 0.0);
glVertex3f(0.0, 0.0, 0.0);
3. Rappels / Exercices
Définissez une projection orthographique et dessinez
un triangle ou un rectangle coloré en modifiant minigl du
td précedent.
Animation. En utilisant le callback idle de glut
animez votre objet (une rotation ou un changement d'échelle, par exemple).
Manipulation. En utilisant le callback keyboard
ou mouse de glut modifiez la position de la camera ou de votre objet.
Définissez une projection perspective.
Utilisez l'élimination des faces cachées
automatique (Z-Buffer).
4. Questions Subsidiaires
Définissez une carte 2D, extrudez la géométrie
représentée et déplacez-vous à l'intérieur.
Comment détecter la collision avec la géométrie
lors d'un déplacement ?
Ajoutez des matières, des sources de lumières,
des textures, des animations, etc.
5. Annexes
Extraits à ne pas manquer du manuel de programmation
Thinking about Transformations
Let's start with a simple case of two transformations: a 45-degree counterclockwise
rotation about the origin around the z-axis, and a translation
down the x-axis. Suppose that the object you're drawing is small
compared to the translation (so that you can see the effect of the translation),
and that it's originally located at the origin. If you rotate the object first
and then translate it, the rotated object appears on the x-axis.
If you translate it down the x-axis first, however, and then rotate
about the origin, the object is on the line y=x, as shown in Figure
3-4. In general, the order of transformations is critical. If you do transformation
A and then transformation B, you almost always get something different than
if you do them in the opposite order.
Figure 3-4 : Rotating First or Translating First
Now let's talk about the order in which you specify a series of transformations.
All viewing and modeling transformations are represented as 4x4 matrices.
Each successive glMultMatrix*() or transformation command multiplies
a new 4x4 matrix M by the current modelview matrix C to yield
CM. Finally, vertices v are multiplied by the current modelview
matrix. This process means that the last transformation command called in
your program is actually the first one applied to the vertices: CMv.
Thus, one way of looking at it is to say that you have to specify the matrices
in the reverse order. Like many other things, however, once you've gotten
used to thinking about this correctly, backward will seem like forward.
Consider the following code sequence, which draws a single point using
three transformations:
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMultMatrixf(N); /* apply transformation N */
glMultMatrixf(M); /* apply transformation M */
glMultMatrixf(L); /* apply transformation L */
glBegin(GL_POINTS);
glVertex3f(v); /* draw transformed vertex v */
glEnd();
With this code, the modelview matrix successively contains I, N,
NM, and finally NML, where I represents the identity
matrix. The transformed vertex is NMLv. Thus, the vertex transformation
is N(M(Lv)) - that is, v is multiplied first by L, the
resulting Lv is multiplied by M, and the resulting MLv
is multiplied by N. Notice that the transformations to vertex v
effectively occur in the opposite order than they were specified. (Actually,
only a single multiplication of a vertex by the modelview matrix occurs;
in this example, the N, M, and L matrices are already
multiplied
into a single matrix before it's applied to v.)
General-Purpose Transformation Commands
If you want to specify explicitly a particular matrix to be loaded as the
current matrix, use glLoadMatrix*(). Similarly, use glMultMatrix*()
to multiply the current matrix by the matrix passed in as an argument. The
argument for both these commands is a vector of sixteen values (m1,
m2, ... , m16) that specifies a matrix M as follows:
[...]
Caution: If you're programming in C and you declare a matrix as
m[4][4], then the element m[i][j] is in the ith
column and jth row of the OpenGL transformation matrix. This is
the reverse of the standard C convention in which m[i][j] is in
row i and column j. To avoid confusion, you should declare
your matrices as m[16].
- void glLoadMatrix{fd}(const TYPE *m);
- Sets the sixteen values of the current matrix to those specified
by
m.
- void glMultMatrix{fd}(const TYPE *m);
- Multiplies the matrix specified by the sixteen values pointed
to by
m by the current matrix and stores the result as the
current matrix.
Note: All matrix multiplication with OpenGL occurs as follows: Suppose
the current matrix is C and the matrix specified with glMultMatrix*()
or any of the transformation commands is M. After multiplication, the
final matrix is always CM. Since matrix multiplication isn't generally
commutative, the order makes a difference.