Pile Matrices Modelview OpenGL
Pile Matrices Modelview OpenGL
rixed@[Link]
Abstract
Avec OpenGL, peut on déplacer un objet indépendamment des autres ? Com-
ment déplacer la caméra ? OpenGL a t-il ou pas une "caméra" ? Dans quel ordre
multiplier les matrices pour obtenir tel effet ? Toutes ces questions sont quotidi-
ennes sur les forums OpenGL (cf [Link] Apparemment, beau-
coup d’utilisateurs d’OpenGL ne comprennent pas comment utiliser efficacement
la pile de matrices M ODELV IEW. Il s’agit en fait d’un fonctionnement telle-
ment simple et naturel qu’OpenGL, contrairement à d’autres API graphiques,
ne cherche pas à le masquer derrière une batterie de fonctions de maniement de
la caméra. Cet article a pour but d’aider les programmeurs peut habitués aux
mathématiques utilisées en 3D à utiliser OpenGL efficacement.
La première partie de l’article, purement mathématiques, consiste en un rap-
pel de notions de géométrie et d’algèbre linéaire. Les notations employées sont
un compromis entre les notations usuelles et ce que LATEXm’a laissé faire.
La seconde partie est l’application de ces notions aux applications OpenGL
les plus typiques.
Avertissement : Pour des raisons évidentes de simplicité, les formules de
maths s’écrivent. Mais pour bien faire, elles devraient se dessiner. Je vous invite
donc fortement à lire cet article avec une feuille blanche et un crayon afin de
vous représenter graphiquement ce qui se cache derrière chaque formule au fur
et à mesure que vous lisez. C’est à ce prix (modique ; à défaut de feuille on
peut même écrire directement sur sa table, c’est très joli) que l’on comprend les
maths.
Le lecteur pressé pourra sauter les sections écrite dans une fonte plus petite ; ces
sections traitent des repères non orthonormés. La plupart des programmes OpenGL ne
travaillant qu’avec des repères orthonormés, cette lecture n’est donc pas utile, ni la dis-
tinction entre coordonnées contra- et co-variantes.
Contents
1 Notions de géométrie 2
1.1 Éclaircissements sur les vecteurs . . . . . . . . . . . . . . . . . . . . 2
1.2 Produit scalaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.3 Changement de repère . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.4 Notation matricielle . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.5 Résumons en 3 dimensions . . . . . . . . . . . . . . . . . . . . . . . 8
1.6 Changement de repère multiple . . . . . . . . . . . . . . . . . . . . . 8
1.7 Matrices 4x4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.8 Composition de matrice 4x4 . . . . . . . . . . . . . . . . . . . . . . 10
1
1.9 Inversion de matrice 4x4 . . . . . . . . . . . . . . . . . . . . . . . . 11
2 Mise en pratique 11
2.1 Fonctions OpenGL et pile de matrices . . . . . . . . . . . . . . . . . 11
2.2 Structures de données . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.3 Problèmes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.3.1 Rendre une liste d’objets sans architecture . . . . . . . . . . . 15
2.3.2 Rendre une liste d’objets architecturés . . . . . . . . . . . . . 15
2.3.3 Rotation d’un objet autour d’un axe de son repère . . . . . . . 17
2.3.4 Translation d’un objet le long d’un vecteur constant . . . . . . 17
2.3.5 Trouver un Vecteur perpendiculaire . . . . . . . . . . . . . . 17
2.3.6 Trouver deux vecteurs pour former un repère . . . . . . . . . 18
2.3.7 Déplacement de la caméra avec la souris . . . . . . . . . . . . 19
2.3.8 Calcul de la position du centre d’un objet dans le repère de la
caméra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.3.9 Frustum culling d’une sphère englobante . . . . . . . . . . . 23
List of Figures
1 Coordonnées contra-variantes . . . . . . . . . . . . . . . . . . . . . . 3
2 Coordonnées co-variantes . . . . . . . . . . . . . . . . . . . . . . . . 5
3 Changement de repère lorsque les origines ne coïncident pas . . . . . 9
4 Parcours en profondeur . . . . . . . . . . . . . . . . . . . . . . . . . 16
5 Construire un vecteur perpendiculaire . . . . . . . . . . . . . . . . . 18
6 Éloignement de la sphère et du frustum . . . . . . . . . . . . . . . . 25
1 Notions de géométrie
1.1 Éclaircissements sur les vecteurs
le déplacement qui conduit de à .
Un vecteur, c’est le déplacement d’un point à un autre, par exemple le vecteur est
Pour pouvoir repérer n’importe quel point dans un espace à dimensions (On
utilisera dans cet article des espaces à deux ou à trois dimensions), il suffit de se donner
vecteurs non colinéaires1 et une origine. N’importe quel point de l’espace est alors
une combinaison unique de ces vecteurs : tant de fois le premier vecteur plus tant de
fois le second, etc... Les valeurs des "tant" utilisés ici permettent donc de donner une
position à tous les points de l’espace ; on les appelle coordonnées du point.
L’ensemble de ces vecteurs et l’origine que l’on s’est donné forment une base, ou
un repère, vis à vis duquel chaque point n’a
seul jeux de coordonnées possible.
qu’un
Par exemple : si les vecteurs , et forment un repère dans un espace à
3 dimensions, alors à chaque on peut trouver des nombres ,
et tels que
point
de cet
espace
. Le trio est unique et est
1
deux vecteurs sont colinéaires, ou proportionnels, si l’un est un multiple de l’autre. Deux vecteurs
identiques sont un cas particulier de vecteurs colinéaires ou le facteur de proportionnalité vaut 1
2
appelé coordonnées contra-variantes de dans
le repère ! .
est la coordonnée de dans " le long de , etc...
Lorsque les vecteurs d’un repère sont tous orthogonaux deux à deux, le repère est
dit orthogonal. Si la longueur de tous les axes est l’unité, le repère est alors dit normé2 .
On se choisi souvent un repère construit avec un tel jeux de vecteurs pour jouer
le rôle de repère absolu. Ce repère est une pure commodité : lorsqu’on parlera de
coordonnées absolues, on voudra dire : coordonnées dans ce repère.
Un repère à la fois orthogonal et normal est dit orthonormé.
$
%
# & ') ( '+ des
Lorsqu’on veut parler
* )
' coordonnées
,
contra-variantes d’un point dans un repère
-.
particulier
0/21 . Les coordonnées de 34/21 -. , on 5. repère en indice supérieur, par exemple :
note le
4/
et de ne seront donc pas les mêmes !
On aura remarqué que les vecteurs s’ajoutent et se multiplient comme des déplace-
ments :
768:9;<= et 7>? ' :@A étant les coordonnées de deux vecteurs (dans un même repère),
et un nombre, on a :
BC768:9;<=DEBF68BG9;BF<)
L-PRQ-S
K
2
cela suppose que l’espace soit doté d’une mesure "absolue" de distance et d’angle
3
1.2 Produit scalaire
Nous allons voir maintenant une autre façon de se donner des coordonnées à partir
d’un repère, coordonnées dites covariantes. Nous allons le faire dans un espace à deux
dimensions car on voit mieux ce qui se passe et les calculs sont plus simples. Les
résultats auxquels nous allons aboutir restent vrais en
Le produit scalaire entre deux vecteurs
et W trois dimensions.
donne un nombre, noté
YX 0 W
et le vecteur
Une définition géométrique
entre le vecteur
0 W est
du produit
projeté
scalaire pourrait être celle-ci
W fois
: le produit scalaire
la longueur de
W . Ceci donne la formule suivante :
égal à la longueur de sur
donnent en effet deux nombres qui permettent de repérer sans ambiguïté la position du
point dans le repère5 .
On notera désormais les coordonnées covariantes avec un indice en bas et les co-
ordonnées contra-variantes avec un indice en haut.
sur
x y w
x y g
z2} (de même avec z2 ). Cela reviens à travailler avec les versions normées de ces deux vecteurs
mais plutôt afin de faire apparaitre l’angle droit de la projection de
4
% % A^
A^
% % %
% N
K
On a alors :
YX 0 W s W s u W u
C’est cette relation que l’on utilise souvent pour calculer un produit scalaire.
¦ ¦ ¦ § §
# s C s ¨ u # u C s ¨ u
5
¦ ¦ ¦ § ¦ ¦ §
© # s s # u s ¢ ¨ # s u # u u
Ou encore :
¦ ¦ ¦ ¦ § ¦ ¦ §
s u 2E # s s # u s # s u # u u
Cette formule est assez intuitive.
En passant, on peut trouver des formules supplémentaires en utilisant les coordonnées covariantes
(notées avec l’indice en bas) :
Par définition :
«ª )¬ ª I x yª| x y } ¬ x yª| x y4
Développons :
«ª ;¬ ª ?ig«ª 1 ~ x y ® ª 1 D~ x y¯ | x y } ¬ «ª 1 q~ x y ® ª 1 ¢~ x y¯ M| x y°
« ª ;¬ ª I«ª 1 ~ x y ® | x y } ª 1 D~ x y¯ | x y } ¬ ª 1 q~ x y ® | x y$ª 1 ¢~ x y¯ | x y°
«ª ¬ ª Ii«ª 1 ~ ® ª 1 ~ ¯ ¬ ª 1 ~ ® ª 1 ~ ¯
On peut le faire également dans l’autre sens :
«ª 1 ¬ ª 1 I x yª| x y ® ¬ x yª| x y¯
«ª 1 ¬ ª 1 8 x yª|± ® ~ x y }k ® ¢~ x y² ¬ x yª|¡ ¯ q ~ x y k } ¯ ¢ ~ x y4
g
« ª 1 ¬ ª 1 I ® ~ x yª| x y } ® ¢~ x y ª| x y ¬ ¯ q~ x yª| x y } ¯ ¢~ x yª| x y²
«ª 1 ¬ ª 1 I ® ~vª ® ¢~vª =¬ ¯ q~ª ¯ ¢~¤ª
1
Remarquez bien que si et sont tous deux orthonormés, les coordonnées contra-variantes sont
égales aux coordonnées covariantes et on obtient bien 3 fois la même formule.
§
µ
Cs Ds
Cu Du¶
6
Pourquoi cette notation loufoque ? J’ai noté en colonne les coordonnées contra-
variantes des vecteurs écrits en haut par rapport aux vecteurs écris à gauche. Lorsqu’on
multiplie ces tableaux (ou matrices) par un vecteur, il faut prendre les coordonnées du
vecteur par rapport aux axes notés en haut, et on obtient comme résultat les coordon-
nées du vecteurs par rapport aux axes notés à gauche.
Cette multiplication d’une matrice par un vecteur se déroule et se note comme ceci
:
¦ /21
¸d¹8º»5¼¾½8º¼ <^6 »5¼N¿ ¾ÀI µ
V’s
Á
§ / ¦ ui¶
V’
µ µ ° ¦ § ¦
Cs Ds = V
s s ¦ # s § °s ¦ #u
Cu Du ¶ u #s u #u ¶
V
u
Ou encore :
/´¤/ 1 ¦ / 1 ¦ /
³ À
Comme on le voit, cette notation ramasse beaucoup de significations.
Puisque le produit scalaire est commutatif, les coordonnées covariantes de et de
§
dans le repère
# s’obtiennent simplement à partir des coordonnées covariantes de
dans . Dans le cas où les repères et # sont orthonormés, les coordonnées
et
contra-variantes sont égales aux coordonnées covariantes. ³
/21´v/ s’obtient alors
facilement à partir de ³
/´¤/21 :
µ
§ C
s § s C
u § u
D
s s D
u u¶
On a simplement inversé lignes et colonnes. Deux matrices que l’on déduit l’une
de l’autre en inversant lignes et colonnes sont dites transposées l’une de l’autre. La
transposée de ³ est notée »Äà 6Å ½ g³U .
On peut donc rajouter :
/ 1 ´¤/ ¦ /
»5Ã 6MÅ ½ g³ /´¤/ 1 .À ¦ / ¦ / 1
³ À
qui est la relation inverse de celle vue plus haut.
Obtenir les coordonnées d’un vecteur dans un repère orthonormé donné quand on
les connaît dans un autre repère orthonormé consiste à chercher la matrice de passage
du premier repère vers le second, c’est à dire à connaître soit les coordonnées des axes
du premier repère dans le second repère, soit au contraire les axes du second dans le
premier. La notation est alors un bon moyen mnémotechnique de retrouver quelles
opérations effectuer pour trouver le résultat. Et c’est pour ça qu’on l’a inventé.
7
1.5 Résumons en 3 dimensions
¦
Les coordonnées d’un vecteur toujours
sont / un repère formé par
définies ¦ dans
une origine et trois vecteurs , et : on note et on l’écrit en notation
matricielle :
¦
ÆÇ
Vs
Vu
V È£ÉÊ
On note en colonne les coordonnées et à côté des coordonnées les vecteurs par
rapport auxquels ces coordonnées se¦ rapportent.
¦ ¦ ¦
Si le
repère est orthonormé,
s u
, et
È sont les produits scalaires entre et
,
et § . Ë et Ì forment un second repère orthonormé appelé # , la matrice
Si , , § § Ë Ì
formée par les coordonnées de , et par rapport à , et , multipliée (À )
par un vecteur exprimé dans donne le même vecteur exprimé dans # .
La matrice transposée multipliée par un vecteur exprimé dans # donne le même
vecteur exprimé dans .
Ë Í / 1 Ì 5 / 1
µ
ÀÁ § E’s F’s
§ Ë E’ / u Ì F’ / u ¶
µ µ s Ë # u4 § s s Ì # u4 § s
Cs Ds E’s° F’s²
Cu Du ¶
=
E’s
u Ë #u § u F’s
u Ì #u § u ¶
8
On appelle composition de matrice cette multiplication, À .
Ë et onÌ la note également
obtient en bas à droite du tableau les coordonnées de et par rapport à et
On
, c’est à dire la matrice de passage de # # vers . On note en abrégé :
/´v/ 1 1 /´v/ 1 / 1 ´¤/ 1 1
³ γ Àv³
On peut ainsi composer les matrices à volonté, en prenant toujours soin de faire
correspondre les vecteurs comme il faut (les vecteurs de la matrice en haut à droite
doivent être définis par rapport aux vecteurs de la matrice en bas à gauche, et le résultat
donnera les vecteurs de la matrice de droite par rapport aux mêmes vecteurs que les
vecteurs de la matrice de gauche).
Dans le cas où on manipule des repères orthonormés, on pourra également utiliser
les matrices transposées pour passer un vecteur dans les deux sens (du repère * vers
le repère , pour les vecteurs exprimés dans * que l’on veut connaître dans , , ou
dans l’autre sens pour les vecteurs exprimés dans , et que l’on veut connaître dans
* ).
1.7 Matrices 4x4
Jusqu’ici, tous nos repères étaient définis par rapport à la même origine, le point .
et des axes même
Que se passe t-il si on prend deux repères qui n’ont pas la
par exemple le repère formé de§ l’origine
origine
? Prenons
Ë Ì ¦ / 1 , et ¦ / repère $#
et le
formé de l’origine et des axes ¦ - , et . On a , on cherche .
/ 1 1 , où # # est le repère formé par
On sait déjà comment avoir ¦ / ¦w /2 1 1Ï 5I / , , et .
Géométriquement (Cf figure 3), on voit que .
OPR
VR’
VR’’ VR B
A
e2
C
P
OPR
O
e1
e0
L’idée des matrices 4x4 est de réaliser ce changement de repère et cette translation
supplémentaire dans un seul calcul matriciel. Pour rajouter cette opération, on rajoute
une colonne à la matrice habituelle, colonne contenant les coordonnées du déplace-
ment à ajouter au nouveau vecteur. Pour des raisons (essentiellement) de commodité,
on rajoute également une ligne afin d’avoir le même nombre de lignes que de colonnes.
9
Prenons la matrice 4x4 suivante :
§ Ë Ì Ñ
ÆÒÒ
Ç Ds Es Fs Ts
Du Eu Fu Tu
Ð DÈ EÈ FÈ TÈ É=ÓÓ
0 0 0 1 Ê
Ð
(Je note ’ ’ un vecteur qui n’a pas de signification évidente, et dont la ligne
vaudra toujours (K ,K , K , L ) dans cette article).
Multiplions cette matrice par le vecteur exprimé dans # avec une coordonnée
supplémentaire vallant toujours L :
¦ / 1
§ ÒÆÒ
Ë Ç Vs
Ì Vu
Ð V È =É Ó
1 Ê
Ó
#
On obtiendra :
¦ /
ÒÆÒ § ¦ Ë ¦ Ì Ñ
Ç V °
s § s ¦ Ë s ¦ È
u0 u È Ì su Ñ us
0
u
Vs² § u ¦ Ë ¦ Ì Ñ
Ð Vs È u È v È È È É=ÓÓ
1 Ê
¦ /
On a donc Ñle même que l’on aurait obtenu avec une matrice ÔBÔ normale,
/
plus le vecteur ¦ /
/ 1
On voit donc que pour pouvoir¦ ajouter un vecteur constant au vecteur Ñ au cours
/´¤/ 1 , il suffit de rajouter / dans la
de la multiplication du vecteur par ³ ¦
dernière colonne de la matrice, et L dans les coordonnées
Ñ
Ä. / .
Il faut donc, pour passer de à # , que vaille
de
, c’est à dire la position de
l’origine de $# exprimée dans .
Avec les matrices 4x4, on peut donc passer de repère à repère même s’ils n’ont pas
d’origine commune en une seule opération.
10
1.9 Inversion de matrice 4x4
L’inversion de matrice 4x4, par contre, ne se fait pas aussi naturellement que pour les
/´¤/21 /21×´¤/
matrices 3x3. Lorsque l’origine des repères est la même, on a vu que la matrice in-
verse de ³ Ñ ÕHÖÏÕ , c’est à dire ³ ÕHÖÏÕ , était simplement la transposée de ³ ÕHÖÏÕ .
/´¤/21
Lorsque
/´¤/21 doit en effet toujours avoir un vecteur Ñ dans
figure dans la matrice, la transposée n’a plus du tout de signification
géométrique. L’inverse de ³ Ø Ö Ø
la dernière colonne, et si on prenait la transposée de ³ Ø Ö Ø
/´¤/ 1 on se retrouverait tou-
jours avec le vecteur K K K L dans le dernière colonne, ce qui ne mènerait à rien.
Si on peut toujours transposer la partie “rotation” de la matrice Ñ (les 9 valeurs en
haut à gauche), il faut donc faire quelque-chose Ñ de spécial
/ 1 ¤
´ / pour .
5I /
géométriquement que le vecteur de ³ Ø Ö Ø
-. / 1
On voit doit valoir, non plus ,
mais +.
/21 on ne/2connaît
. Si
¾
1 ¤
´ / Ä.pas
/ ce vecteur, on peut leÄ?calculer
/ par
ÄI la/ méthode
habituelle ( γ ÕHÖÏÕ À , sachant bien sur que ).
2 Mise en pratique
2.1 Fonctions OpenGL et pile de matrices
OpenGL fonctionne avec des matrices 4x4, et considère que les vecteurs sont des
vecteur à 4 dimensions (dont la quatrième coordonnée vaut 1). Il met à disposition des
piles de matrices que l’on utilise grâce aux fonctions GL L OAD M ATRIX, GL P OP M A -
TRIX et GL P USH M ATRIX . Chaque pile de matrice possède un nom, et celle qu’openGL
utilise pour transformer les positions des points s’appelle M ODELV IEW.
GL L OAD M ATRIX remplace la matrice au sommet de la pile par la matrice donnée
en argument.
GL P OP M ATRIX incrémente le pointeur sur la pile (en se représentant la pile crois-
sant vers les adresses inférieures).
GL P USH M ATRIX copie la matrice qui se trouve au sommet de la pile vers le som-
met de la pile, et décrémente le pointeur sur la pile, ce qui a pour effet de faire une
copie de la matrice sur la pile.
Lorsqu’on donne un vecteur à openGL, il le multiplie par la matrice qui se trouve
au sommet de la pile M ODELV IEW pour obtenir les coordonnées du vecteur "par rap-
port à la caméra", c’est à dire juste avant projection.
et les axes D Ú , ¢ Û et Ü . Le
La bonne façon d’utiliser cette pile est donc la suivante :
Soit Ù le repère de la caméra, formé
DÚ , ¢Û et Ü sont les axes de la caméra (je vous
par l’origine
point est la position de la caméra,
rappelle que par défaut
DÚ pointe vers la droite, ¢ Û vers le haut, et Ü vers l’arrière).
Ces vecteurs sont connus dans le repère absolu de l’espace.
Soit Ý le repère d’un objet, formé
et des vecteurs Þ , ß et à , où
est la position de l’objet et Þ , ß et à les axes du repère de l’objet. Ces vecteurs
de l’origine
sont eux aussi connus dans le repère absolu. Les points de l’objet sont, quant à eux,
connus dans le repère Ý .
Appelons le repère absolu par rapport auquel les repères Ù et Ý sont définis
(c’est le repère du "monde"). ¦0 á
Les points de l’objet sont les vecteurs , exprimés dans Ý .
11
¦
¦¤ â à avoir
On cherche â ´ les ¦ á
á vecteurs dans le repère de la caméra. Donc, on doit
calculer les γ Ø Ö Ø À .
Que l’on peut décomposer comme ceci :
¦ â ⤴ / / ´ á ¦ á
γ Ø Ö Ø Àt³ Ø Ö Ø À
/ ´ á
pour n’utiliser que des matrices connues. En effet :
Ø Ö Ø
³ â ´¤/ est donnée par la position de l’objet dans .
³ Ø Ö Ø est l’inverse (la transposée) de la position de la caméra dans (c’est à dire
la position de dans le repère de la caméra â ´¤/ Ù )
Oná commence donc par empiler ¦ á ³ Ø Ö Ø dans M ODELV IEW, puis on multiplie par
³ Ø / Ö ´ Ø , et ensuite on envoi les .
Dans le cas d’objets hiérarchisés, c’est à dire lorsqu’on a par exemple Ý , , Ý Õ et
Ý Ø tous les trois définis dans Ý * , lui même défini dans , on fait :
â ´¤/
1. GL L OAD ³ Ø Ö Ø ,
/´ á±ã
2. GL M ULTIPLIE ³ Ø Ö Ø ,
On peut ainsi facilement gérer les objets organisés hiérarchiquement dans un arbre.
12
// structures
typedef struct {
union {
GLfloat c[4];
struct {
GLfloat x,y,z,u;
} n;
};
} Vecteur;
typedef struct {
union {
GLfloat c[16];
struct {
Vecteur x,y,z,t;
} n;
};
} Matrice;
typedef struct {
Matrice position;
Objet *pere;
void *(affiche(void));
} Objet;
// 10 objets et la caméra
#define NBOBJETS 10
Objet objet[NBOBJETS];
Objet camera;
Où les coordonnées des vecteurs ou les matrices peuvent être accédées directe-
ment par le tableau c ou-bien nommément grâce aux structures n. Ainsi, par exemple,
matrice.n.z.n.y désigne le même élément que matrice.c[9] ou matrice.n.z.c[1].
Attention : j’utilise ici des union non nommées, qui ne sont normalement pas valides
en C même si certains compilateurs C les acceptent. Si vous voulez utiliser ces struc-
tures, compilez en C++, qui accepte les unions non nommées.
L’objet contient l’élément pere qui pointe sur l’objet parent de celui considéré,
c’est à dire l’objet par rapport auquel la position de celui considéré est exprimée. Si la
position de l’objet est relative au repère absolu, ce pointeur vaudra NULL.
La fonction pointée par affiche affiche l’objet lui même. Par exemple, cette
fonction peut faire l’affaire si l’objet est un cube :
void afficheCube(void) {
13
glBegin(GL_QUAD);
glVectorf(-1,-1,-1);
glVectorf(-1,-1, 1);
glVectorf( 1,-1, 1);
glVectorf( 1,-1,-1);
glVectorf(-1, 1,-1);
glVectorf(-1, 1, 1);
glVectorf(-1,-1, 1);
glVectorf(-1,-1,-1);
... etc ...
glEnd();
}
Nous allons considérer dans la suite que l’objet caméra a sa position déjà inversée,
Ñ que la partie rotation de la position de la caméra est transposée, et que le
c’est à dire
vecteur de la position, plutôt que d’être la translation de dans le repère absolu ,
est la position de l’origine de dans le repère de la caméra Ù . Le déplacement de la
caméra se fait aussi aisément, parfois même plus aisément que si cette position n’était
pas inversée, et cela élimine le besoin de transposer cette matrice lors de l’affichage.
D’autre part, la position de la caméra sera considérée pour l’instant comme ex-
primée par rapport au repère absolu (son père vaut NULL).
Nous aurons également besoin d’un certain nombre de fonctions, dont voici les
déclarations (je ne donne pas toutes les implémentations) :
// copy v2 dans v1
void copy(Vecteur *v1, Vecteur *v2);
// renvoit m1*m2
void compose(Matrice *m1, Matrice *m2, Matrice *resultat);
// renvoit m*v
void multiplie(Matrice *m, Vecteur *v, Vecteur *resultat);
// renvoit v1.v2 (produit scalaire)
double scalaire(Vecteur *v1, Vecteur *v2) {
return v1->c[0]*v2->c[0]+v1->c[1]*v2->c[1]+v1->c[2]*v2->c[2];
}
// renvoit la norme d’un vecteur
double norme(Vecteur *v) {
return sqrt(scalaire(v,v));
}
// renorme un vecteur
void renorme(Vecteur *v) {
double n=norme(v);
if (0!=n) {
n = 1/n;
v->c[0] *= n;
v->c[1] *= n;
v->c[2] *= n;
}
14
}
// renvoit le produit vectoriel
void vectoriel(Vecteur *v1, Vecteur *v2, Vecteur *resultat);
2.3 Problèmes
2.3.1 Rendre une liste d’objets sans architecture
Revenons sur ce problème déjà traité. Voici, avec nos nouvelles structures, une fonc-
tion qui affiche tous les objets si tous les objets sont relatifs au repère absolu.
void afficheTout() {
int o;
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf([Link].c);
for (o=0; o<NBOBJETS; o++) {
glPushMatrix();
// Soit ce push et le pop qui suit,
// Soit déplacer le LoadMatrix de dessus ici.
// je préfère comme ceci car ca peut
// réduire les transferts RAM->VRAM
glMultMatrixf(objet[o].position.c);
objet[o].affiche();
glPopMatrix();
}
}
void afficheTout() {
int o;
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf([Link].c);
initPile();
pushPile(NULL); // pour la camera
for (o=0; o<NBOBJETS; o++) {
while (topPile()!=objet[o].pere) {
glPopMatrix();
15
Repère absolu
popPile();
}
glPushMatrix();
pushPile(objet+o);
glMultMatrixf(objet[o].position.c);
objet[o].affiche();
}
}
Objet *(pile[NBOBJETS]);
int idxPile;
void initPile() {
idxPile=0;
}
void pushPile(Objet *obj) {
pile[idxPile++]=obj;
}
void popPile() {
idxPile--;
}
Objet* topPile() {
return pile[--idxPile];
}
16
2.3.3 Rotation d’un objet autour d’un axe de son repère
Généralement, les objets que l’on affiche doivent se déplacer d’une image à l’autre.
Gérer les objets hiérarchisé facilite grandement le déplacement des objets. Dans la
plupart des phénomènes, en effet, les mouvements des objets peuvent être réduit à des
mouvements simples si on ”accroche” les objets entres eux de la bonne façon (par
exemple, dans le repère lié au cadre du vélo, la position de la roue se réduit à une
simple rotation autour de son centre).
Un cas intéressant de déplacement est la rotation autour d’un axe. Si cet axe est
à rotation autour d’un
constant dans le repère de l’objet, on peut ramener la rotation à une
des vecteurs formant le repère. Prenons par exemple
Þ et ß ). Appelons
le vecteur
Ý
du repère de l’objet
Appelons Ý #
(le troisième, les autres étant appelés à ce repère.
ce même repère tourné d’un angle f autour de l’axe ß de
Þ Ý . à La trigonométrie nous
donne les coordonnées par rapport à Ý des vecteurs # , # et # qui forment Ý # :
Þ # á E7\^]`_fJ_l«mOfJ K
ßÄ á
# b _lnmof\^]`_f K
àæ á
# E K à K L
Faire tourner les points de Ý autour de peut donc facilement s’obtenir en sup-
posant que les points sont en fait définis dans Ý # , lui même exprimé dans Ý . Cela
reviens, dans notre exemple, à dire que l’objet ¿ # est relatif à l’objet ¿ , et à donner à
l’objet ¿ la position initiale de l’objet que l’on veut faire tourner tandis que l’objet ¿ # a
comme position initiale l’identité, c’est à dire la matrice formée par des K partout et des
L sur la diagonale, et plus tard lorsque f deviens non nul par la matrice trigonométrique
exprimée tout à l’heure.
17
¦
ë ì ¦
ì
ç (presque quelconque)
ç ë ì ¦
ì N
18
Une implémentation possible :
æ Þ
entre les deux nous aurait compliqué la vie dans la suite.
Pour faire tourner la caméra autour de son axe , la trigonométrie nous in-
dique qu’il faut multiplier la position de la caméra par la matrice 4x4 suivante (qui ne
s’attaquera qu’à la partie rotation de la position), l’angle f étant proportionel à depY
:
19
N Ú 1 Û 1 Ü 1 Ñ
D Ú ÒÆÒ
Û Ç 1 0
0 \^]`_f
0
-_lnmof
0
Ü 0 _l«mof
0
\^]`_8f 0 =É ÓÓ
Ð
0 0 0 1 Ê
20
m->c[14] = coef[1];
}
Après ces calculs, la caméra aura donc tournée autours de son vecteur
D Ú pointant
vers la droite d’un angle proportionnel à depY.
un angle h (fonction de depX) autours de
Û quiIl pointe
faut faire la même oppération avec
vers le haut.
Mais attention : tous ces calculs auront pu propager des erreur d’arrondis dans la
position, et à force de les exécuter, il se peut qu’au bout d’un certain temps (très long
dans la pratique), la matrice représentant Ù se déforme de façon visible. Il faut donc,
de temps en temps, la renormaliser avant que ces déformations ne soient visibles.
Renormaliser la matrice
DÚ , ¢ Û et donnant
Ü sont bien la position de la caméra consiste ici à s’assurer
que les vecteurs
de norme 1 et orthogonaux Ú entre eux. Pour se
¢ Û etonle peut
faire, procéder comme ceci : d’abord,
Ü renormer Ü D Ú î
. Ensuite, réorthogonaliser
¢Û renormer. Finalement, recalculer avec un produit vectoriel :
.
¢ Û ¢ Û sa composante suivant D Ú , qui doit
Réorthogonaliser DÚ ¢consiste
Û à retirer de
être nulle Û puisque Û etÚ sont Ú censés être perpendiculaires. Û On a déjà vu comment
faire : E X I . Ne pas oublier de renormer après cette opération.
On aurrait pu effectuer ces opérations
Ü , puisque
dans un autre sens mais il est bon que la plus
grande opération soit effectuée sur
¢
c’est suivant Û Z que les changements
sont le moins visibles (et ensuite en général, le long de , car il y a souvent moins
de résolution en Y qu’en X dans l’image générée).
21
2.3.8 Calcul de la position du centre d’un objet dans le repère de la caméra
On a vu jusqu’à maintenant comment envoyer les points d’un objet à openGL pour que
leur positions soient correctement calculée par l’API. Mais souvent, indépendament de
l’affichage, on a besoin de connaitre la position du centre d’un objet dans le repère de
la caméra, notament pour déterminer avant de l’afficher s’il est visible ou pas.
Mettons que le centre de l’objet ne soit pas à l’origine du repère de l’objet (par ex-
emple, si l’objet est articulé autours d’un point précis, il est plus utile de mettre le cen-
tre du repère sur ce point d’articulation plutôt que su le centre de l’objet pour faciliter
son mouvement). On se donne donc le vecteur centre dans la structure Objet qui
donne la position du centre de l’objet dans le repère local de l’objet (ce vecteur est
donc constant, calculé par exemple une fois pour toute au début du programme). On
rajoute aussi le rayon de la sphère englobante, dont le centre est le centre de l’objet,
qui sera utile par la suite.
typedef struct {
Matrice position;
Vecteur centre;
double rayon;
Objet *pere;
void *(affiche(void));
} Objet;
void afficheTout() {
int o;
22
Matrice temp;
Vecteur centreOdansC;
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf([Link].c);
initPile();
pushPile(NULL); // pour la camera
for (o=0; o<NBOBJETS; o++) {
while (topPile()!=objet[o].pere) {
glPopMatrix();
popPile();
}
glPushMatrix();
pushPile(objet+o);
glMultMatrixf(objet[o].position.c);
glGetFloatv(GL_MODELVIEW_MATRIX,temp.c);
multiplie(&temp, &objet[o].centre, ¢reOdansC);
/* Ici on a le centre de l’objet exprimé dans le
* repère de la caméra. On peut par exemple s’en
* servir comme ca pour éliminer les objets qui
* sont entièrement dans le dos de la caméra :
*/
if (centre0dansC.n.z<objet[o].rayon) {
objet[o].affiche();
}
}
}
En fait, dans la plupart des cas il vaut mieux se garder de récuperer la matrice
M ODELV IEW aussi souvent ; En effet, sur les cartes vidéos qui font elles mêmes les
transformations des points, cette lecture nécessite de ramener les données de la carte
elle même et interrompt son travail le temps du transfert. Le temps ainsi perdu peut
être suppérieur au temps qu’il aurait fallu pour calculer soit même la matrice.
En fait, dans le cas le plus général, on n’a pas nécéssairement besoin de connaitre
le centre de tous les objets : par exemple, on se contentera de déterminer la visibilité
du vélo avant d’afficher tous les objets composants le vélos dans leur repères propres
; on a donc deux arborescences de repères : des repères à calculer soit même dont
on a besoin de connaitre la position (dans le repère absolu et/ou dans le repère de la
caméra, en fonction des besoins), et les repères de M ODELV IEW utiles à l’affichages,
plus nombreux.
23
mode GL_PROJECTION. Les paramètres sont tels que les coordonnées L L L
T
seront projetées dans le coin bas-gauche du viewport et
T L dans le coint
L T
haut-droit. Quant à 7 ils fournissent les éloignements minimum et maximum
qui seront visibles. Ces six paramètres définissent donc une pyramide tronquée qui est
la zone de vision.
Connaissant la position du centre d’une sphère englobante et son rayon, et les
paramètres du frustum, on peut déterminer si une sphère est complètement en dehors
de cette pyramide tronquée. Soit le vecteur pos la position du centre et rayon le
rayon de la sphère. Les deux premières conditions à remplir pour être au moins en par-
tie dans le frustrum sont : pos.z-rayon>=-frustumZ1 et pos.z+rayon<=-
frustumZ2 ; sinon, c’est que l’objet est complètement dans le dos de la caméra,
oubien trop loin hors du z-buffer.
Les conditions sur les bords sont plus pointues et nécessitent un petit dessin (Cf
figure 6).
c’est à dire à
Þ Ã-¹ »Ä¹8¸ Þ L
@ @ -Ã ¹ Å Å »5¹?¸ à L
½8¿ Å
2. ó , la distance entre la sphère englobanteM.I.I.I L gauche Ï.?.II.I à L vaut, par
similarité avec le triangle formé par @ -
à ¹ Å
et le bord
5
» ?
¹ ¸ Þ et @ -
à ¹
du frustum,
Å Ä
» 8
¹ ¸ :
ô Eõ=ö5÷w> øæù¾÷)ú Ú Ü *
L õ=ö5÷wøæù¾÷)ú *
Ú
On a donc
½8¿ Å Þ ½8¿ Å à õ^õ^öÍöÍ÷;÷;øæøæù¾ù¾÷H÷Hú ú Ü * *
ó ô õ^öÍ÷;øNù¾÷)ú Ú Ü *
L õ^öÍ÷;øNù¾÷)ú *
Il faut comparer cette distance ó avec le rayon de la sphère englobante pour savoir
si la sphère est intégralement à gauche ou non du frustum.
Refaire des tests similaires avec les bords droit, haut et bas du frustum.
Pour calculer ces distances, on fera bien de précalculer, à chaque intitialisation du
frustum, les constantes û
@ -à ¹ Å »Ä¹8¸ Þ à L
L õ -à ¹ Å »Ä¹8¸ü 6 ¹ =< ó ' Ã-¹ »5¹?¸ L
û û@ Å
et
T õ -à ¹ Å »5¹?¸ü 6 ¹ =< ó ' Eý L @ Ã-¹ Å »5¹?¸ü 6 ¹ <=ó ' ,
pour le bord gauche, etc...
24
sphère englobante
' »ÄÃ`'
?.
posX (<0)
ó
*
ãã
>° 8½ ¿ Å Þ Þ
Þ ©ÿ ø Ü Ö õ^öÍ÷;øNù¾÷)Ü ú * Ú *
õ=ö5÷wøæù¾÷)ú
frustumX1 (<0)
D Ú
25