0% ont trouvé ce document utile (0 vote)
4 vues25 pages

Pile Matrices Modelview OpenGL

Cet article explique l'utilisation de la matrice MODELVIEW dans OpenGL, en abordant des questions courantes sur le déplacement d'objets et de la caméra. Il présente d'abord des concepts mathématiques de géométrie et d'algèbre linéaire, suivis d'applications pratiques dans OpenGL. Le document vise à aider les programmeurs à mieux comprendre et utiliser la pile de matrices d'OpenGL.

Transféré par

RahanneLore
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
4 vues25 pages

Pile Matrices Modelview OpenGL

Cet article explique l'utilisation de la matrice MODELVIEW dans OpenGL, en abordant des questions courantes sur le déplacement d'objets et de la caméra. Il présente d'abord des concepts mathématiques de géométrie et d'algèbre linéaire, suivis d'applications pratiques dans OpenGL. Le document vise à aider les programmeurs à mieux comprendre et utiliser la pile de matrices d'OpenGL.

Transféré par

RahanneLore
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Utilisation de la matrice M ODELV IEW sous OpenGL

rixed@[Link]

August 27, 2002

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<)

768:9H<)  7>I ' :@AJE76  >I:9  ' <  @A


La définition des coordonnées données ci-dessus signifie  donc ceci
 à  est donnée dans le repère &E   ')(  '+*  'H ,  : par
le déplace-
ment qui conduit
H
' K de
M
' L fois le
NIO / , plus fois le déplacement , etc... où  sont les coordonnées
déplacement
   
de . Les déplacements le long des trois axes 'H( , '-* et 'H, pouvant bien sur être
effectués dans n’importe quel ordre.
      ce  que
Géométriquement, voici
vecteurs du repères  
ça donne, en 2D (j’ai fait exprès de prendre des
 complètement quelconques) :
  U L-PRQ-S    V
  T   
T   

 

L-PRQ-S    
K  

Figure 1: Coordonnées contra-variantes

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

  YX 0 W [Z   Z3VZ 0 W Z3G\^]`_.a


où a est l’angle entre
  et 0 W , et où Z   Z désigne la norme3 de   , etc.
 K W K seulement si la longueur de K  ou de  W est nulle,
On voit  que UX 
ou-bien si
 et W sont perpendiculaires.
Ceci nous amène à un nouveau jeux de coordonnées, appelé coordonnées covari-
antes (Cf figure 2) : on peut en effet repérer la position d’un point grâce aux produits
   le  vecteur position de ce point4 et chacun des vecteurs du repère.
 Dans

&b    , les produits scalaires d’un vecteur par les vecteur   et  
 
scalaires entre

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.

La relation entre les coordonnées covariantes et contra-variantes dépend du produit


scalaire des vecteurs du repère entre eux. Dans le cas d’un repère orthonormé, ces
coordonnées sont égales, ce qui est très pratique.
  0 W dans le repère 
trouver une formule simple pour calculer cX
 
         si  est orthonormé :
On peut

  YX  W [Z   dZ3VZ  W Z3d\^]`_egf ih 


où f est l’angle entre les vecteurs
  et    et h l’angle entre  W et    .
  jX 0 W [Z   Z3VZ 0 W Z3C7\^]`_fkF\^]`_ h  _lnmofpF_lnm h 

Or, Z dZqr \^]`_8f et Z Zo
  r_lnmof sont les coordonnées covariantes de   dans
le repère  et Z
 Z3F\^]`_ h , Z  Z3F_:lnm h les coordonnées covariantes de  dans ce
même repère (on utilise ici l’hypothèse que  est orthonormé). Notons 7ts.vuw et
 W s  W u  ces coordonnées (contra-variantes).
3
norme = longueur
4
vecteur position = vecteur allant de l’origine du repère considéré au point
z2x)y {$| z2x^y }~ z2x€y }
5
ƒNˆ ‚ ƒ„`†‡ ƒgˆ‚ ‰‡ ~ z2x=y } z2x)y {
Si les axes du repère ne sont pas normés, comme c’est la cas dans la figure 2, on représente non pas

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  

Figure 2: Coordonnées co-variantes

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.

Dans le cas général où – est quelconque, on a :


z2xHy {r| z2xHy —˜i™n{2š›~ z2x€y }œ${2žŸ~ z2x;y ŠD|¡™n—Jš¢~ z2x^y }œ$—JžD~ z2x;y Š¢
On peut alors développer le produit scalaire (le produit scalaire est distributif et commutatif) :
z2x=y {| z2xHy —£˜d{2š›~¤—Jš¢~ z2x€y } “ œ${2š¢~—JžD~¤¥D~ z2x^y }| z2xwy Šdœ{2žD~—ŸžŸ~ z2xwy Š “
On retrouve donc la formule précédente si – est orthonormé.

1.3 Changement de repère


Mettons qu’on ait un  ¦ repère &E 
  
  ), ¦ et les vecteurs , et
¦  §
(dorénavant,
on note simplement § le vecteur position
 ).
     §  .
Si les points et ne sont pas alignés on peut former un repère ¦  #
¦ E ¦
 l’on connaisse les coordonnées contra-variantes  # § s 
Supposons¦ que
/ 1  
#§ u  de  (on

 / §  /  s  u  et  s  u  de et
§ donc ), et les coordonnées contra-variantes
note
dans  (notées donc respectivement
¦ ¦ et ¦ ). ¦ /
Cherchons les coordonnées  s  u  de dans  ( ):
¦ ¦  ¦ §
#s    #u 
Par définition :
 §
Explicitons et dans  :

¦ ¦     ¦ §  § 
# 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 :
™«ª š;¬ ª ž ?˜i™g™«ª 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а
™«ª š ¬ ª ž I˜i™«ª 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.

1.4 Notation matricielle


En reprenant les repères  et  # du paragraphe précédent, je note la matrice ³
/Ÿ´v/21
le tableau suivant :

 §
 µ
 Cs Ds
Cu Du¶

qui se lit comme ceci : ·


   ¨  
§ § s4  ¨ § 0
u  
s  u 

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.6 Changement de repère multiple


 

Prenons maintenant 3 repères§ d’un espace à deux dimensions
   Ë Ì :  formé par , ¦- /  et1 1
,  # formé par , et et  # # formé par , et . On a un vecteur
défini dans  # # , on cherche ses coordonnées Ë  / Ì  dans  . ¦- /  1 1
/
Supposons que l’on connaisse Ë  / Ì  / et : on multiplierait par la matrice
formée par les coordonnées de et (i.e. exprimés dans  ) : on passera alors de
$# # à  . Ë Ì
Le cas §
intéressant est celui où on ne connaît et que dans  # , et où on connaît
dans  Ë .  Ì /
en plus et
/
Il faut alors trouver et . Ë  /21 Ì 5 /21
Pour les trouver,   il§ faut évidement multiplier et par la matrice formée
par les vecteurs et exprimés dans  : En notation matricielle, multiplier tous
les vecteurs formant un repère  # # , exprimés Ë dans Ë  # , parÌ la matrice Ì de passage de  #

ËÍ  ÌÍ 
vers , se note comme ceci § §
(en appelant  # s  # -
u  et  #
 §s  # -
u  les coordonnées de
/ 1 et
/ 1 et   s   -u  ,  s  u- les coordonnées de
 / et
/ ):

Ë Í / 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

Figure 3: Changement de repère lorsque les origines ne coïncident pas

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.

1.8 Composition de matrice 4x4


La composition de matrices 4x4 en cas de repères imbriqués se fait sans difficultés.
Quelque soit les matrices, leur taille et leur signification, multiplier successivement un
Ñ est équivalent à multiplier le vecteur parÑ la composées des
vecteur par deux matrices
matrices. Le vecteur de la matrice composée, sera la vecteur de la matrice de
droite, multiplié par la matrice de gauche (tout comme n’importe quel vecteur de la
matrice de droite).

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 ³ Ø Ö Ø ,

3. GL P USH pour sauver le résultat,


á ã´ á
4. GL M ULTIPLIE ³ ØÖ Ø “,
€ á “
¦0
5. Envoi des ,
â´ /
Ø Ö¤Ø v Ÿ/ ´ á ã
6. GL P OP pour récupérer le résultat de ³ À ³ ØÖØ ,
7. GL P USH pour sauvegarder à nouveau ce résultat,
á±ã ´ á%ä
8. GL M ULTIPLIE ³ ØÖØ ,
¦€ á% ä
9. Envoi des ,
â´ /
Ø Ö¤Ø v Ÿ/ ´ á±ã
10. GL P OP pour récupérer le résultat de ³ À ³ ØÖØ ,
á±ã ´ á%å
11. GL M ULTIPLIE ³ ØÖØ ,
¦€ á% å
12. Envoi des .

On peut ainsi facilement gérer les objets organisés hiérarchiquement dans un arbre.

2.2 Structures de données


Pour utiliser la pile M ODELV IEW de cette manière, je vous propose d’imaginer cette
organisation des données. Ces structures seront reprises et employées pour résoudre
les problèmes que l’on se posera dans la suite de ce document.

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();
}
}

2.3.2 Rendre une liste d’objets architecturés


Il s’agit du problème que nous avons traité dans l’introduction de cette section.
Si les objets sont susceptibles d’être relatifs à d’autres objets, on peut toujours les
afficher très simplement. Restriction : Les relations de dépendance entre les objets
doivent former un arbre, et la liste doit contenir ces objets dans l’ordre de parcours en
profondeur de l’arbre (Cf figure 4)
La routine afficheTout deviens :

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

Bonhomme (buste) Vélo (cadre)


Objet 0 Objet 6

Tete Abdomen Bras


Objet 1 Objet 2 Objet 5 Roue avant Roue arrière
Objet 7 Objet 8

Jambe gauche Jambe droite


Objet 3 Objet 4

Figure 4: Parcours en profondeur

popPile();
}
glPushMatrix();
pushPile(objet+o);
glMultMatrixf(objet[o].position.c);
objet[o].affiche();
}
}

Où les fonctions initPile, pushPile, popPile et topPile gèrent une pile


de pointeurs sur objets, qui mémorise les références des objets dont la position dans le
repère de la caméra est au sommet de la pile M ODELV IEW.
Une implémentation possible serait :

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.

2.3.4 Translation d’un objet le long d’un vecteur constant


La translation le long d’un vecteur constant ne pose aucune difficulté. On fera attention
au repère par rapport auquel est défini le vecteur de translation : il faut évidement
exprimer ce vecteur dans le repère par rapport auquel est défini la Ñ position de l’objet
que l’on veut translater avant d’ajouter ce vecteur à la colonne de la position de
l’objet.

2.3.5 Trouver un Vecteur perpendiculaire


Souvent, notamment pour construire un repère comme on le verra dans le paragraphe ¦

suivant, on a besoin de calculer un vecteur perpendiculaire à un vecteur donné .
Une méthode simple consiste ¦ à prendre pour commencer un vecteur dont  ¦ on est
sûr qu’il n’est pas aligné avec , puis à lui retirer sa projection le long de . C’est à
dire : ç •è ' < »'=¹8ͽ8Ã+' ÅHé  ¹ê' é ¹'Hº  ¦ < ¿ Aé ¹'
ç  E  ¦ X ç  ¦ ›
Ce vecteur
ç
sera ainsi perpendiculaire à .
Voici géométriquement ce que cela donne :

17

ë‹ Œ  ‹ Œ ì  ¦
’ ‹ Œì ’ “ 

ç (presque quelconque)

ç ë‹ Œ  ‹ Œ ì  ¦
’ ‹ Œì ’N“ 

Figure 5: Construire un vecteur perpendiculaire



Dans le cas usuel est normé, ce qui facilite encore le calcul.
Une implémentation possible pourrait être :

void perpendicularise(Vecteur *v, Vecteur *w) {


// rend w perpendiculaire à v
double s;
int i;
s = scalaire(w, v); // Diviser ici par scalaire(v, v) si
for (i=0; i<3; i++) // on n’est pas certain que v soit normé.
w->c[i] -= s*v->c[i];
}

2.3.6 Trouver deux vecteurs pour former un repère


Parfois,  à pour les besoins d’un calcul, on a besoin d’un ¦ repère orthonormé dont le
vecteur , par exemple, doit être à aligné avec
 ¦ un vecteur donné.
On commence à par dire que
Þ
perpendiculaire àß
Þ
. On peut ensuite obtenir un vecteur à
 à Þ on doit renormer et .
avec la méthode ci-dessus. Ensuite,
Puis, on obtient ¦ entreç  et . í
avec un produit vectoriel ¦ïî ç 
Le produit í í í í
vectoriel de deux vecteurs et se note . Les coor-
données de ( s , u , È ) s’obtiennent par les formules suivantes :
í ¦ ç È ¦ È  ç u
í s ¦ u 
ç s ¦ s  ç È
í u ¦ È  ¦
È s  ç u ¦ u  ç s
ç sont de donner un vecteur per-

Les propriétés du produit
ç 
vectoriel entre et
pendiculaire à la fois à et à ¦ , et
ç  dont la norme est égale  í à K laPðS surface
 ¦ du triangle
ç  ZJ
dont les côtés sont formés par ¦ et (c’est à dire que Z q
Z £
 Z J
Z £
 Z
_lnmof où f est l’angle entre et ç ). Le produit vectoriel est donc souvent employé
pour trouver des vecteurs perpendiculaires à d’autres vecteurs.

18
Une implémentation possible :

void repere(Vecteur *x, Vecteur *y, Vecteur *z) {


// Z contient le vecteur Z pas forcément normé
Vecteur a = {{ 1,0,0 }};
Vecteur b = {{ 0,1,0 }};
// trouve un x non aligné avec z
if (z->c[1]==0 && z->c[2]==0)
copy(x, &b);
else
copy(x, &a);
perpendicularise(z,x);
renorme(x);
renorme(z);
vectoriel(z, x, y);
}

2.3.7 Déplacement de la caméra avec la souris

Considérons maintenant le déplacement de la caméra. Essayons de déplacer la caméra


uniquement avec la souris, de la façon suivante : Lorsqu’on déplace la souris, le caméra
se tourne vers la droite, la gauche, le haut ou le bas en fonction de la position du
curseur, et lorsqu’on appuis sur un bouton la caméra avance droit devant elle.
Appelons depX et depY les déplacements en X et en Y de la souris. Le déplace-
ment en X induit une rotation de  Ù autour de son axe
¢Û , et le déplacement en Y une
rotation de Ù autour de son axe
D
 Ú .
On a vu que la position de la caméra serait toujours exprimée par rapport au repère
absolu (ce n’est évidement pas une nécessité pour OpenGL que la première matrice
poussée sur la pile soit celle de la caméra ; pour openGL, seule la matrice au sommet
de la pile a une signification). Cette contrainte que nous nous sommes nous même posé
nous empêche de résoudre cette difficulté en intercalant des objets entre la caméra et
le repère absolu comme nous l’avons fait pour faire tourner un objet.
Nous devons donc nous même calculer la nouvelle position de la caméra avant de
la pousser sur la pile ModelView. Il est de toute façon plus pratique de connaître la
position de la caméra par rapport au repère absolu, et poser des positions intermédiaires

æ  Þ
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 Ê

Mais attention : nous ne stoquons pas dans caméra la position absolue de la


caméra, mais la transposée de cette position, c’est à dire la position de l’origine du
repère absolu dans le repère de la caméra.
Le problème se pose une fois de plus pour la partie translation de la position (pour
la partie rotation, il suffit de transposer). Si la caméra tourne sur elle même, la position
de l’origine du repère de la caméra dans le repère absolu ne change pas, mais il est clair
que la position de l’origine du repère absolu dans le repère de la caméra, elle, change.
Nous somme dans un cas où il est plus difficile de stoquer l’inverse de la position de
la caméra que sa position.
Si les vecteurs formant le repère de la caméra tournent d’un angle f autour du
DÚ de Ù dans un sens, alors l’origine ç du repère absolu  tourne d’un angle
vecteur Ñ
f dans l’autre sens. Il faut donc multiplier la position de ce point, présente dans
la quatrième colonne de la matrice représentant le repère Ù , par la matrice donnée à
l’instant avec un angle f opposé.

void tourneX(Matrice *m, double angle) {


double c = cos(angle);
double s = sin(angle);
// les 6 coeficient de la matrice
// qui vont changer
double coef[6];
coef[0] = c*m->c[4] -s*m->c[8];
coef[1] = c*m->c[5] -s*m->c[9];
coef[2] = c*m->c[6] -s*m->c[10];
coef[3] = s*m->c[4] +c*m->c[8];
coef[4] = s*m->c[5] +c*m->c[9];
coef[5] = s*m->c[6] +c*m->c[10];
m->c[4] = coef[0];
m->c[5] = coef[1];
m->c[6] = coef[2];
m->c[8] = coef[3];
m->c[9] = coef[4];
m->c[10] = coef[5];
// maintenant la translation
// (seuls x et y changent)
coef[0] =-c*m->c[13] -s*m->c[14];
coef[1] = s*m->c[13] -c*m->c[14];
m->c[13] = coef[0];

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).

void renormalise(Matrice *m) {


renorme(&m->n.x);
perpendicularise(&m->n.x, &m->n.y);
renorme(&m->n.y);
vectoriel(&m->n.x, &m->n.y, &m->n.z);
}

Ensuite, occupons nous de faire avancer la caméra lorsqu’un bouton de la souris


est pressé.

Pour faire avancer un objet le long d’un de ses axes, on a vu qu’il suffit d’ajouter
un vecteur constant, ici l’axe voulu du repère de l’objet considéré, au vecteur de sa
position. Pour la caméra, dont on ne connait que l’inverse de la position, les choses
sont différentes, et encore plus simple.
› Ü Faire
de l’origine de  . Il suffit donc de retirer à la coordonnées de
Ÿ ñ une
avancer la caméra droit devant consiste à réduire la coordonnée le long de
valeur
proportionnelle à la vitesse du déplacement de la caméra :

void avance(double vitesse) {


[Link].c[14] -= vitesse;
}

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;

Question : où ce trouve le centre dans le repère de la caméra ?


Soit Ý , le repère de l’objet considéré, lui même défini dans Ý * , lui même.I 
dans le repère absolu  , et Ù le repère de la caméra défini dans  . Le vecteur
 '
défini
 »5Ã`'
est donc connu dans ò , . Le calcul à effectuer, en n’utilisant que des matrices connues,
est donc :
â ´¤/ /Ÿ´ á±ã ã ´ á -I.I. á 
'  »5Ã+' â
+I.?
³ Àv³ Àt³ # “ À  '  »ÄÃ`' “
On peut généralement calculer ce centre juste avant l’affichage de l’objet, pour tous
les objets. Dans ce cas, on n’est pas obligé de calculer soit même les compositions de
matrices puisqu’elles sont faites dans M ODELV IEW.
la plusá  simple est de lire la matrice M ODELV IEW et de la
Deux solutions donc :+.I.I
 “
multiplier par le vecteur '  »ÄÃ`' . L’autre méthode consiste à calculer soit meme
toutes les compositions de matrices et à donner les résultats à OpenGL (ne plus faire
alors que des glLoadMatrixf et plus de glPushMatrix, glMultMatrixf,
etc...
En fonction des cas et des implémentations d’OpenGL, le plus rapide peut être soit
l’un soit l’autre.
Nous allons écrire ici la première méthode.
donc dans la fonction afficheTout :

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, &centreOdansC);
/* 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.

2.3.9 Frustum culling d’une sphère englobante


Nous allons utiliser maintenant la sphère englobante d’un objet et les paramètres de
projection pour déterminer simplement si un objet est au moins partielement inclus
dans la zone de vision ou pas (auquel cas il n’est pas nécessaire d’envoyer la géométrie
de l’objet à openGL).
La zone de vision est définie avec glFrustum(x1,y1, x2,y2, z1,z2) en

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).

On a représenté sur ce dessin une vue de haut du repère de la caméra et du bord


ß
 ½8¿ Å Þ  ½8¿ Å  .
gauche du frustum, ainsi que la sphère englobante d’un objet dont le centre se situe en
à
En n’utilisant queà des distances algébriques, ce qui implique d’utiliser @ Ã-¹ Å »5¹?¸ L
au lieu de @ Ã-¹ Å »Ä¹8¸ L pour replacer cette longeur dans le repère de la caméra, on voit
sur ce dessin que :
> Þ
1. , la distance en
englobante, vaut Å8
½ ¿ Þ Þ où Þ est la demi-largeur du frustum pour ce à ,
entre le bord gauche du frustum et le centre de la sphère

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½ ¿ Å Þ Þ

þ bord gauche du frustum

Þ ©ÿ ø Ü Ö õ^öÍ÷;øNù¾÷)Ü ú * Ú *
‹ õ=ö5÷wøæù¾÷)ú

frustumX1 (<0)

Ü posZ (<0)

-frustumZ1 (<0) vers la fin du z-buffer




D Ú

Figure 6: Distance entre une sphère et le frustum

25

Vous aimerez peut-être aussi