Réalité virtuelle appliquée au headtracking et au handtracking

Transcription

Réalité virtuelle appliquée au headtracking et au handtracking
Réalité virtuelle appliquée au headtracking et au
handtracking
Deshayes Romuald
Remerciements
Je tiens à remercier :
Mon directeur de projet, Mr Tom MENS, pour son suivi tout au long de l'année,
ainsi que ses conseils et relectures. Grâce à lui, j'ai également eu l'occasion de participer à une activité de vulgarisation scientique annuelle nommée Printemps des
Sciences. Lors de cette participation, j'ai eu l'opportunité d'y présenter mon projet
aux visiteurs. Cette expérience fut pour moi très enrichissante, aussi bien du point
de vue humain que didactique.
Mon co-directeur, Mr Michaël HOSTE, qui s'est montré d'une grande attention
vis-à-vis de ce projet, et qui m'a apporté de nombreux conseils tout à long de la
réalisation de mon projet.
Mon rapporteur, Mr Hadrien MELOT, pour ses relectures, ses corrections ainsi que
ses conseils.
Toutes les personnes qui ont contribué à ce projet, notamment par l'apport de
corrections lors de leur relecture attentive de ce rapport.
1
Prérequis du lecteur
Pour faciliter la lecture de ce document, le lecteur devra posséder certains prérequis :
Pour comprendre les sections traitant de l'implémentation, des connaissances en
programmation (Orienté-objet) sont nécessaires. De plus, une bonne connaissance
du langage OpenGL facilitera leur compréhension
Les concepts théoriques sous-jacents à notre problème demandent des connaissances
de trigonométrie de base et de physique de base (un niveau secondaire est susant).
2
Table des matières
1 Introduction
6
1.1
Headtracking
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7
1.2
Handtracking
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7
1.3
Vue en trois dimensions
1.4
1.5
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
Etat de l'art . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8
1.4.1
Johnny Chung Lee
8
1.4.2
Chris Harrison
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9
1.4.3
TrackIR
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10
1.4.4
Playstation Eye . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11
1.4.5
Projet Natal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Contributions
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2 Approche du problème
2.1
2.2
8
12
13
15
Choix technologiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
15
2.1.1
15
Lunettes infrarouges
. . . . . . . . . . . . . . . . . . . . . . . . . .
2.1.1.1
Avantages et inconvénients . . . . . . . . . . . . . . . . . .
15
2.1.1.2
Montage et fonctionnement
16
. . . . . . . . . . . . . . . . .
2.1.2
Gant infrarouge . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
17
2.1.3
Wiimote . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
21
2.1.3.1
Qu'est ce qu'une Wiimote ?
. . . . . . . . . . . . . . . . .
21
2.1.3.2
Spécications techniques . . . . . . . . . . . . . . . . . . .
21
2.1.4
Utilisation d'une Webcam
. . . . . . . . . . . . . . . . . . . . . . .
2.1.5
Headtracking grâce à la stéréoscopie
2.1.6
Amélioration des lunettes
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
23
25
26
2.1.6.1
Utilisation de trois sources infrarouges
. . . . . . . . . . .
26
2.1.6.2
Polarisation . . . . . . . . . . . . . . . . . . . . . . . . . .
27
2.1.6.3
Obturation
. . . . . . . . . . . . . . . . . . . . . . . . . .
29
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
30
Choix informatiques
2.2.1
Besoins du langage
. . . . . . . . . . . . . . . . . . . . . . . . . . .
30
2.2.2
Choix du langage et argumentation . . . . . . . . . . . . . . . . . .
31
2.2.2.1
Ecacité de la librairie . . . . . . . . . . . . . . . . . . . .
32
2.2.2.2
Rapidité du langage
. . . . . . . . . . . . . . . . . . . . .
32
2.2.2.3
Portabilité . . . . . . . . . . . . . . . . . . . . . . . . . . .
33
2.2.2.4
Orienté Objet . . . . . . . . . . . . . . . . . . . . . . . . .
33
2.2.2.5
API 3D
34
2.2.2.6
Perfectionnement du langage
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
3
. . . . . . . . . . . . . . . .
34
2.2.3
2.3
Idées alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Concepts théoriques
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
35
36
2.3.1
Concernant le headtracking
. . . . . . . . . . . . . . . . . . . . . .
36
2.3.2
Concernant le handtracking
. . . . . . . . . . . . . . . . . . . . . .
42
2.3.3
Concernant l'eet fenêtre . . . . . . . . . . . . . . . . . . . . . . . .
42
2.3.4
Rappels de mécanique
44
. . . . . . . . . . . . . . . . . . . . . . . . .
3 Implémentation du module
46
3.1
Vue d'ensemble
3.2
Utilisation de la librairie
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
47
3.3
Module headtracking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
48
3.5
HeadtrackedCamera
. . . . . . . . . . . . . . . . . . . . . . . . . .
48
3.3.2
Camera
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
49
3.3.3
CameraHandler . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
50
3.3.4
WiimoteHandler
51
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3.4.1
WiimoteHeadAndHand
. . . . . . . . . . . . . . . . . . .
52
3.3.4.2
WiimoteHand . . . . . . . . . . . . . . . . . . . . . . . . .
52
3.3.4.3
Utilité du WiimoteHandler
. . . . . . . . . . . . . . . . .
52
3DMath
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
53
Fonctionnalités étendues
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
53
. . . . . . . . . . . . . . . . . . . . . . . . . . .
53
3.4.1
Gestion des angles
3.4.2
Gestion des tremblements
. . . . . . . . . . . . . . . . . . . . . . .
3.4.2.1
ShakeDetector
3.4.2.2
Interpolation des précédents évènements
. . . . . . . . . . . . . . . . . . . . . . . .
54
54
. . . . . . . . . .
55
3.4.3
Gestion de plusieurs Wiimotes . . . . . . . . . . . . . . . . . . . . .
56
3.4.4
3D stéréoscopique . . . . . . . . . . . . . . . . . . . . . . . . . . . .
57
3.4.5
WindowedView
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
59
Module Handtracking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
59
3.5.1
HandTracking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
59
3.5.2
HandHandler
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
60
3.5.3
HandMovementsDetector . . . . . . . . . . . . . . . . . . . . . . . .
61
4 Applications
4.1
46
3.3.1
3.3.5
3.4
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
63
Jeu de Pong . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
63
4.1.1
Trajectoire de la balle
. . . . . . . . . . . . . . . . . . . . . . . . .
63
4.1.2
Gestion des collisions . . . . . . . . . . . . . . . . . . . . . . . . . .
63
4.1.3
Règles du jeu et Calcul du score . . . . . . . . . . . . . . . . . . . .
64
4.1.3.1
Règles du jeu . . . . . . . . . . . . . . . . . . . . . . . . .
65
4.1.3.2
Calcul du score . . . . . . . . . . . . . . . . . . . . . . . .
65
4.1.4
Intelligence articielle . . . . . . . . . . . . . . . . . . . . . . . . . .
66
4.1.5
Handtracking
67
4.1.6
Déroulement d'une partie
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
67
4.2
Targets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
67
4.3
Eet fenêtre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
68
4.4
Formes 3D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
69
4.5
Explorateur de Fichiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
70
4.6
Visualisateur de photos . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
71
4
4.7
Interface graphique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
71
4.7.1
Architecture globale
72
4.7.2
Communication avec les applications
4.7.3
Intégration des scènes
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
72
. . . . . . . . . . . . . . . . . . . . . . . . .
73
5 Utilisation du module
5.1
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
74
5.1.1
Réaliser une application
Installation des librairies . . . . . . . . . . . . . . . . . . . . . . . .
74
5.1.2
Headtracking
75
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.1.2.1
Utiliser la Camera du module . . . . . . . . . . . . . . . .
75
5.1.2.2
Détection des angles
76
5.1.2.3
Lier la caméra au module
. . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
76
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
77
Interface utilisateur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
78
5.2.1
Lancement de l'application . . . . . . . . . . . . . . . . . . . . . . .
78
5.2.2
Région Ouest
78
5.1.3
5.2
74
Handtracking
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Onglet
5.2.2.2
Onglet
Mesures .
Wiimote
. . . . . . . . . . . . . . . . . . . . . . .
79
. . . . . . . . . . . . . . . . . . . . . . .
81
Région Est . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
83
5.2.3.1
Onglet
. . . . . . . . . . . . . . . . . . . . . .
83
5.2.3.2
Onglet
. . . . . . . . . . . . . . . . . . . . . .
90
5.2.4
Région Nord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
91
5.2.5
Région Sud
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
92
5.2.6
Région Centrale . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
92
5.2.3
5.2.2.1
Paramètres
Contrôles .
6 Conclusion
94
Bibliographie
95
5
Chapitre 1
Introduction
Le monde dans lequel nous évoluons est de plus en plus informatisé. Si bien qu'aujourd'hui tout le monde possède un ordinateur à la maison. Depuis plus de cinquante ans,
l'informatique n'a cessé d'évoluer, de l'arrivée de la première interface utilisateur, il y a
plusieurs dizaines d'années, à l'apparition des premiers ordinateurs de salon. Cependant,
une chose est restée gée durant toutes ces décennies, comme si personne n'avait jamais
voulu qu'elle évolue. Il s'agit de la façon d'intéragir avec notre ordinateur.
Certes les claviers ont évolué, leur frappe est devenue plus agréable, les touches mieux
placées, etc. Avec l'arrivée des ordinateurs de salon, nous avons même vu débarquer des
télécommandes permettant de piloter l'ordinateur. Mais au fond, cela n'a pas beaucoup
changé. Depuis cinquante ans, utiliser un ordinateur signie appuyer sur des boutons, et
regarder son écran signie regarder une image xe et monotone.
Qui n'a jamais revé de changer la façon d'utiliser sa machine, pour qu'elle devienne
réactive au moindre de vos mouvements ou adapte l'image achée en fonction de votre
position par rapport à l'écran ? Autant d'idées qui semblaient folles il y a encore quelques
années. Mais aujourd'hui, avec la puissance de calcul grandissante des machines, et les
prouesses technologiques du multimédia, la ction devient une réalité. Il est aujourd'hui
possible de transformer son ordinateur en une véritable station réactive aux mouvements,
grâce à l'arrivée de technologies innovantes.
Parmi ces technologies, le
headtracking
permet d'adapter une image achée à l'écran
en fonction de la position que l'utilisateur prend par rapport à ce dernier.
Dans ce document, nous expliquerons comment mettre en place une solution de headtracking ecace et portable. Nous présenterons les diérentes contributions ayant déjà été
apportées par les communautés scientiques et industrielles au problème de headtracking.
Lors de cet état de l'art, nous introduirons les méthodes utilisées pour mettre en place
le système de headtracking. Nous consacrerons la suite de ce document à la description
de notre contribution personnelle au problème de headtracking. Cette description traitera
de l'approche utilisée pour mettre au point notre propre solution de headtracking. Nous
discuterons notamment des aspects techniques concernant le matériel utilisé, ainsi que
des aspects théoriques et informatiques concernant la détection de la position de la tête
en temps réel. Enn, nous présenterons l'application développée, sous la forme d'un guide
de l'utilisateur souhaitant utiliser l'application graphique développée et d'un guide du
développeur désireux de créer une application de headtracking utilisant notre application.
6
1.1 Headtracking
An de permettre au lecteur une bonne compréhension du sujet traité dans ce document, il nous est nécessaire de dénir le terme de
headtracking.
Le headtracking est un concept appliqué dans le cadre de la réalité virtuelle
qui permet de détecter la position de la tête d'une personne dans l'espace.
Cette position réelle pourra ensuite être analysée et travaillée de façon à être
intégrée dans un espace virtuel au sein d'un monde en 3 dimensions. De cette
façon, chaque mouvement de la tête de l'utilisateur entraînera une modication
en temps réel de l'achage virtuel, comme si l'utilisateur regardait à travers
une fênêtre. Le but recherché par cette application est de rendre l'utilisation
d'un écran plus intéractive, en donnant l'impression que celui-ci n'existe plus.
Ce concept possède de nombreuses applications. Dans le monde du jeu vidéo, il serait
par exemple possible d'utiliser les mouvements de la tête comme un contrôleur. Dans un
jeu de moto, nous pourrions imaginer que le fait de pencher la tête vers la gauche/droite
ferait basculer la moto du même côté.
Les applications ne se limitent pas au monde du jeu vidéo. Si nous nous plaçons dans un
contexte industriel, cette application peut être utilisée par un architecte pour visualiser
des plans en trois dimensions. Le fait de se déplacer autour de l'écran permettrait au
créateur de visualiser son projet sous un angle diérent, dépendant directement de sa
position par rapport à l'écran.
Dans un contexte plus général, la position de la tête d'un utilisateur en face de son écran
peut être utilisée pour visualiser des images en trois dimensions. La modication de l'angle
de vue permettrait de visualiser des objets qui sont cachés par des sujets étant situés au
premier plan de l'image.
Une autre idée intéressante serait de naviguer dans une interface grâce aux mouvements
de la tête, qui déclencheraient des commandes en fonction du type de mouvement eectué.
Cette dernière possibilité sort du cadre de headtracking au sens strict du terme (car nous
n'allons pas nécessairement réadapter l'image en fonction des mouvements de la tête, mais
plutôt nous servir de ses mouvements pour contrôler une interface), mais reste toutefois
une application intéressante.
1.2 Handtracking
Dans la suite de cette étude, nous serons amenés à parler de
handtracking. Ce concept
n'est pas très diérent de celui que nous avons expliqué à la section 1.1. La principale
diérence, comme son nom nous le laisse deviner, est qu'au lieu de détecter la position
du visage, nous allons être intéressés dans la détection des mains.
Parmi les applications possibles, citons la possibilité de déplacer une raquette de tennis,
dans un jeu vidéo. Une vision bien plus vaste de cette notion est de piloter une interface
avec les mains. Un mouvement rapide de gauche à droite de la main permettrait par
exemple de changer la chaîne de la télévision, alors qu'un mouvement vers le haut aurait
pour eet d'augmenter le volume.
L'étude du handtracking est un sujet que nous n'aborderons que brièvement dans ce
document. Toutefois, ce concept est tout autant digne d'intérêt que son paronyme (cf
section 1.1).
7
1.3 Vue en trois dimensions
Avant de commencer, il est nécessaire d'introduire une dernière notion importante. Il
s'agit de la vision en trois dimensions. Formellement, les êtres humains ont la capacité
de percevoir les objets qui les entourent avec un certain relief. La position des deux yeux
chez l'être humain est telle que tout ce qui se trouve devant lui est vu à la fois par l'oeil
gauche et l'oeil droit. La faible distance (environ 6,5 cm) qui sépare nos yeux implique
que deux images légèrement diérentes sont traitées par notre cerveau. C'est l'association
de ces deux images qui est à l'origine du relief que nous sommes capable de discerner.
Informatiquement parlant, la vision en trois dimensions ne s'obtient pas de la même
façon que dans la réalité. Pour perçevoir du relief à travers un écran, car c'est ce à quoi
nous souhaitons arriver, il est nécessaire de donner au cerveau l'illusion qu'il peut voir en
trois dimensions. Pour ce faire, un écran doit générer de façon simultanée deux images.
Chacune de ces images doit ensuite être restituée à l'un de nos yeux.
Pour arriver à ce résultat, plusieurs procédés existent. Les trois principaux sont l'anaglyphe, la polarisation et l'obturation. Nous expliciterons en détail de quelle façon des
images en trois dimensions peuvent être obtenues grâce à ces technologies à la section
2.1.1.
1.4 Etat de l'art
Le concept de headtracking n'est pas nouveau ; en eet, un certain nombre de recherches en la matière ont déjà été eectuées. Toutefois, ces diérentes recherches n'ont
pas toutes utilisé les mêmes concepts. La technologie de headtracking peut-être vue d'une
multitude de façons diérentes. Dans cette section, nous dégagerons quelques unes des
diérentes méthodes qui ont déjà été présentées.
1.4.1 Johnny Chung Lee
La première méthode présentée est celle de Johnny Chung Lee, un jeune chercheur de
l'Université Carnegie Mellon. L'intérêt de sa méthode [15] repose sur le fait qu'il utilise du
matériel répandu et bon marché pour mener à bien son expérience. Les seuls outils utilisés
sont une télécommande de la célèbre console de jeu Wii (la
Wiimote 1 ), ainsi qu'une paire
de lunettes munie de leds infrarouges, les leds étant placées de chaque coté des lunettes, sur
les branches de celles-ci et émettant dans la direction regardée par leur porteur. Pour que
la position de son visage soit détectée, l'utilisateur doit simplement porter ces lunettes.
Les deux infrarouges émis seront alors perçus par la caméra infrarouge située dans la
Wiimote. Il ne reste plus qu'à eectuer une série de calculs en fonction de ces points et
l'ordinateur connaît à présent la position relative de l'utilisateur par rapport à la caméra
infrarouge.
Les mouvements permis par l'application de Johnny Lee sont les déplacements :
1. Latéraux
2. Verticaux (de haut en bas)
1. Nous parlerons plus en détails de la Wiimote à la section 2.1.3
8
Figure 1.1: Application développée par Johnny Lee
3. D'avant en arrière.
L'application utilise deux leds infrarouges pour détecter la position du visage. Le calcul
de la distance entre la Wiimote et l'écran est eectué en se basant sur la distance entre
ces deux leds. Par conséquent, si l'utilisateur pivote la tête vers la gauche ou la droite,
la distance entre les sources infrarouges diminue. En eet, lors de leur projection sur le
capteur, nous passons de trois dimensions à deux dimensions, et c'est l'information de
profondeur qui est perdue. Cette diminution de distance va provoquer une erreur dans
le calcul de la distance entre la caméra et l'utilisateur, qui semblera plus éloigné qu'en
réalité sur le rendu de la scène.
De façon générale, l'utilisation du headtracking peut tout à fait se passer de l'utilisation
du mouvement
pivot de la tête, qui entraîne une certaine gêne à l'utilisation.
Pour montrer l'eet apporté par le headtracking, J. Lee a développé en C# (un langage de programmation orienté objet) et avec la librairie Direct3D une petite application
graphique. Cette application ache une série de petites cibles dans un espace en trois
dimensions. Ces cibles sont contenues à l'intérieur d'une grille (cf gure 1.1). Ce procédé
transforme l'écran en environnement virtuel, lequel réagit en temps réel aux mouvements
eectués par la tête de l'utilisateur. Au nal, l'écran apparaît comme une fenêtre et donne
une forte impression de profondeur à la scène.
1.4.2 Chris Harrison
Chris Harrison [11] est un jeune chercheur issu de la même université que J. Lee.
Plutôt que d'utiliser la Wiimote pour détecter des infrarouges, il a préféré utiliser une
simple webcam pour détecter la position de la tête d'un utilisateur. De cette façon, son
application cible un public beaucoup plus vaste. Il a développé l'application en utilisant
OpenCV [17], qui est une librairie d'algorithmes pour traiter la vision en temps réel.
Grâce à cette librairie, il a pu construire une solution de headtracking en temps réel,
dont les performances sont qualiées de étonnamment bonnes par son auteur. En effet, sa solution permet de détecter et modier la position virtuelle du personnage à 25fps
(Frame Per Second), une vitesse susante pour une interaction en temps réel. Malheureusement, ce traitement visuel nécessite toute la puissance d'une machine récente équipée
9
de plusieurs coeurs pour fonctionner de manière optimale. En eet, la vidéo
2
de démons-
tration réalisée par le jeune chercheur américain montre une certaine faiblesse au niveau
de la uidité de l'application.
Le succès et la simplicité de la méthode de C. Harrisson ont poussé une série de
développeurs à créer leur propre application de headtracking en utilisant une webcam.
De nombreuses videos sont disponibles sur internet, en utilisant par exemple les mots-clés
headtracking webcam sur Youtube [12].
1.4.3 TrackIR
TrackIR [22] est la première et plus ancienne version développée par Microsoft. Cette
solution est à ce jour une des plus développées en matière de headtracking, mais c'est
aussi une des plus onéreuse. Elle se compose de deux parties : comme le montre la gure
1.2,la première partie est un petit boîtier à placer au-dessus de l'écran (comme une simple
webcam) contenant une caméra infrarouge ainsi qu'une série de leds infrarouges émettant
dans la direction de l'utilisateur. La seconde partie est une petite tige en plastique contenant trois réecteurs à infrarouges à placer sur l'utilisateur. Ces réécteurs sont capables
de capter la lumière infrarouge émise par la caméra et de la renvoyer à la façon d'un catadioptre. Les trois points ainsi captés peuvent donc être récupérés via la caméra infrarouge
et utilisés pour calculer la position du joueur par rapport à la caméra. L'application de ce
procédé touche uniquement le monde du jeu vidéo. C'est d'ailleurs dans cette optique que
Track IR a développé sa propre solution de headtracking. A ce jour, plus d'une centaine
de jeux vidéos sont compatibles avec ce matériel. La plupart d'entre eux sont des jeux de
simulation, touchant des domaines tels que la course automobile ou à moto ainsi que la
simulation ou les combats aériens.
À ce jour, la caméra possède un capteur de 300.000 pixels (640x480), avec un taux de
rafraîchissement à 120Hz (cela signie que l'image peut être réadaptée jusqu'à 120 fois
par seconde) et un angle de vue de 52°C. La caméra est capable de détecter 6 types de
mouvements diérents :
1. déplacement gauche-droite,
2. déplacement haut-bas,
3. déplacement avant-arrière,
4. hochement de tête vertical
5. hochement de tête horizontal
6. hochement de tête latéral
Pour chaque type de mouvement, on parle de
degré de liberté.
Chacune de ces actions
peut donc être interpretée et reproduite par un personnage virtuel dans un jeu video.
La solution de Microsoft est déjà commercialisée depuis 2001. À l'époque, seuls deux
degrés de liberté étaient présents et le capteur ne possédait que 60.000 pixels (300x200),
avec un angle de vision de 33°C.
2. voir http ://www.youtube.com/watch ?v=1ZMZK3heklY&feature=player_embedded
10
Figure 1.2: Dispositif de capture de Track IR
Figure 1.3: Le nouveau Playstation Eye [5]
1.4.4 Playstation Eye
Le Playstation Eye [9] (voir gure 1.3) est la nouvelle génération de caméra destinée
à remplacer l'ancienne Eye Toy [21] de Sony. Avec un capteur plus puissant et un taux
de rafraîchissement plus important, la nouvelle caméra peut à présent mieux se prêter
à des activités en temps réel telles que le headtracking. Du côté de Sony, rien d'ociel !
Cependant, un jeune programmeur de la marque japonaise a déjà montré les possibilités
de la petite caméra lors de la GDC (Game Developers Conference) de 2009 [10].Il s'agit
de Thomas Miller. Tout comme Chris Harrison, Thomas Miller n'a utilisé que la simple
caméra, ainsi que des algorithmes de détection de visage pour détecter la position de
la tête de l'utilisateur. Le code source de son application a été publié an de donner la
possibilité aux développeurs Sony de se lancer dans des applications de headtracking.
Bien que rien ne soit encore ociellement lancé, cette démonstration laisse entrevoir
de nombreuses possibilités de réalité virtuelle chez Sony [28].
11
Figure 1.4: Le contrôleur utilisé par le projet natal
1.4.5 Projet Natal
La dernière technologie dont nous parlerons se nomme Projet Natal. D'un point de
vue technique, c'est de loin la plus avancée qui soit. Les possibilités oertes par ce nouveau
contrôleur sont énormes.
Le projet natal est un ensemble de technologies qui permettent de jouer avec la console
de jeu Xbox 360 sans utiliser de manette. A la place, le joueur doit utiliser son corps pour
interagir avec les jeux vidéos. Par exemple pour conduire une voiture dans un jeu de
course, les mains du joueur doivent être placées en avant pour tenir un volant imaginaire.
Il sut alors de braquer pour faire tourner la voiture.
Pour arriver à ce résultat, il est nécessaire de posséder un capteur qui est chargé
de détecter les mouvements du joueur. Comme nous le montre la gure 1.4, il s'agit
d'une petite barre horizontale d'une vingtaine de centimètres. Celle-ci contient tout un
concentré de technologies. Le capteur est équipé d'une caméra RGB, d'un détecteur de
profondeur, d'une série de microphones et d'un processeur dédié, capable de traiter toutes
les informations reçues par les diérents capteurs. La partie headtracking est assurée par le
détecteur de profondeur qui est en réalité un projecteur infrarouge combiné à un récepteur
permettant de détecter la position de l'utilisateur (voir [18]).
Au travers de cette interface, il est alors possible d'interagir directement dans une
application multimédia (telle qu'un jeu video par exemple) sans avoir recours à aucun
contrôleur physique sur l'utilisateur. Aucune led infrarouge ni paire de lunettes ne sont
donc nécessaires. Le contrôle d'un personnage, la navigation dans un menu, dans un jeu
video, etc, autant de choses qui pourront être entièrement contrôlées par le déplacement
de l'utilisateur, de son visage, et/ou via des commandes vocales.
Projet Natal permet d'utiliser l'entièreté du corps pour contrôler un personnage ou
interagir avec la console, avec une latence à l'utilisation d'une dizaine de millisecondes
pour reconnaître un mouvement. Cette latence est susamment faible pour permettre
d'interagir en temps réel et de façon uide avec la console de jeu. De plus, comme le
dispositif de capture possède son propre processeur, les ressources de la console restent en
majeure partie disponibles pour gérer l'achage des jeux.
Lorsqu'on parle du projet natal, il s'agit donc d'une interface complète pour utiliser
la Xbox 360. La partie headtracking n'est donc qu'une des multiples facettes du nouveau
contrôleur.
12
1.5 Contributions
Le but de ce travail est de créer une solution de headtracking, foncionnant à l'aide
de la Wiimote et de leds infrarouges posées sur des lunettes à anaglyphes, les recherches
eectuées par le chercheur Johnny Lee (cf section 1.4.1) ayant servi de base pour la
réalisation de notre application.
Ce travail consiste en une généralisation et une extension des recherches apportées
par Johnny Lee, qui avaient pour but de montrer à la communauté des développeurs la
possibilité d'utiliser la Wiimote comme un dispositif de détection pouvant servir de base
à des applications de réalité virtuelle.
D'un point de vue généralisation, un module réutilisable a été développé, sous la
forme d'une librairie en Java. Ce module contient toutes les fonctionnalités nécessaires à
la réalisation d'une application de headtracking . Principalement, il se charge du calcul
de la position de l'utilisateur par rapport au dispositif de capture, qui est par défaut une
Wiimote, mais qui peut être remplacée par d'autres dispositifs, à condition que ceux-ci
possèdent une interface permettant de récupérer les coordonnées de deux points situés
près de yeux de l'utilisateur.
Une fois la position calculée, elle peut être intégrée dans n'importe quelle type de scène
virtuelle en trois dimensions. Plusieurs scènes intégrant le module de headtracking ont
été développées, de cette façon nous disposons de plusieurs exemples d'applications qui
utilisent le module.
En termes d'extension, plusieurs fonctionnalités ont été ajoutées an de proposer une
série d'outils pouvant être utilisés de façon complémentaire au headtracking. Ces fonctionnalités sont :
Ajout d'un degré de liberté supplémentaire permettant à l'application de détecter
le roulis de la tête. Le fait de pencher la tête vers la gauche ou la droite entraîne
une adaptation de l'image en temps réel donnant ainsi l'impression d'être sur une
moto enchaînant des virages serrés.
Gestion des tremblements : lorsque l'utilisateur s'éloigne du dispositif de capture,
le calcul de la distance devient plus compliqué. Comme nous l'avons précédemment
expliqué à la section 1.4.1, le calcul de la distance utilisateur/caméra se base sur
la distance perçue sur le capteur entre les deux leds infrarouges présentes sur les
lunettes portées par l'utilisateur. A une distance supérieure à environ deux mètres,
la distance en pixels entre les deux sources devient très faible, et une variation d'un
seul pixel sur le capteur est perçu comme un mouvement de plus de dix centimètres.
Les légers mouvements de notre visage ont alors tendance à faire trembler l'image
sur la scène virtuelle. Pour limiter ce phénomène, nous avons ajouté la possibilité
de limiter ces tremblements grâce à un lissage des mouvements sur la profondeur.
Plus de détails seront apportés à la section 3.4.2.
Gestion des angles : an de mieux contrôler le headtracking, un outil de détection
des angles a été mis en place. Il permet d'avertir l'utilisateur qu'il est sur le point de
quitter le champ de vision du dispositif de capture. Pour ce faire, lorsque l'utilisateur
s'approche du bord du capteur, le coté de l'écran correspondant au bord du capteur
approché devient rouge. A mesure que les sources infrarouges se rapprochent du
13
bord du capteur, le rouge devient de plus en plus vif. Cet outil permet à l'utilisateur
d'éviter de quitter le champ de vision de la caméra lorsqu'il utilise le module de
headtracking
Vision stéréoscopique : la technique de stéréoscopie consiste à acher deux images
légèrement décalées d'une même scène, où chaque image a été passée à un ltre de
couleur (typiquement rouge et bleu). Pour que chaque oeil puisse voir l'image qui
lui est destinée, les lunettes seront équipées de ltres de couleur. C'est ce que nous
appelons communément des lunettes à anaglyphes.
En plus de l'immersion apportée par le headtracking, il est possible de visionner la
plupart des scènes avec un eet de relief, grâce à l'utilisation de lunettes à anaglyphes
et la génération en temps réel d'images stéréoscopiques. L'impression de réalité est
alors encore plus importante.
l'angle de vision de la Wiimote étant assez faible (environ 45 degrés), il serait assez intéressant de pouvoir placer deux Wiimotes a côté l'une de l'autre an d'élargir le champ de
vision. Celui-ci peut être étendu horizontalement, ou encore verticalement, en fonction des
besoins de l'application. De cette façon l'utilisateur disposerait d'une plus grande liberté
de mouvements pour interagir avec une application. Cette dernière fonctionnalité n'est
pas encore intégrée au module, mais son ajout constitue une extension intéressante au
projet.
Pour la réalisation de ce travail, nous utilisons une Wiimote car elle possède une
interface de capture très intéressante : elle est équipée d'une caméra infrarouge d'une
qualité plus que susante pour nous permettre de détecter des sources infrarouges à
plusieurs mètres de distance et ce, avec une grande réactivité. En eet, le capteur possède
un taux de rafraîchissement susant pour recalculer la position d'un visage 100 fois par
seconde, c'est-à-dire bien plus rapidement que la vitesse de l'oeil humain (L'oeil humain
est capable de distinguer environ 25 images par seconde). De plus, la qualité du capteur
permet de détecter la position de la tête avec une grande précision. En eet, puisque le
capteur de la Wiimote est destiné à ne capter que les sources infrarouges, il est possible
3
de lui assigner une résolution de plus de 700.000 pixels (1024x768 exactement) à 100Hz .
Puisque la Wiimote possède une caméra infrarouge, de simples leds infrarouges seront
parfaitement utilisables. L'avantage de leds est qu'elles sont très discrètes, peu gourmandes
en électricité et peu onéreuses. Il sut donc d'équiper une simple paire de lunettes avec
deux leds infrarouges pour pouvoir détecter la position d'un utilisateur dans l'espace.
3. Chaque point infrarouge peut être stocké sur 2 x 10bits (0-1023). Acher 4 points à 100Hz requiert
donc un taux de transfert de moins de 8Kbps pour la position des points. Les détails techniques concernant
les Wiimote sont disponible au point 2.1.3. Le bluetooth peut transférer jusqu'a 720Kbps.
14
Chapitre 2
Approche du problème
2.1 Choix technologiques
Pour mettre en place un système de headtracking ecace, il est nécessaire d'utiliser du
matériel de détection, permettant de situer la tête de l'utilisateur dans l'espace. Comme
détaillé à la section 1.4, plusieurs solutions existent pour permettre la mise en oeuvre du
headtracking.
Dans cette section, nous détaillerons le matériel physique qui a été utilisé pour la réalisation de notre application. Ces détails seront utiles au lecteur, an de permettre une
bonne compréhension de la façon dont les concepts théoriques pourront être appliqués sur
les outils dont nous disposons.
Nous proterons également de cette section pour discuter des détails techniques concernant d'autres solutions existantes. Ces solutions concernent aussi bien la mise en place
d'un système de headtracking diérent que l'ajout de fonctionnalités supplémentaires.
Nous verrons par exemple comment il est possible d'utiliser une webcam ou une caméra
stéréoscopique pour détecter la position de la tête dans l'espace, ou encore, comment
détecter les rotations de la tête en utilisant plus de deux sources infrarouges.
2.1.1 Lunettes infrarouges
Dans cette section, nous expliquerons l'utilité des lunettes infrarouges dans notre système de headtracking. Nous y détaillerons également leur mise en place et verrons comment il est possible de les utiliser pour détecter la position de la tête.
2.1.1.1 Avantages et inconvénients
Le fonctionnement de notre solution est principalement basé sur un équipement : les
lunettes. Elles sont le point de repère de la caméra, et permettront de localiser avec précision le visage de celui qui les porte. Utiliser des lunettes pour notre modèle de headtracking
peut sembler un inconvénient, cependant, leur présence est aussi intéressante :
L'utilisation de lunettes peut être vue comme un inconvénient, car contrairement
à certaines méthodes présentées dans la section 1.4, il est
nécessaire
d'être équipé
de lunettes pour obtenir une solution ecace de headtracking. Rappelons-nous du
15
projet traité au point 1.4.5, où aucune paire de lunettes n'est nécessaire. Cependant, une telle solution à un coût : une caméra RGB, un capteur infrarouge, une
série de micros et un processeur dédié au traitement des informations en temps réel.
Cependant, l'utilisation de lunettes ne nuit pas au caractère académique de ce projet. Ajoutons qu'une solution alternative utilisant d'autres dispositifs de détection
pourrait être adaptée sur l'application développée, sans pour autant entraîner de
modications dans celle-ci.
La présence de lunettes infrarouges est intéressante, car elle nous permet de traiter
uniquement
les infrarouges, entraînant alors un poids très réduit de l'information à
véhiculer. Alors qu'une image complète en RGB peut peser plusieurs mégaoctects,
l'information contenant uniquement les sources infrarouge ne pèse que quelques
octets. En eet, seules les coordonnées de chaque point sur le capteur infrarouge
doivent être stockées et envoyées à l'application, alors qu'une solution classique demande d'envoyer tous les points perçus par le capteur à l'application.
En ne traitant que les sources infrarouges, le procédé est également beaucoup plus
rapide et nous pouvons traiter une centaine d'images chaque seconde, sans utiliser
un processeur dédié pour le traitement de l'information. Cela apporte donc un gain
de performances mais aussi un coût minimal.
Notons que l'utilisation de sources infrarouges requiert la présence d'un ltre infrarouge qu'il faut placer devant le capteur de la caméra servant de dispositif de capture.
En second lieu, la présence de lunettes nous sera protable pour l'ajout de la stéréoscopie. En eet, il est nécessaire de porter des lunettes anaglyphes
1
pour proter
de l'application en 3D stéréoscopique.
2.1.1.2 Montage et fonctionnement
Nos lunettes sont le point de repère de la caméra, qui se base sur la position dans
l'espace de ces dernières pour déterminer la position de l'utilisateur.
Détaillons la composition de notre paire de lunettes :
Pour la réalisation du montage, n'importe quelle paire de lunettes convient. Nous
pouvons par exemple utiliser des lunettes de soleil auxquelles les verres ont été
retirés, ou encore une paire de lunettes classique. Finalement, tout ce dont nous
avons besoin est une monture où nous pourrons xer les leds infrarouges ainsi que
les ltres de couleurs.
Il faut ensuite se munir de deux leds infrarouges, an d'en xer une sur chaque
branche de la paire de lunettes (comme sur la gure 2.1), de façon à ce qu'elles
émettent dans la direction regardée par leur porteur. Nous pouvons par exemple
utiliser deux leds de type
LD242-3.
Ce type de led est représenté sur la gure 2.2.
2
Pour l'alimenter, une tension inférieure à 1.5V doit lui être appliquée à 100mA .
Nous avons donc utilisé une simple pile LR6 rechargeable (les piles crayon utilisées
dans tous les types d'appareils électriques, leur tension est de 1.5V pour les batteries
non-rechargeables et 1.2V pour les rechargeables) pour alimenter nos deux leds. Pour
1. Les lunettes anaglyphes possédent un ltre rouge pour l'oeil gauche et cyan (vert et bleu) pour
l'oeil droit.
2. Pour plus de renseignement consulter les datasheet. Vous pouvez consulter les datasheet via le
moteur de recherche http://www.alldatasheet.com/. REFERENCES
16
Figure 2.2: Une led de type ld242-3
que la tension délivrée soit la même sur chaque led, nous avons eectué le montage
en parallèle.
Figure 2.1: Lunettes équipées de leds infrarouges. Source:http://www.nguyot.fr/img/
/divers/combowii.png
Pour améliorer la facilité d'utilisation des lunettes, nous pouvons équiper ces dernieres d'un interrupteur pour activer/désactiver les leds.
À partir de lunettes à
anaglyphes 3
(gure 2.3), nous pouvons découper les ltres
rouge et cyan, an de les xer sur notre paire de lunette munie de leds infrarouges.
Nous obtenons nalement une paire de lunettes infrarouges équipées de ltres rouge/cyan.
Ces lunettes sont notre portail vers la réalité virtuelle, que nous pourrons utiliser pour
visualiser des scènes réactives à nos mouvements, et ce, en trois dimensions.
2.1.2 Gant infrarouge
Plutôt que de se restreindre à l'utilisation du headtracking au sens strict, nous pouvons
proter de la technologie que nous possédons pour élargir les possibilités de nos applications. Nous pouvons créer un
gant infrarouge
qui nous permettra d'interagir avec la main
3. Un anaglpyhe est une image crée à l'aide de deux ltres de couleurs diérentes pour être vue en
relief. Ce procédé utilise le décallage entre nos deux yeux pour créer des images qui seront perçues en
relief par un utilisateur équipé de lunettes à anaglyphes. [Wikipedia]
17
Figure 2.3: Lunettes 3D, équipées d'un ltre rouge pour l'oeil gauche et cyan pour l'oeil
droit. Source :
http://www.pandorabox.fr/headers/lunettes3d2.jpg
dans nos applications, en plus de la tête. Le terme de gant infrarouge signie simplement
que nous équipons un gant de leds infrarouges, ajoutant ainsi la possibilité à la caméra
de détecter la position de la main de l'utilisateur.
L'intérêt de ce gant est qu'il nous permet d'ajouter un degré de liberté supplémentaire
dans nos applications de réalité virtuelle. Il est facile d'imaginer tout ce que la main peut
contrôler. Dans un jeu de ping-pong par exemple, la tête peut gérer le déplacement de
la caméra et la main s'occupe de déplacer la palette de ping-pong. Dans un explorateur
de chier, tout l'intérêt est de pouvoir piloter l'interface avec ses mains. Sélectionner un
dossier, acher une image, la déplacer, l'agrandir, la faire pivoter, autant de possibilités
envisageables en ajoutant l'utilisation de la main.
Lorsque nous utiliserons le gant infrarouge et les lunettes, il nous faudra être capable
de les dinstinguer. Pour ce faire, plusieurs solutions sont envisageables.
Une première approche dans la distinction entre la main et le visage consiste à utiliser
un nombre de leds diérent pour la main. Nous pouvons par exemple utiliser une seule
led pour détecter la position de la main. Avec une unique led, il n'est alors plus possible
de détecter la dimension de profondeur, car un minimum de deux leds sont nécessaires
pour calculer cette information. Cependant, en fonction du type d'application désirée, il
n'est pas toujours utile d'avoir une information sur la profondeur de la main.
Exemple 1
Dans une application où nous souhaitons visualiser des photographies, les
mouvements de la main sont utilisés pour passer d'une photo à l'autre, ou encore pour
agrandir ces dernières. L'information de profondeur n'est alors pas utile, car ce qui nous
intéresse concerne les mouvements latéraux perçus sur le capteur.
Exemple 2
Dans un jeu de pong simple, nous pouvons nous restreindre à positionner la
main à la même profondeur que le visage. Les seuls mouvements possibles de la raquette
sont alors de gauche à droite et de haut en bas. Bien que cette approche soit restrictive,
elle apporte tout de même deux degrés de liberté supplémentaires par rapport au seul
headtracking.
La limitation induite par la Wiimote ne nous permet pas de détecter aisément plus
18
de 4 sources simultanément. Nous ne pouvons donc pas utiliser plus de 2 sources pour
détecter la position de la main. Toutefois, l'utilisation d'un autre type de caméra nous
permettrait d'utiliser plus de sources comme point de repère pour détecter la main.
Une autre solution pour détecter la position de la main, en plus de celle du visage, serait
d'utiliser deux Wiimotes. L'utilisation de deux caméras, placées côte à côte, permettrait
d'inverser tout le processus de détection de profondeur, en se basant non plus sur la
distance entre deux leds pour calculer la profondeur, mais en utilisant plutôt la distance
en pixels d'une seule source infrarouge sur les deux capteurs. Puisque les deux caméras
sont côte à côte, la distance qui les sépare implique que l'image perçue par l'une sera
légèrement décalée par rapport à l'image perçue par l'autre caméra. Nous pouvons utiliser
ce décalage pour calculer la distance à laquelle se trouve une source infrarouge.
Grâce à ce nouveau procédé, une seule led est susante pour détecter la profondeur.
Nous pouvons alors utiliser deux leds pour détecter la position du visage et une seule led
pour la position de la main.
Si nous souhaitons utliser le même nombre de leds pour détecter le visage et la main, un
autre artice est nécessaire pour permettre de les distinguer. Nous pouvons par exemple
utiliser la distance réelle entre les leds comme information codante pour discerner la main
du visage. Lorsque le headtracking est utilisé en complément des mains, l'utilisateur se positionne couramment à une distance d'au moins un mètre cinquante par rapport à l'écran.
Si nous utilisons une distance physique deux fois plus petite entre les leds servant à détecter la main par rapport à celles utilisées pour le visage, il n'est pas dicile discerner
ces dernières, tant qu'elles ne sont pas confondues. Si toutefois les mains de l'utilisateur
se trouvent beaucoup plus en avant, la distance perçue sur le capteur entre les leds pourrait devenir supérieure à celle entre les leds servant de point de repère pour calculer la
position du visage. Pour éviter ce problème, nous pourrions utiliser une distance physique
plus grande entre les leds de la main. Toutefois, cela impliquerait que l'information de
profondeur concernant la main pourrait être détectée avec une plus grande précision que
celle concernant le visage.
Utiliser une distance supérieure entre les leds permet de détecter l'information de
profondeur avec une meilleure précision. Illustrons ceci par un exemple : si à une distance
de deux mètres, deux leds situées à une distance réelle de 20 centimètres sont séparées par
une distance de 50 pixels sur le capteur, alors deux leds situées à une distance réelle de 10
centimètres ne seront séparées que par 25 pixels sur le capteur. Cela signie que lorsque
l'utilisateur s'éloignera encore plus de la caméra, une diérence de 1 pixel correspond à
une variation de 2% dans le premier cas et 4% dans le second cas. Nous pouvons alors
dire que plus la distance réelle entre les leds est grande, plus la précision avec laquelle
nous pourrons calculer la profondeur sera bonne.
D'un autre coté, utiliser une distance trop importante entre les leds pourrait devenir
inconfortable pour l'utilisateur, qui ne souhaite pas être contraint à utiliser un dispositif
de 40 centimètres de large pour détecter avec précision la position de sa tête. De plus,
utiliser une plus grande distance entre les leds augmente également la distance minimale
à laquelle le headtracking peut être eectué. Avec une distance de 40 centimètres entre
les leds, un capteur ayant un angle de vue de 45 degrés ne sera pas capable de distinguer
19
celles-ci à une distance inférieure à 1m.
Le cas des leds confondue devra être traité avec plus de soin, si nous voulons éviter
les incohérences. Une méthode naïve pour calculer la position des mains et de la tête
est décrite ci-dessous. Cette méthode devrait également être appliquée pour les méthodes
précédemment exposées :
Si les mains et la tête ne sont pas confondues alors :
pos_tete := calc_pos_tete()
pos_main := calc_pos_main()
Sinon :
(x,y) := calculer la position latérale et verticale
du centre de gravité des points confondus
z := la précédente valeur de la profondeur de pos_tete
pos_tete := (x,y,z)
pos_main := pos_tete
1. Les méthodes calc_pos_tete() et calc_pos_main() se chargent respectivement de
calculer la position de la tête et de la main dans l'espace. Ces méthodes renvoient
un triplet contenant la position sur les axes x,y et z, correspondant respectivement
aux mouvements latéraux, verticaux et en profondeur.
2. Si les points concernant la tête et la main sont confondus, alors on positionne la
main et la tête au même endroit dans l'espace. Cette heuristique n'est pas parfaite,
dans le sens où de légers mouvements de la main près de la tête ne pourront pas
être perçus. De plus, l'information de profondeur ne pourra pas être remise à jour
tant que la main et la tête seront confondues. Si nous voulons de meilleurs résultats,
il est alors nécessaire d'implémenter un algorithme plus performant qui se base sur
la détection et la reconnaissance de motifs. Le but est alors de distinguer le motif
correspondant au visage de celui représentant la main.
Utiliser pour seule information codante la distance entre les leds pour distinguer la main
du visage n'est pas un procédé susamment robuste. Il sera donc nécessaire d'ajouter
d'autres méthodes pour éviter que la machine ne se trompe. Nous pouvons par exemple
supposer que entre deux évènements infrarouges, le mouvement eectué n'a qu'une faible
amplitude (nous pouvons supposer que c'est le cas, car nous pouvons traiter jusqu'à 100
évènements par seconde). En gardant en mémoire la position de chaque point lors de
l'évènement précédent, nous pouvons comparer les anciens points et les nouveaux points,
et supposer que le point le plus proche d'un ancien point représente la même chose que
cet ancien point. Si nécessaire, nous pourrons aussi utiliser une convention pour améliorer
le tracking des points. Par exemple, nous pouvons demander à l'utilisateur que, lors du
lancement du programme, sa main ne soit pas située beaucoup plus en avant/arrière par
rapport à sa tête, de sorte que l'ordinateur puisse distinguer la main de la tête via la
distance entre les points. Une fois cette déduction faite, le programme pourra suivre les
points d'un évènement à l'autre.
En pratique, la proximité entre deux évènements n'est pas toujours assurée. Lors de
nos tests, il est arrivé que le nombre d'évènements par seconde chute à une valeur inférieure
à 30. Lors de mouvements rapides, le qualité du tracking est alors fortement dégradée, et
il est alors dicile pour l'algorithme de suivre ecacement les sources infrarouges.
20
Figure 2.4: une Wiimote
2.1.3 Wiimote
Comme nous l'avons déjà mentionné plusieurs fois, le périphérique de capture utilisé
pour la réalisation de notre projet est une Wiimote. Dans cette section, nous expliquerons
ce qu'est une Wiimote et nous discuterons des spécications techniques de ce contrôleur.
2.1.3.1 Qu'est ce qu'une Wiimote ?
la
Wiimote
ou
Wii Remote
est le nom donné au contrôleur de la console de jeu Nin-
tendo Wii. Il s'agit d'une petite télécommande de forme rectangulaire contenant une série
de capteurs tels que des accéléromètres, et une caméra monochrome équipée d'un ltre infrarouge. Ces détecteurs permettent de situer la Wiimote dans l'espace, et de retranscrire
ses mouvements à l'écran. Cette nouvelle manette apporte une dimension plus immersive
au jeu vidéo selon son constructeur. Nous pouvons voir sur la gure 2.4 une Wiimote. Elle
est composée d'une série de boutons en plus de ses capteurs, pour permettre l'interaction
avec son utilisateur.
Pour notre projet, nous n'utilisons que la caméra infrarouge située sur la partie haute
de la télécommande (le rectangle noir sur l'image du haut de la gure 2.4 ). En eet, la
Wiimote sera déposée sous ou sur l'écran où la scène en 3 dimensions sera projetée. Il
n'est donc pas utile d'utiliser les diérents boutons du contrôleur.
2.1.3.2 Spécications techniques
D'un point de vue technique, la Wiimote [25] dispose :
de plusieurs accéléromètres pour détecter sa position (debout, penchée vers le bas,
etc),
d'un capteur infrarouge pour se repérer par rapport à la
Sensor bar (
la Sensor bar
est une petite barre d'une vingtaine de centimètres de long, qui contient à chaque
extrémité une série de leds infrarouges. Elle est utilisée comme point de repère pour
21
la Wiimote. Les sources infrarouges sont vues par la caméra de la Wiimote, qui peut
alors se repérer sur base de la position des points sur le capteur.
),
de plusieurs boutons (A,B, èches directionnelles, +, -, 1, 2, Home, gachette, power)
pour l'interaction avec le joueur,
d'un haut-parleur pour émettre des sons,
d'un vibreur,
d'une interface bluetooth pour communiquer sans l avec la console ou le PC.
Parmi toutes ces spécications, nous n'utilisons que le capteur infrarouge et l'interface
bluetooth.
Le capteur infrarouge (IR) se compose d'une caméra monochrome (en niveau de gris)
ayant un angle de vue horizontal de 42° et un angle de vue vertical de 33°, regardant
à travers un ltre infrarouge. Cette caméra est équipée d'un petit processeur capable
de détecter jusqu'à 4 sources mobiles. Ce petit processeur ne permet pas de capturer
l'image complète vue par la caméra, les seules informations disponibles concernent les 4
sources lumineuses pouvant être détectées. De cette façon, l'information renvoyée par la
caméra ne pèse que quelques octets à chaque image. Il existe 3 modes implémentés (par
Nintendo) pour représenter les sources infrarouges (les informations qui suivent ne sont
pas nécessaires à la compréhension du problème et sont présentes comme complément
d'informations).
1. Le mode basique est le plus léger, il ne nécéssite que 10 octets pour stocker les
coordonnées des 4 sources infrarouges pouvant être détectées. Chaque coordonnée
(X1,Y1,X2,Y2) peut être codée sur 10 bits (0-1023) et chaque paire de point est
emballée dans un paquet de 5 octets comme le montre la gure 2.5. Sur cette gure,
est représenté un paquet contenant les informations de deux sources infrarouges
P1=(X1,Y1) et P2=(X2,Y2). Parmi les 5 octets de ce paquet, le premier octet
représente les 8 bits de poids le moins signicatif de la coordonnée X1 du point P1.
Le second octet représente les 8 bits de poids le moins signicatif de la coordonnée
Y1 du point P1. Le troisième octet contient 4 informations : les 2 bits de poids le plus
signicatif contiennent les 2 premiers bits de Y1, les 2 bits qui suivent contiennent
les 2 premiers bits de X1, les 4 derniers bits contiennent les 2 bits de poids signicatif
de Y2 et X2. Les 4e et 5e octets contiennent les 8 bits de poids le moins signicatif
de X2 et Y2.
2. Le mode étendu permet d'obtenir en plus des coordonnées de chaque point une
information sur l'intensité de chaque source lumineuse codée sur 4 bits (0-15), chaque
point est alors emballé dans un paquet de 3 octets, comme nous le montre la gure
2.6. Une taille totale de 12 octets est alors nécessaire pour stocker les 4 points.
3. Le mode complet est le plus lourd des trois, mais aussi le plus complet. Il nécessite
une information de 36 octets pour stocker les 4 sources lumineuses.
Chaque point possède, en plus des 3 octets du mode étendu, 6 octets supplémentaires
(cf gure 2.7). Parmi ceux-ci, un octet permet d'obtenir une valeur plus précise de
l'intensité du point IR.
22
Figure 2.5: représentation des points en mode basique
Figure 2.6: représentation des points en mode étendu
Figure 2.7: représentation des points en mode complet
Grâce à la librairie que nous utilisons (WiiuseJ), nous ne devons pas nous occuper de
gérer l'interface bluetooth. Elle gère toute l'interface de communication entre la Wiimote
et le PC. Nous pouvons donc voir cette interface comme une couche à abstraire.
2.1.4 Utilisation d'une Webcam
Plutôt que d'utiliser une Wiimote et des sources infrarouges pour détecter la position
de la tête, il est possible d'utiliser une simple webcam (cf gure 2.8 ) pour détecter la
position de la tête dans l'espace. La diculté consiste alors à détecter où se trouve la tête
sur l'image perçue par la webcam. Pour ce faire, plusieurs solutions sont possibles :
23
Figure 2.8: Une Webcam [24] (à gauche) et une caméra stéréoscopique [20] (à droite)
1. Utiliser des points de repères comme avec la wiimote pour faciliter la détection. Par
exemple en positionnant deux points uo près du visage, il est possible de faciliter la
recherche du visage, qui se limiterait alors à chercher ces points de repère. Une fois
la position de ces deux points sur le capteur trouvée, il ne reste plus qu'à envoyer
l'information au module de headtracking qui se chargera d'eectuer les calculs pour
situer la tête dans l'espace à partir des coordonnées des deux points de repère. Un
algorithme naïf pour la recherche des deux points est de parcourir chaque pixel
du capteur, à la recherche de deux points ayant une couleur assez proche de celle
recherchée. La comparaison de couleurs s'eectuera en RGB avec un certain seuil.
Notons que la taille du point uo a très peu de chance de ne faire qu'un seul pixel,
il est alors nécessaire de calculer quand nous nous trouvons deux fois sur le même
point uo. Par exemple, le pixel juste à droite du premier pixel uo trouvé à de fortes
chances d'être lui aussi coloré de la même façon. Il faut donc un mécanisme qui soit
capable de détecter cette situation, tout en permettant de trouver le deuxième point
uo.
2. Une autre solution consiste à utiliser une librairie annexe permettant de détecter la
position du visage. De nombreuses recherches ont été menées dans le domaine de
la détection de visage à la caméra, il serait par conséquent dommage de ne pas en
proter. A cette n, nous pouvons citer OpenCV, dont nous avons déjà discuté à
la section 1.4.2. Cette seconde solution est plus intéressante car elle nous permet de
ne pas avoir à réinventer la roue.
De façon générale, quelle que soit l'approche utilisée, l'utilisation d'une webcam pour
détecter la position de la tête est très couteuse en temps CPU. L'algorithme utilisé doit
être capable de détecter la position de la tête sur une image possédant 300K pixels (dans
le cas d'une résolution VGA, de 640x480 pixels). Seule une machine très puissante est
capable de détecter le visage et d'en déduire sa position dans l'espace en moins de 35
millisecondes, temps maximal autorisé si nous souhaitons une application temps réel.
24
2.1.5 Headtracking grâce à la stéréoscopie
Une autre solution pour détecter la position de la tête en temps réel a déjà été discutée à la section 2.1.2. Il s'agit d'utiliser le principe de stéréoscopie an de détecter dans
l'espace la position d'une personne.
Une caméra stéréoscopique (cf gure 2.8) est un dispositif possédant deux interfaces
de capture vidéo, utilisé pour générer des images stéréoscopiques. Plus précisément, il
s'agit d'une caméra qui possède deux objectifs, distants de quelques centimètres, et pointant dans la même direction. Les images perçues par chacun de ces objectifs sont alors
légèrement décalées l'une par rapport à l'autre, permettant de générer des vidéos stéréoscopiques. Les images perçues par chacune des caméras pourront être traitées avec des
ltres de couleurs, polarisés ou via une autre méthode qui permettra par la suite de les
projeter en générant un eet de relief moyennant le port de lunettes adéquates.
L'idée sous-jacente à la création d'un système de headtracking en utilisant une caméra
stéréoscopique est de se servir du décalage entre les deux images pour calculer la distance
à laquelle se trouve le sujet. Au lieu d'utiliser deux leds infrarouges et une seule caméra,
le processus inverse est appliqué : deux caméras lment une même scène avec un léger
décalage entre les deux objectifs. Lorsqu'un sujet s'éloigne de la caméra, la distance en
pixels perçue sur le capteur, entre les deux sujets présents sur chaque objectif, diminue.
Nous pouvons dès lors utiliser cette information pour calculer la profondeur à laquelle est
situé le sujet à détecter.
Toutefois, d'un point de vue performance, l'utilisation de deux caméras pour détecter
en temps réel la position d'un utilisateur demande encore plus de ressources que l'utilisation d'une webcam. Une solution alternative est d'utiliser deux wiimotes situées à
quelques centimètres de distance pour remplacer une caméra stéréoscopique. De cette façon, nous pouvons combiner l'ecacité de la stéréoscopie à la rapidité de traitement liée
à l'utilisation de leds infrarouges.
Nous pouvons alors penser que l'utilisation d'une seule led infrarouge comme point
de repère nous sura à détecter la position du sujet dans l'espace. C'est en eet le
cas, puisque ce sont les deux caméras infrarouges qui exploiteront la distance qui les
sépare pour calculer la profondeur à laquelle se situe l'utilisateur. Notons cependant que
l'utilisation d'une seule led ne nous permettra plus de distinguer les mouvements de roulis
de la tête. Il serait donc certainement plus intéressant de continuer à utiliser deux leds
infrarouges pour situer la position de la tête. De plus, avec deux caméras et deux leds
infrarouges, nous serions également capables de détecter les rotations de la tête. En eet,
si un utilisateur eectue une rotation de la tête, alors les leds sur le capteur vont se
rapprocher, tandis que la distance entre les deux images sur chacun des deux capteurs ne
bouge pas. L'utilisation de deux leds infrarouges et de deux wiimotes nous permet donc
de créer une solution de headtracking aussi ecace que celle développée par Track IR (cf
section 1.4.3) en terme de nombre de degrés de liberté. Cette approche pourrait être vue
comme une amélioration possible de notre application.
25
Figure 2.9: Utilisation de trois leds
2.1.6 Amélioration des lunettes
Dans cette section, nous verrons que pour une détection complète en trois dimensions
de tous les mouvements, deux leds ne susent pas. Nous passerons également en revue
deux autres techniques plus ecaces que l'anaglyphe pour perçevoir des images en trois
dimensions.
2.1.6.1 Utilisation de trois sources infrarouges
Pour la réalisation de notre projet, nous avons choisi d'utiliser deux leds infrarouges
comme point de repère pour situer la position de la tête. Ce choix a été le sujet d'une longue
réexion, à l'issue de laquelle nous avons décidé de n'utiliser que deux leds. Rapelons
brièvement les diérences qui existent entre utiliser deux ou trois sources infrarouges :
1. L'utilisation de deux leds ne permet pas de détecter tous les types de mouvements.
Plus spéciquement, les rotations de la tête ne peuvent pas être décelés. Si l'utilisateur tourne la tête vers la droite ou la gauche, la projection des leds sur le capteur
se rapproche et le calcul de la distance est faussé, car si la distance entre les leds
diminue sur le capteur, cela est perçu comme un éloignement de l'utilisateur.
2. Choisir d'utiliser trois leds comme point de repère permet en revanche de rendre
la détection des rotations de la tête possible. En eet, si nous disposons les leds
de sorte qu'elles ne soient pas alignées, alors lorsque l'utilisateur tourne la tête, le
comportement des leds perçu sur le capteur est diérent d'un simple éloignement.
Supposons par exemple que les trois sources infrarouges sont placées selon un triangle
équilatéral, représenté à la gure 2.9 (triangle de gauche). De cette façon, lorsque
l'utilisateur pivote la tête, la projection sur le capteur du triangle sera perçue comme
le triangle de droite de la gure 2.9. De cette façon, nous voyons qu'il est tout à fait
possible de diérencier la rotation de la tête de l'éloignement du sujet.
26
Figure 2.10: Emetteurs de Track IR [23]
Au vu de ces diérences, l'utilisation de trois leds infrarouges semble beaucoup plus intéressante puisqu'elle permet une meilleure robustesse de l'application, ainsi que l'ajout
d'un degré de liberté supplémentaire. Toutefois, nous sommes freinés par une limitation
stricte du matériel utilisé : la wiimote ne peut détecter que 4 sources infrarouges. Si nous
souhaitons utiliser la main en plus du visage, il devient alors impossible d'utiliser deux
leds pour détecter la position des mains, car trois sources sont déjà utilisées pour la détection du visage. Nous sommes donc restreints à n'utiliser qu'une seule led pour interagir
avec la main dans nos applications. Comme nous le savons, l'utilisation d'une seule led
ne nous permet pas de calculer l'information de profondeur associée à la main.
Si nous utilisons trois leds comme points de repère, notre solution devient alors plus
proche de celle employée par Track IR (voir section 1.4). Pour détecter la position de
l'utilisateur avec un total de six degrés de liberté, Track IR utilise une solution avec trois
leds infrarouges pour calculer la position, ainsi que l'orientation du joueur dans l'espace.
L'émetteur utilisé par Track IR contenant trois sources infrarouges est représenté à la
gure 2.10.
Pour conclure, l'utilisation de trois leds pour détecter tous les mouvements de la tête
peut sembler utile, mais rend dicile l'utilisation de la main. Une évolution possible de
notre programme est d'intégrer la détection de la tête grâce à trois leds, et de laisser la
possibilité à l'utilisateur de choisir quelle solution il souhaite utiliser (deux ou trois leds
pour détecter le visage, sachant que l'utilisation de trois leds rend le calcul de la profondeur
impossible sur la main). Si nous comparons cette solution avec l'ajout de la stéréoscopie
(cf section 2.1.5), l'utilisation de deux wiimotes est plus intéressante, car seules deux leds
sont nécessaires pour de détecter tous les mouvements possibles de la tête, et l'utilisation
d'une seule led pour la main permet de calculer l'information de profondeur lui étant
associée.
2.1.6.2 Polarisation
An d'améliorer la qualité de la trois dimensions, d'autres solutions plus performantes
que la projection d'anaglyphes existent. Parmi ces solutions, la polarisation est la technique la plus courante utilisée dans les salles de cinéma équipées pour la 3D.
27
Nous savons que la lumière qui nous entoure est consitutée de photons se propageant
dans toutes les directions. Ces photons se répercutent dans toutes les directions sur les
diérentes surfaces qui nous entourent, et nissent par arriver à notre oeil, en provenance
de toutes les directions.
La polarisation de la lumière est un procédé qui ltre les rayons lumineux, en ne laissant passer que les rayons ayant une certaine direction, par rapport à la surface polarisée.
Par exemple, si nous utilisons une surface polarisée de façon horizontale, seuls les rayons
lumineux ayant une direction horizontale pourront passer à travers le ltre polarisé. C'est
le cas des lunettes de soleil dites polarisées. Elles contiennent un ltre qui permet de
bloquer les rayons du soleil issus de la rééxion sur une surface plane telle qu'une route
mouillée ou encore un sol enneigé.
Pour observer des images en relief, la polarisation peut être utilisée. Il est alors nécessaire d'utiliser deux projecteurs équipés de ltres polarisés perpendiculairement l'un par
rapport à l'autre et une paire de lunettes, elle aussi équipée de deux ltres orientés dans
les directions correspondantes à ceux placés sur les projecteurs. Grâce à cet équipement,
chaque projecteur va acher une image étant destinée à un seul oeil. L'image achée par
le projecteur étant équipée du ltre horizontal ne pourra alors être perçue que par l'oeil
devant lequel le ltre horizontal a été placé. De cette façon, il est possible de visualiser
une scène avec un eet de relief, en projetant une image diérente pour chaque oeil, de
la même façon que pour des images anaglyphes.
Le principal problème est que, pour avoir un eet de relief ecace, il est nécessaire de
garder les lunettes bien horizontales. En eet, puisque les images achées par le projecteur
sont polarisées horizontalement et verticalement, ne pas garder les lunettes dans une
position horizontale empêcherait les rayons lumineux de passer au travers des ltres placés
sur les lunettes (qui ne sont plus horizontaux et verticaux, si les lunettes ne sont pas bien
horizontales).
Par la suite, cette technologie de lunettes à polarisation dite linéaire, a été remplacée
par des lunettes équipées de ltres polarisés de façon circulaire, rendant alors la visualisation indépendante de l'orientation de la paire de lunettes.
Au niveau ecacité, la polarisation est plus intéressante que l'anaglyphe, car elle ne
doit pas utiliser des ltres colorés pour donner un eet de relief. Pour une ecacité optimale, l'utilisation de ltres colorés pour donner un eet de relief à une image se retreint à
la génération d'images en noir et blanc. Puisque les couleurs sont utilisées pour générer le
relief, elles ne peuvent plus être utilisées pour coloriser l'image. Toutefois, il est toujours
possible d'utiliser les couleurs pour visualiser des anaglyphes, mais il est souvent nécessaire
d'optimiser ces images. La technique de polarisation est plus ecace que l'anaglyphe, car
la qualité du relief ne dépend pas de la couleur de l'image à acher. Si nous souhaitons
acher un objet rouge en relief avec des lunettes à ltres colorés, la composante cyan de
cet objet est nulle. Par conséquent, la partie destinée à l'oeil droit (typiquement, le ltre
cyan est placé sur l'oeil droit, mais il ne s'agit pas d'une obligation) ne contiendra pas
l'objet rouge. Avec la technique de polarisation, ce problème n'est pas rencontré.
Notons toutefois que l'utilisation de la polarisation est bien plus onéreuse que la mé-
28
thode basée sur les anaglyphes. Pour utiliser cette méthode, il est nécessaire de s'équiper
de deux projecteurs, ainsi que de ltres polarisés qu'il faudra placer devant chacun d'eux.
Il est également indispensable de s'équiper de lunettes polarisées, compatibles avec les
ltres placés sur les projecteurs. Au total, plusieurs milliers d'euros sont nécessaires à la
mise en place d'un tel système.
En plus de son prix élevé, la mise en place d'une solution basée sur la polarisation
requiert une mise en oeuvre beaucoup plus importante. La présence de deux projecteurs
étant indispensable, la solution est bien plus dicile à déplacer, comparée à celle utilisant
les anaglyphes, qui ne requiert aucun écran spécial, et une simple paire de lunettes.
2.1.6.3 Obturation
Le système de lunettes à obturation (Liquid Crystal shutter glasses en anglais) est une
solution alternative à l'utilisation de la polarisation, de plus en plus utilisée pour le grand
public, pour visualiser des images en relief.
De nos jours, de plus en plus de télévisions estampillées
3D Ready
font leur apparition
dans les grandes surfaces. La plupart de ces télévisions requièrent l'utilisation de lunettes
à obturation pour obtenir un eet de relief dans l'image achée.
D'un point de vue technique, la visualisation d'images en trois dimensions est possible
grâce à l'utilisation de lunettes spéciales. Ces lunettes contiennent des verres à l'intérieur
desquels une couche de cristaux liquides est contenue. Cette couche a la particularité
de pouvoir s'assombrir très rapidement lorsqu'une tension électrique lui est appliquée.
Lorsqu'aucune tension électrique ne lui est appliquée, la couche de cristaux liquides est
translucide et laisse donc passer la lumière.
Comme pour tous les procédés permettant de voir des images en relief, cette méthode
ache une image diérente pour chaque oeil. C'est donc la manière dont les images parviennent à chaque oeil qui dière. Comme les lunettes sont capables de s'obturer à une très
grande vitesse, il est possible de ne laisser passer la lumière que pour un seul oeil durant
e
une très courte période (typiquement 1/60 de seconde). Le procédé utilisé est donc le suivant : pendant que l'image est projetée à l'écran, son taux de rafraîchissement est mesuré.
Les lunettes se synchronisent alors et obturent successivement l'oeil gauche, puis l'oeil
droit à la même vitesse que le taux de rafraîchissement de l'écran. Une image sur deux
sera donc visible uniquement par l'oeil gauche/droit. Pour permettre cette synchronisation, la télévision est équipée d'un émetteur infrarouge, qui envoie aux lunettes un signal
de timing leur permettant de s'adapter au taux de rafraîchissement de l'écran. Notons
que d'autres méthodes existent pour eectuer cette synchronisation entre la télévision et
les lunettes. Parmi celles-ci, nous citerons le DLP-Link, qui ne nécessite pas d'émetteur
infrarouge. Durant la projection, un bref ash de lumière est émis par le téléviseur, lors
de la transition entre l'image destinée à l'oeil gauche et celle destinée à l'oeil droit. Les
lunettes utilisent ce signal pour changer l'obturation de l'oeil droit à l'oeil gauche. Le
même signal est utilisé lors du passage de l'oeil droit à l'oeil gauche.
Le principal avantage de l'obturation est que la télévision projette successivement
une image pour chaque oeil. Comme les deux images ne sont jamais présentes en même
temps sur l'écran, la qualité du relief est meilleure qu'avec un système à polarisation, qui a
tendance à parfois acher des images oues, comme montré sur la gure 2.11. Cependant,
29
Figure 2.11: Eet de ghosting [4]
comme chaque oeil ne voit qu'une image sur deux, il ne reçoit que la moitié du taux de
rafraîchissement eectif du téléviseur qui doit alors être important (supérieur à 120Hz)
pour éviter l'eet de clignotement.
2.2 Choix informatiques
Lors du développement d'un projet logiciel, il est nécessaire de choisir un langage
adapté au travail réalisé. En fonction des besoins requis par l'application, certaines caractéristiques d'un langage sont parfois intéressantes. L'exemple le plus simple est l'utilisation
d'un langage fonctionnel pour réaliser des calculs mathématiques. Pour notre travail, nous
souhaitons développer une application temps réel qui soit portable et réutilisable. De plus,
an d'éviter de réinventer la roue, il est souhaitable de choisir un langage pour lequel une
librairie capable d'interagir avec la Wiimote existe. Le choix d'un langage adapté sera
donc déterminé par la réalisation de ces diérents besoins.
2.2.1 Besoins du langage
Pour implémenter le concept de headtracking, une chose importante est de se munir
d'une librairie ecace. Lors de nos recherches, nous avons pu constater qu'une multitude
de libraires permettant de contrôler la Wiimote existent. Ces librairies sont présentes dans
diérents langages de programmation, tels que C, C#, Java, Python. Malheureusement,
pour bon nombre de ces librairies, la documentation présente est très restreinte, ce qui
ne permet pas à un développeur de pouvoir les utiliser facilement, sans avoir à se plonger
dans le code source (c'est par exemple le cas de Cwiid, une librairie développée en C
pour interagir avec la Wiimote). Nous avons également remarqué que certaines librairies
fonctionnaient mieux que d'autres. Lors de nos tests, nous avons essayé plusieurs librairies,
parmi celles-ci, nous en avons extrait trois qui semblaient adéquates pour le travail que
nous souhaitons réaliser.
30
1. Cwiid [2] : il s'agit d'une librairie développée en C, qui permet d'interagir avec la
Wiimote. Cwiid est disponible sur les dépôts d'ubuntu et peut donc être facilement
téléchargé. Pour fonctionner, il est nécessaire d'installer diverses librairies gérant
l'interface bluetooth. Son principal défaut est le manque de documentation, qui
rend son utilisation pour le développement assez dicile.
2. WiiRemoteJ [8] est une API développée en Java, qui permet de communiquer avec
la Wiimote. La documentation concernant cette librairie est très riche, et facilite
son utilisation. Malheureusement, sa robustesse est assez faible. La communication
avec bluetooth est assez instable, ce qui rend son utilisation assez aléatoire.
3. Wiiuse [26] est la librairie la plus intéressante que nous avons rencontrée. Sa documentation est susante pour comprendre son utilisation. Sa prise en main est très
aisée, et sa robustesse est plus importante que celle de la librairie WiiRemoteJ. Lors
de nos recherche, nous avons pu constater que Wiiuse semblait être la librairie la
plus utilisée sur Internet.
Une fois le choix d'une bonne librairie eectué, il est nécessaire d'opter pour un langage
qui puisse interagir ecacement avec celle-ci. Nous souhaitons programmer une application temps réel, il est donc impératif d'utiliser un langage qui soit non seulement robuste,
mais aussi susamment rapide pour interagir avec la librairie choisie. Avec la puissance
grandissante des ordinateurs, la plupart des langages récents, même de haut niveau ou
interprété, sont capables de gérer des applications multimédia en temps réel. Le critère
de performance est donc de moindre importance, même si l'utilisation d'un langage performant nous permettrait de représenter des scènes plus complexes. Toutefois, poussé à
l'extrême, ce raisonnement ne nous aurait jamais permis de passer à des langages de
plus haut niveau, et l'assembleur serait toujours le langage le plus utilisé. De nos jours,
l'utilisation d'un langage de haut niveau s'impose presque comme un standard, car il est
important de privilégier la qualité du code, ainsi que sa lisibilité, an de rendre son évolution plus aisée.
Puisque nous souhaitons développer des applications avec un rendu en trois dimensions, notre langage doit posséder une API 3D. La plupart des langages de haut niveau
possèdent une telle librairie. La plus connue est OpenGL [6]. Il s'agit d'une librairie graphique, multiplateforme, qui possède une implémentation dans de très nombreux langages
de programmation. OpenGL est presque considéré comme un standard et est la librairie
graphique la plus utilisée dans le monde scientique et industriel.
Lors du développement d'une application, la portabilité est un atout majeur, pour
être utilisable sous diérentes plateformes, telles que Windows, Unix (incluant MacOS)
ou Linux. Le principal avantage d'un langage portable est qu'une application développée
ne nécessite que peu voire pas de changement pour fonctionner sur diérents systèmes
d'exploitation.
2.2.2 Choix du langage et argumentation
Un des langages qui rencontre tous les besoins de notre module
headtracking
est le
langage Java. Dans la suite de cette section, nous montrerons que ce langage est un bon
choix pour ce type de programme.
31
2.2.2.1 Ecacité de la librairie
La première chose à aborder est la librairie utilisée pour interagir avec la caméra
infrarouge (la Wiimote). Parmi celles qui ont été testées, la plus ecace et la mieux documentée est Wiiuse [26]. Cette librairie a été implémentée en C, un choix particulièrement
intéressant puisque l'interaction avec un contrôleur est un travail d'assez bas niveau qui
requiert l'utilisation de sémaphores, de threads, etc et dont le protocole de communication
est souvent proche du langage machine.
L'avantage du Java est la présence de l'interface JNI (Java Native Interface). À titre
informatif pour le lecteur, les couches bas-niveau de Java ont été écrites en C. JNI est
une interface qui permet d'utiliser du code C via un appel de méthode en Java. Pour plus
d'informations sur JNI, consulter [13]. La librairie Wiiuse a ainsi pu être étendue au Java
via l'utilisation de JNI et le nouveau nom accompagnant cette interface est WiiuseJ [27].
Cette interface est donc toute indiquée puisqu'elle eectue du code C via un appel de
méthode en Java. Cette technique nous permet donc de communiquer via le langage Java
à une vitesse très correcte avec notre librairie. Pour nous convaincre de la réactivité obtenue grâce à la librairie WiiuseJ, nous avons eectué un petit benchmark où nous avons
mesuré le nombre d'évènements moyen par seconde que la librairie est capable de traiter.
En moyenne, les résultats montrent que plus de 60 évènements peuvent être gérés chaque
seconde, c'est-à-dire un temps de réponse moyen de l'ordre de 15ms. Dans le pire des cas,
il peut arriver que le nombre d'évènements chute à 25 par seconde (temps de réponse
d'environ 40 ms), ce qui reste toutefois raisonnable.
Dans le cadre de notre travail, nous n'exploitons qu'une petite partie de cette librairie.
La seule partie qui nous intéresse est celle qui gère la caméra infrarouge. Une fois la caméra
activée, nous pouvons invoquer des méthodes sur notre Wiimote qui nous permettent de
récupérer des points infrarouges sur le capteur (sous la forme d'un objet de type IRDot).
De ces points infrarouges, nous pouvons tirer un certain nombre de renseignements, tels
que leur position ou encore leur intensité (codée sur un entier positif de 4 bits). Ces informations sont susantes pour calculer la position de la tête d'un utilisateur par rapport à
la caméra.
2.2.2.2 Rapidité du langage
Le langage Java est un langage de haut niveau qui est
compilé
(c'est-à-dire qu'avant
de l'exécuter, il est nécessaire de le traduire en un programme plus bas niveau appelé
bytecode, an qu'il puisse être exécuté par la machine). Un avantage d'un langage compilé
est qu'il est très souvent plus rapide que son opposé, le langage
interprété. De plus, il est
bon de savoir que pour des calculs mathématiques, et c'est principalement l'objet de
notre travail, le Java est un langage susament rapide notamment grâce à la présence
4
des types primitifs Pour représenter les nombres . Les performances du langage Java sont
donc plutôt correctes, malgré qu'il tourne sur une machine virtuelle, ce qui le rend moins
4. Pour se faire une meilleure idée des performances de Java, un article discutant de ses performances
a été réalisé sur Wikipédia. Il peut être visité à cette adresse http://en.wikipedia.org/wiki/Java_
performance. Bien qu'il soit plutôt objectif, ce type d'article ne doit pas être considéré comme une réalité
absolue. La lecture de Statistically rigorous java performance evaluation [19] pourrait nous donner une
meilleure idée de ses performances
32
performant qu'un langage compilé nativement (Nous parlerons un peu plus en détail de
la machine virtuelle dans la partie suivante, portabilité).
Le but de notre programme est de créer une application temps réel, elle doit donc être
réactive. C'est pourquoi il est important d'utiliser un langage susamment rapide. En
eet, pour de petites applications, un langage interprété peut sure. Cependant, il pourrait se montrer insusant pour représenter un grand nombre de formes assez complexes et
traiter un grand nombre d'informations avant de pouvoir rafraîchir l'image comme c'est
le cas ici, puisqu'il est nécessaire de recalculer la position de l'utilisateur avant de pouvoir
la rafraichir. Pour éviter ce type de problèmes, nous préférons utiliser un langage compilé.
2.2.2.3 Portabilité
Un autre avantage non négligeable du Java est sa portabilité. Un langage portable
signie qu'il peut être executé sur diérentes machines en ne nécéssitant que peu, voir
pas de modications. La plupart des langages sont
adaptables, cela signie qu'ils peuvent
fonctionner sur plusieurs machines sans être nécessairement modiés, mais il est nécessaire de les recompiler à chaque fois qu'on change de machine. Cette contrainte n'est pas
présente dans le langage Java. Une fois traduit en bytecode le programme peut être exécuté sur n'importe quelle machine, quelque soit son système d'exploitation sans devoir le
recompiler. Cet avantage est dû à l'utilisation d'une machine virtuelle
Une machine virtuelle est un procédé permettant de simuler le travail d'une
machine de façon logicielle. Dans le cas de la machine virtuelle Java (JVM),
le programme Java est converti en un code compréhensible et exécutable par
cette machine virtuelle. De cette façon, un même code peut être utilisé sur
n'importe quelle couple machine/plateforme en donnant les mêmes résultats,
puisque le code est exécuté via la JVM. Chaque plateforme possède sa propre
JVM [16].
L'utilisation d'une machine virtuelle, comparée à un code natif entraîne une légère perte
de performances, toutefois, elles restent supérieures à celle d'un langage interprété.
Dans le cadre de notre projet, la portabilité est un atout important car elle nous assure
la compatibilité avec n'importe quelle machine (pourvue de Java). Le programme créé
pourra donc être utilisé sur n'importe quel type de machine, moyennant la compilation
des librairies C utilisées.
2.2.2.4 Orienté Objet
En permettant la programmation de type orienté objet (POO), le Java est un langage
qui permet de développer simplement, en considérant que chaque composant est un objet,
à l'exception des nombres, qui sont des types primitifs. De plus, le concept d'interface nous
est très utile pour permettre une bonne portabilité. Grâce à cette notion, notre module
contiendra une interface
HeadtrackedCamera
et une classe CameraHandler qui utilise un
objet implémentant l'interface HeadtrackedCamera pour diriger la caméra. Ces classes interagissent directement avec la Wiimote via le système de gestion d'évènements. À chaque
fois que le capteur infrarouge détecte un mouvement d'une source infrarouge, il pourra
33
le notier à la classe WiimoteHandler, qui se chargera à son tour d'appeller la méthode
handleEvent() de la calsse CameraHandler. La classe CameraHandler pourra alors modier les variables de position de la caméra contenues dans l'interface HeadtrackedCamera.
La POO permet donc de programmer tout en gardant une structure claire ou chaque
objet eectue un travail précis (concept de cohésion), cela permet une meilleure lisibilité
qu'un programme entièrement codé avec un paradigme procédural tel que le C.
Une fois le concept de POO bien maîtrisé, il est assez simple d'implémenter des programmes qui sont en réalité complexes, grâce aux concepts d'emboîtement et d'intéraction
entre les diérents objets qui seront créés. Pour réaliser une application de grande envergure, celui-ci peut être séparé en plusieurs modules distincts, dont chacun eectuera une
partie précise du travail demandé par l'application.
Pour en revenir à la gestion évènementielle, nous noterons que ce concept est largement
utilisé à diérents endroits de l'application. C'est le cas de la détection des mouvements
pour le headtracking. Lorsqu'un mouvement de la main est détecté, un appel à une méthode de l'interface MovementListener est alors automatiquement déclenché. La gestion
des bords de la caméra utilise elle aussi la gestion évènementielle. Lorsqu'une source infrarouge s'approche d'un des bords du capteur, un évènement est déclenché, qui provoque
la coloration du bord de l'écran correspondant au bord du capteur dont la source se
rapproche.
2.2.2.5 API 3D
En plus de toutes ces qualités, le Java est capable de travailler avec la célèbre librairie
3D OpenGL. Dans le monde de la 3D, il existe 2 grands noms, Direct3D et OpenGL. Ce
sont les deux librairies les plus avancées et les plus stables en la matière. La première librairie est directement liée au systeme d'exploitation Windows, elle est donc dépendante
d'un système d'exploitation payant et n'est pas multi-plateforme. La seconde librairie
comme son nom l'indique est open-source et possède une implémentation dans la plupart
des langages actuels. l'OpenGL est donc un choix qui s'impose si nous voulons une application stable, mais aussi utilisable par tous.
En Java, il existe aussi une librairie nommée Java3D, cette librairie est en fait un
binding qui fait appel à des fonctions OpenGL ou Direct3D, ce n'est donc pas une implémentation à part entière d'une librairie 3D propre à Java.
L'intégration de OpenGL dans le langage Java est possible en plaçant le contexte
OpenGL de la scène dans une fenêtre graphique de awt ou swing. Cela nous permettra
donc d'intégrer ce composant dans une interface graphique complète, réalisée avec l'api
swing. Cette interface graphique nous permettra de contrôler les nombreux paramètres
de l'application réalisée.
2.2.2.6 Perfectionnement du langage
La dernière motivation concernant le Java concerne l'opportunité de perfectionner un
langage déjà maîtrisé. En eet, nous avons appris à utiliser ce langage pour réaliser des
34
applications diverses.
L'utilisation d'une librairie graphique 3D est une des chose qui n'a pas encore été réalisée dans ce langage, cette occasion est donc intéressante pour découvrir les possibilités
du Java en termes de rendu en trois dimensions.
Enn, le développement de notre application demande une certaine richesse de programmation. Nous développerons des interfaces, utiliserons les concepts d'héritage et de
gestion évènementielle. Un ensemble de concept déjà connu qu'il nous sera une fois de
plus possible de perfectionner, grâce à la réalisation d'une application conséquente.
Remarquons que le Java n'est pas un silver bullet il a, comme tous les langages ces
petits défauts. Nous pourrions par exemple lui reprocher d'être très verbeux (la réalisation
d'un application simple demande parfois un grand nombre de lignes de code, pour faire
au nal peu de choses). Aussi, sa vitesse n'est en général pas comparable à celle des
autres langages compilés, à cause du bytecode généré. Cependant, sa portabilité et son
expressivité pèsent lourdement de l'autre coté de la balance.
Java n'est donc pas
le langage absolu pour réaliser notre application. D'autres langages
auraient pu convenir, comme le C++ par exemple.
2.2.3 Idées alternatives
Plutôt que de rechercher un langage qui nous permette de communiquer aisément avec
la librairie qui sera utilisée, il aurait été possible d'utiliser n'importe quel autre langage,
même s'il ne permet pas d'interagir directement avec la librairie.
Une solution simple aurait été de récupérer les données renvoyées par la librairie et de
les écrire dans un chier à chaque mouvement de l'utilisateur. En pratique cette solution
est tout à fait possible. Gardons toutefois en tête que notre application devra interagir en
temps réel avec l'utilisateur. Il est pour cela nécessaire que le temps de latence entre le
mouvement réel et le mouvement simulé soit minimal. Or la lecture/écriture sur un chier
est un procédé d'entrée/sortie dont la vitesse est de loin inférieure à une simple gestion
d'évènement en Java. Pour cette raison, cette solution a très vite été écartée au prot de
l'utilisation d'un seul langage pour réaliser l'ensemble de l'application.
Plutôt que de dialoguer entre diérents langages via des chiers, il est aussi possible
d'utiliser le procédé de
mémoire partagée
ou de
pipeline
entre le processus détectant les
mouvements sur le capteur infrarouges et celui s'occupant de traiter l'information reçue
pour simuler le déplacement de la tête dans une application 3D. Ce procédé est bien plus
rapide que la lecture écriture sur un chier puisque le pipelining s'exécute sur la mémoire
principale de l'ordinateur (la RAM). Cette solution n'a pas été retenue car sa mise en
place aurait pris un temps inutile, puisque l'interface JNI nous ore déjà tout ce dont
nous avons besoin.
35
Figure 2.12: Repère Wiimote/joueur/TV
2.3 Concepts théoriques
Dans cette section, nous détaillerons les concepts théoriques utilisés pour la mise en
oeuvre du headtracking et du handtracking. Nous discuterons notamment du modèle mathématique utilisé pour simuler les mouvements d'un utilisateur de façon réaliste, ansi que
des concepts théoriques inhérents à l'eet fenêtre. Nous eectuerons également quelques
rappels de mécanique théorique, qui nous serons utiles à la compréhension de l'implémentation du jeu de pong, en ce qui concerne la modélisation des mouvements de la balle de
tennis de table.
2.3.1 Concernant le headtracking
Commençons par détailler comment fonctionne le headtracking. Supposons que la
caméra infrarouge est placée sous l'écran, et qu'elle regarde dans une direction perpendiculaire à celui-ci, comme le montre la gure 2.12. Nous pouvons voir du dessus, en gris
foncé l'écran, en bleu la wiimote et son champ de vision, ainsi qu'en rouge les axes x et y.
Pour nos calculs, nous utilisons un repère orthonormé dont l'axe des abscisses est
36
l'axe x sur la gure, l'axe des ordonnées est celui des y, et l'axe des cotes correspond à
l'axe z, qui sort de l'écran. Le point (0,0,0) est situé là ou est plaçée notre caméra. l'axe
des x délimite donc la frontière entre le monde réel et le monde virtuel représenté à l'écran.
La première chose que nous devons faire est calculer la distance entre l'utilisateur et
la caméra. Supposons que celui-ci se trouve au point p. Comme expliqué à la section 2.1,
il y a deux sources infrarouges sur le capteur, correspondant chacune à une des deux
sources présente sur la paire de lunettes. Comme nous avons besoin d'un seul point, nous
considérons que le point p est le milieu des deux sources (juste entre les deux yeux). Pour
calculer la distance entre l'utilisateur et la caméra, nous pouvons utiliser le principe mathématique suivant :
Soit
Ω
un cercle de centre (0,0) et de rayon r (cf gure 2.13 ).
Considérons l'arc de cercle formé par deux rayons séparés par un angle
a une longueur
α∗r
α, cet arc de cercle
(alpha est en radians).
Si le rayon de ce cercle passe de r à 2r, alors la taille de l'arc de cercle passe de
α ∗ 2r.
α∗r
à
Sa taille est donc deux fois plus importante.
Figure 2.13: cercles et leurs arcs
Appliquons cette propriété à notre capteur. En réalité, la résolution qu'il peut observer
est xe, par conséquent, lorsqu'un objet s'éloigne d'une distance x à une distance 2x, cet
37
objet est vu sur un arc de cercle deux fois plus grand (l'arc de cercle est un arc virtuel
qui contient tous les points situés à une distance x du capteur). Comme notre capteur est
de taille xe, l'objet sera donc perçu comme ayant une taille deux fois plus petite. Cette
propriété nous permet donc d'établir que la distance entre les deux sources infrarouges
1
). De cette façon, si nous connaisdécroît selon une hyperbole parfaite (d'équation y =
x
sons la distance en millimètres entre les deux sources infrarouges, nous pouvons aisément
calculer à quelle distance se trouve l'utilisateur portant les lunettes. Pour ce faire, nous
devons connaître la distance en pixels entre nos sources infrarouge à une distance arbitraire de vingt centimètres. Ce calcul sert donc de base, puisque nous savons qu'à 40cm le
nombre de pixels entre les deux sources sera divisé par deux. Ce calcul doit bien entendu
être relativisé par la distance réelle qui sépare les leds. De cette façon, si nous utilisons un
dispositif d'émission d'infrarouges diérent, il sut de modier la valeur en millimètres
d'une variable contenant la distance entre les deux sources infrarouge.
À présent que nous savons calculer la distance entre les points, nous avons tout ce qu'il
nous faut pour calculer la position de l'utilisateur dans le plan. Soit p, cette position. Nous
la calculons comme suit :
Considérons le triangle rectangle formé par les points
est bien rectangle par construction puisque le point
np , p
np
et
o
(cf g 2.14). Ce triangle
est le projeté orthogonal de
p
sur
la normale du capteur (l'axe y). Pour calculer la position p, nous devons calculer l'angle
α.
Dans nos calculs, nous allons approximer la distance
de cercle formé entre
np
et
|np − p|
par la longueur de l'arc
p. Comme l'angle est assez petit (ne dépasse pas 20° de chaque
côté de la normale) l'erreur commise est assez faible.
Pour calculer cette erreur, nous devons calculer la diérence de longueur entre le rayon
du cercle de centre o et de rayon h et le coté adjacent à l'angle
formé par les points o, p et
np .
α
du triangle rectangle
Cette diérence de longueur est la diérence de longueur
entre l'hypothénuse et le coté adjacent à l'angle
Pour un triangle rectangle dont l'angle
α
de ce même triangle rectangle.
α vaut 21° et la longueur de l'hypothénuse vaut
1, la longueur du coté adjacent est de cos(21°)=0,933. L'erreur commise est donc inférieur
à 7%, si l'angle ne dépasse pas 21°.
En OpenGL, la plupart des représentations géométriques ne sont pas 100% correctes.
Nous pouvons donc estimer que ce facteur d'erreur ne nuira pas à la qualité du headtracking.
Cette erreur sera plus importante lorsque l'angle
α grandit, et donc la qualité du head-
tracking pourrait être légèrement détériorée avec des angles importants, si nous utilisons
plusieurs Wiimotes. Un facteur de correction pourra alors être introduit pour limiter cette
erreur. En fonction de l'angle formé entre la normale de la caméra et la position de l'utilisateur, nous pouvons calculer l'erreur commise, comme nous l'avons fait ci-dessus, et
introduire un facteur de correction au calcul de la position.
Pour calculer la valeur de l'angle horizontal
α,
formé entre la caméra et l'utilisatuer,
nous calculons dans le triangle virtuel représenté à la gure 2.15. Supposons que la caméra
possède un angle horizontal de 42°, cela signie qu'elle a un angle de 21° de chaque coté
de sa normale. comme le capteur a une taille horizontale de 1024 pixels, Nous pouvons
dire que
38
Figure 2.14: Triangle rectangle dans le champ de vision
39
Figure 2.15: Triangle virtuel
T an(21) =
512
x
et par conséquent,
x=
512
0.38
= 1347, 36
Le coté opposé de l'angle de 21° a donc une longueur de 1347,36.
Nous pouvons à présent calculer l'angle horizontal pour n'importe quel point p sur le
capteur (le point milieu des deux sources infrarouges). Considérons la distance en pixels
entre le centre du capteur (sa normale) et le point p, notons cette valeur
2.16). Il y a un angle
α
x
(cf gure
entre la normale et le coté formé en joignant p au point o.
Comme nous connaissons la valeur de x, ainsi que la valeur du coté opposé de l'angle
α
(longueur de 1347,36), nous pouvons calculer l'angle par la formule suivante :
x
).
α = Atan( 1347,36
Grâce à cette formule, nous connaissons la valeur de
α . Il est a présent aisé de calculer
la coordonnée x du point p grâce à la formule
sin(α) =
calculons
px ,
coté−opposé
hypothénuse
en utilisant cette formule :
px = hypothénuse ∗ sin(α)
40
Figure 2.16: Angle dans le triangle virtuel
41
où
hypothénuse
est la distance réelle calculée à partir de la distance entre les deux
sources infrarouges, et
α
est l'angle calculé à l'aide du triangle virtuel.
L'ensemble de ces calculs ayant été eectué pour la dimension horizontale, Le calcul
de l'angel vertical est eectué en modiant les valeurs qui distinguent le cas horizontal du
cas vertical (tel que l'angle, qui passe de 42° à 33°, et la taille du coté opposé du triangle
virtuel sera par conséquent diérente). Les résultats obtenus pourront être utilisés pour
calculer la coordonnée z du point p. C'est pourquoi nous ne détaillerons pas ces calculs
ici, et laissons le soin au lecteur de les eectuer s'il le souhaite.
2.3.2 Concernant le handtracking
Passons à présent au module handtracking. La partie de ce module permettant de déplacer des objets nécéssite quelques précisions quant à la méthode employée pour calculer
la position de la main, en utilisant une seule led.
L'inconvénient majeur, en utilisant une seule source infrarouge pour détecter la position de la main, est qu'il n'est pas possible (avec une seule Wiimote) de calculer la
distance à laquelle se trouve la main. Nous devrons donc utiliser un artice pour savoir
à quelle profondeur il faut placer la main. Celui-ci est en réalité très simple : l'utilisation
du handtracking se fait toujours en complément du headtracking. En eet, si le headtracking n'était pas utilisé, nous aurions pû utiliser deux sources infrarouges pour situer la
main dans l'espace. Comme nous savons calculer la position de la tête de l'utilisateur,
nous pouvons supposer que la main de celui-ci se trouve légèrement en avant par rapport
à sa tête. Lorsque nous souhaitons interagir avec les mains, nous les plaçons devant le
corps. Il sut alors de xer la position de la main à une distance de quelques dizaines
de centimètres en avant du visage. Cette valeur doit être choisie judicieusement pour que
l'eet rendu soit le plus réaliste possible.
Une fois la distance estimée, nous pouvons raisonner en utilisant une seule source
infrarouge, exactement de la même façon que pour détecter la tête avec le headtracking.
En eet, lorsque nous avons calculé dans le triangle rectangle formé par la normale de la
caméra, la distance caméra/utilisateur, et la distance caméra/normale, nous avons utilisé
un seul point qui était situé au milieu des deux sources infrarouge. Par conséquent, le
handtracking ne nécéssite pas l'introduction de concepts théoriques nouveaux, car nous
pouvons réutiliser ceux qui ont été introduits pour le headtracking.
2.3.3 Concernant l'eet fenêtre
Pour réaliser une application de headtracking mettant en oeuvre un eet fenêtre, c'est
à dire, une image projetée à l'écran dont la partie visible est modiée, en temps réel, en
fonction de la position de l'utilisateur par rapport à l'écran. L'écran se comporte alors
comme une fenêtre, au travers de laquelle une scène est visionnée.
La principale diculté de cette application consiste à calculer la partie de l'image qui
doit être visible, en fonction de la position de l'utilisateur par rapport à l'écran. Nous
expliquons ci-dessous comment ce calcul est eectué pour la dimension horizontale. Le
42
Figure 2.17: Schématisation de l'eet fenêtre
passage à la dimension verticale implique des calculs similaires que nous ne présenterons
pas dans ce document.
Considérons la gure 2.17, celle-ci représente une vue schématique du dessus, où l'utilisateur est représenté au point p, l'écran correspond au rectangle bleu, et la scène à acher
s'étend du point m au point n. Nous cherchons à déterminer la position des points A et
B.
Considérons la droite
d1
passant par le point p et coupant le bord gauche de l'écran.
Cette droite coupe la scène au point A. considérons également la droite
d2
qui passe par
le point p et coupant le bord droit de l'écran. Cette droite coupe la scène au point B. Nous
connaissons la distance qui sépare la scène de l'écran, cette distance est d. Nous savons
également que l'écran possède une largeur e, et que le point p se trouve aux coordonnées
(px , py ).
Le calcul du point A consiste donc à trouver l'intersection entre deux droite. La première de ces deux droites est la scène, dont l'équation est
y = d.
Pour trouver la position
du point A, il nous sut de calculer l'équation de la droite passant par le point p et le
e
bord gauche de l'écran situé en eright = (− , 0). L'équation d'une droite passant par deux
2
points étant :
y − ya=m(x−xa )
où
m=
(yb −ya )
(xb −xa )
Il nous sut alors de remplacer les valeurs par les données que nous connaissons pour
calculer l'équation de la droite A. Pour trouver la coordonnée x du point A, il nous reste
43
à remplacer y par d dans l'équation. Sachant que d est la coordonnée y du point A. Nous
obtenons alors :
x=
La coordonnée du point A est donc
d − py + m.px
m
(x, d). Nous calculons le point B de façon similaire.
Comme la droite formée par le point p et le point B passe en toute logique, elle aussi, par
le point p. La coordonnée du point B est
(x, d),
où x est calculé de la même façon que
pour A, mais avec un m diérent (puisque le second point correspond au bord droit de
e
l'écran, situé en ( , 0).
2
Après avoir calculé la position des points C et D, qui correspondent aux point A et
B, mais pour calculer la portion verticale visible de l'image, nous connaissons les quatres
bords visibles de l'image, et savons donc quelle partie de l'image acher.
2.3.4 Rappels de mécanique
Le dernier concept théorique que nous présenterons consiste en un très bref rappel des
équations de mouvements issues de la mécanique. Ces équations nous sont utiles lors de
la réalisation du jeu de pong, an de modéliser la trajectoire de la balle.
La première équation que nous utilisons est l'équation du mouvement rectiligne uniforme ou MRU. Cette formule caractérise un mobile se déplaçant à une vitesse constante.
L'équation du MRU est la suivante :
pos(t) = v0 .t + x0
Où
v0
correspond à la vitesse initiale, t correspond au temps, et
x0
est la position
initiale du mobile. Cette formule est utilisée pour caractériser le déplacement latéral et
longitudinal de la balle de tennis de table. En eet, lorsque la balle est envoyée vers le
joueur opposé avec un angle horizontal
α,
la balle se déplace à vitesse constante dans la
direction correspondant à l'angle. La vitesse du mobile selon l'axe des x (horizontal) est
alors
v0x = v0 .cos(α)
Le déplacement longitudinal de la balle est lui aussi caractérisé par un MRU. Si la
balle est envoyée avec un angle vertical
β,
la vitesse selon l'axe y (longitudinal) est :
v0y = v0 .cos(β)
La seconde équation est utilisée pour caractériser le mouvement de la balle selon le
dernier axe, l'axe z, qui correspond à la verticale. Cet axe étant soumis à la gravité, une
équation de type MRU ne sut pas, nous devons utiliser une équation de mouvement
rectiligne uniformément
accéléré
ou MRUA dont l'équation est la suivante :
1
pos(t) = a.t2 + v0 .t + x0
2
44
Le facteur a correspondant à l'accélération du mobile, qui aura une valeur de
-9,81
dans notre cas, puisque l'accélération subie par le mobile est la gravité terrestre.
Grâce à ces deux équations, nous sommes a présent capables de calculer le mouvement
de la balle de tennis de table, durant une partie, entre chaque rebond. Lorsqu'un rebond
a lieu, la balle est simplement relancée à partir d'une nouvelle position initiale, avec un
angle vertical égal à l'angle d'arrivée de la balle sur la table, juste avant le rebond. L'angle
horizontal est quant à lui conservé, car aucun eet n'est donné à la balle.
45
Chapitre 3
Implémentation du module
A présent que nous maîtrisons les concepts théoriques nécessaires à une bonne compréhension du problème, nous pouvons détailler l'implémentation de chaque partie des
modules. Dans la suite de ce chapitre, nous présenterons un aperçu général des classes
créées avant de détailler les liens présents entre les parties les plus importantes. Nous détaillerons ensuite les classes et interfaces qui jouent un rôlé clé dans chacun des modules
développés.
3.1 Vue d'ensemble
Comme l'a dit un jour un Napoléon :
Un bon croquis vaut mieux qu'un long discours.
Typiquement, en informatique, la présence d'un diagramme de classe permet de faciliter
la compréhension d'un programme. Grâce à ce type de schéma, nous pouvons avoir une
vue de haut niveau des classes, de leur interface publique, ainsi que de liens qui les unissent.
Le schéma de la gure 3.1 nous montre un diagramme de classe épuré contenant chacune des classes ayant un rôle important dans la réalisation de notre application. Les
interfaces sont représentées en bleu, et les classes en beige. Par soucis de lisibilité nous
n'avons intégré que les signatures de méthodes importantes dans la représentation des
classes. Les attributs ne sont pas représentés.
Cette gure nous montre que quatre interfaces sont présentes dans notre projet, ces
interfaces seront expliquées dans les sections 3.3.1, 3.4.1, 3.5.1 et 3.5.3. Trois de ces quatre
interfaces jouent un rôle clé dans la portabilité de l'application. Ce sont ces interfaces qui
permettent à un développeur d'utiliser notre module, en respectant les signatures de méthodes qui y sont présentes.
Au niveau des classes, nous pouvons séparer l'ensemble des classes présentes en trois
groupes distincts :
1. Les classes qui concernent la gestion du headtracking : CameraHandler, ShakeDetector, BorderPainter, Windowed View et Camera
2. Les classes qui correspondent au handtracking : HandMovementsDetector, HandHandler
46
Figure 3.1: Diagramme de classe (nettoyé) des modules
3. Les classes qui gèrent l'interface avec la Wiimote : WiimoteHand et WiimoteHeadAndHand, qui héritent de WiimoteHandler
3.2 Utilisation de la librairie
Avant de détailler davantage l'implémentations des modules, il est nécessaire de savoir comment utiliser la librairie WiiuseJ, qui permet de communiquer avec la Wiimote.
Comme nous n'utilisons que la caméra infrarouge de la Wiimote, l'utilisation de la librairie est assez simple.
Pour détecter les mouvements des sources infrarouges sur le capteur, il faut créer une
classe qui implémente l'interface WiimoteListener. Cette interface contient des signatures
de méthodes qui sont automatiquement appelées lorsqu'un évènement issu de la Wiimote
est déclenché. Une seule méthode de l'interface WiimoteListener nous intéresse, il s'agit
de
onIrEvent(IREvent ir).
Cette méthode est automatiquement appelée lorsqu'un mou-
vement est détecté sur le capteur infrarouge de la Wiimote. Les informations concernant
IREvent. L'utilisation de la méthode
ir.getIRPoints() permet alors de récupérer un tableau de sources infrarouges, duquel nous
cet évènement sont contenues dans un objet de type
pouvons extraire les coordonnées des sources infrarouges sur le capteur de la Wiimote.
La connexion d'une Wiimote est eectuée à partir de la classe WiiUseApiManager.
Les commandes à saisir pour connecter une Wiimote, activer sa caméra infrarouge et la
lier au WiimoteListener sont dénies ci-dessous :
47
try{
Wiimote[] wiimotes = WiiUseApiManager.getWiimotes(1, true) ;
wiimote = wiimotes[0] ;
wiimote.activateIRTRacking() ;
wiimote.addWiiMoteEventListeners(this) ;
}catch(Exception e){
e.printStackTrace() ;
}
Ces commandes doivent se situer dans un bloc
try car elles sont susceptibles de renvoyer
une exception si la connexion échoue.
3.3 Module headtracking
Maintenant que nous savons comment utiliser la librairie, nous pouvons détailler l'implémentation du module headtracking. Dans la suite de cette section, nous exposerons les
classes importantes du module headtracking, et développerons les méthodes importantes
qui sont propres à chaque classe.
3.3.1 HeadtrackedCamera
En termes de réutilisabilité, une bonne pratique est d'utiliser les Interfaces de Java.
Celles-ci dénissent un ensemble de signatures de méthodes qui devront être respectées
par les classes qui l'implémentent.
Développer un système de headtracking réutilisable demande donc d'identier les actions desquelles dépendent la position de la tête dans l'espace. Nous savons que le headtracking consiste à déplacer la caméra virtuelle, présente dans la scène OpenGL, en accord
avec les mouvements du visage de l'utilisateur. Cela signie que les paramètres dont dépendent le headtracking concernent la position de cette caméra.
Nous avons donc développé une interface HeadtrackedCamera dont le but est de piloter la caméra OpenGL. Cette interface est composée de 24 signatures de méthodes (12
fonctions pour récupérer la valeur de chaque variable, 12 autres pour les modier). 18 de
ces fonctions correspondent à un paramètre de la fonction GluLookAt() d'OpenGL. Pour
information, la fonction GluLookAt() est une primitive qui permet de contrôler la caméra
grâce aux 9 paramètres suivants. A chacun de ces paramètres, nous avons associé, en bleu,
la signature de l'interface HeadtrackedCamera qui permettra de piloter le paramètre.
1. cameraX - setCameraX(double x) / getCameraX() : permet de positionner la caméra
à la position x sur l'axe des abscisses du repère de la scène
2. cameraY - setCameraY(double y) / getCameraY() : permet de positionner la caméra
à la position y sur l'axe des ordonnées du repère de la scène
3. cameraZ - setCameraZ(double z) / getCameraZ() : permet de positionner la caméra
à la position z sur l'axe des cotes du repère de la scène
48
4. eyeX - setCameraXLook(double x) / getCameraXLook() : permet de diriger la caméra pour qu'elle regarde en abscisse x
5. eyeY - setCameraYLook(double y) / getCameraYLook() : permet de diriger la caméra pour qu'elle regarde en ordonnée y
6. eyeZ - setCameraZLook(double z) / getCameraZLook() : permet de diriger la caméra
pour qu'elle regarde en cote z
7. upX - setUpX(double x) / getUpX() : upX, upY et upZ permettent de dénir
le vecteur vertical. Si nous voulons que l'axe des cotes soit notre verticale, alors
nous donnerons respectivement les valeurs 0,0 et 1 à upX, upY et upZ. Dans notre
application, la convention est d'utiliser l'axe des cotes (celui des z) comme verticale.
8. upY - setUpY(double y) / getUpY()
9. upZ - setUpZ(double z) / getUpZ()
Grâce à ces paramètres, nous pouvons donc contrôler tout ce qui concerne la caméra dans
un monde en 3 dimensions.
Les six dernières signatures de méthode concernent la position initiale de la caméra,
qui doit être modiable, pour s'adapter à n'importe quel type de scène. L'utilisation de ces
paramètres permet également de déplacer la Caméra indépendamment du headtracking.
Il est alors possible de déplacer la caméra avec une manette (ou en utilisant le clavier),
sur de grandes distances, et en même temps d'utiliser le headtracking pour déplacer la
caméra sur une distance plus courte. Les méthodes permettant de déplacer la caméra
indépendamment du headtracking sont les suivantes :
1. getInitX() / setInitX(double x)
2. getInitY() / setInitY(double y)
3. getInitZ() / setInitZ(double z)
Ces six signatures permettent d'accéder et de modier la position initiale de la caméra.
Cette position initiale sera utilisée comme origine pour le placement de la caméra, en
utilisant le headtracking.
Parlons à présent des conventions. Comme nous l'avons remarqué, les 3 derniers paramètres de la fonction GluLookAt() nous permettent de dénir notre axe vertical. C'est
donc au programmeur de choisir ses propres conventions. Toutes les applications que nous
développerons utiliseront les conventions suivantes :
l'axe X est celui qui permet de se déplacer de gauche à droite par rapport à l'écran,
l'axe Y s'enfonce dans l'écran, et permet donc d'avancer ou de reculer,
l'axe Z représente la verticale et donc la hauteur de la scène.
Si une application ne respecte pas ces conventions, il sera alors nécessaire d'intervertir les
méthodes pour qu'elles correspondent aux conventions du module.
3.3.2 Camera
L'interface HeadtrackedCamera est très riche en termes de nombre de signatures. Cela
est dû aux nombreux paramètres dont dépendent la position de la caméra. Pour limiter le
49
nombre de signatures, nous aurions pu renvoyer les données par triplets, contenant ainsi
les données correspondant aux trois axes. Toutefois, l'encapsulation des données peut se
révéler peu ecace, dans le cas où une seule des trois données doit être modiée. La
présence d'un grand nombre de méthodes est donc un choix qui permet à l'utilisateur
potentiel du module de cibler les appels, limitant ainsi l'overhead dû à une encapsulation
trop importante.
Lorsqu'une scène OpenGL est créée, elle doit alors implémenter l'interface HeadtrackedCamera. Pour améliorer la structure du code et éviter d'être contraints d'ajouter
plus de 20 signatures de méthodes dans la classe représentant la scène, nous avons créé
une classe Camera qui implémente l'interface HeadtrackedCamera. Douze variables d'instances correspondant aux douze accessors et mutators ont été ajoutées dans cette classe.
De cette façon, il sut de créer un objet de type Camera dans la scène OpenGL, et d'accéder à ses variables via les méthodes de l'interface HeadtrackedCamera.
L'utilisation de la classe Camera n'est pas nécessaire. Il est toujours possible d'utiliser
directement l'interface HeadtrackedCamera dans l'application développée.
3.3.3 CameraHandler
Le coeur du module de headtracking est développé dans la classe CameraHandler. Son
travail est de récupérer les coordonnées des sources infrarouges issues de la Wiimote, et
de calculer la position de l'utilisateur par rapport à celle-ci en fonction de la position
des sources sur le capteur. Une fois la position calculée, elle met à jour la position de la
caméra qui a été préalablement passée à la construction de l'objet CameraHandler. Cette
classe s'occupe également de gérer les intéractions au clavier, elle possède donc un lien
avec la classe WiimoteHeadAndHand pour lui permettre de connecter une Wiimote ou
d'activer la caméra infrarouge en appuyant sur les touches
R
et
I.
La gestion du clavier est assurée par KeyListener, l'interface par défaut de Java pour
eectuer ce type de travail. Implémenter cette interface nous permet de paramétrer notre
scène grâce à l'appui sur certaines touches, donnant ainsi une plus grande liberté à l'utilisateur. Elle est composée de 3 méthodes, dont une seule nous servira, il s'agit de
Pressed(KeyEvent e)
key-
qui est déclenchée lors de l'appui sur une touche du clavier. Cette
interface est utilisée pour modier les paramètres suivants :
Modication de la position initiale de la caméra, qui permet d'ajuster la position
initiale de la caméra pour tenir compte par exemple de la taille de l'utilisaeur, ou
encore de sa position initiale dans la scène.
Modication du point de vue, qui permet de déplacer le point observé par la caméra.
Modication de la sensibilité des déplacements et du headtracking, qui permet d'intensier ou de diminuer l'amplitude des mouvements de la tête de l'utilisateur dans
l'application. Cela peut-être utile si l'utilisateur souhaite calibrer l'intensité des mouvements pour les adapter à l'espace dont il dispose.
Connexion de la Wiimote et activation de la caméra infrarouge.
La plus grande partie du travail de headtracking se déroule à l'intérieur de la méthode
handleEvent(IRSource[] ir). Lorsque l'utilisateur se déplace avec les lunettes sur les yeux,
50
il déclenche un évènement qui appelle la méthode onIREvent(IREvent ir) de la classe
implémentant l'interface WiimoteListener. Cette méthode se charge alors d'appeler handleEvent(), qui calcule la position de l'utilisateur par rapport à la caméra en se basant sur
les sources infrarouges passées en paramètre de la méthode onIREvent.
Si la détection des bords est activée, alors la méthode handleEvent se charge d'appeler
la fonction handleBorders() dont le travail est de déclencher un évènement lorsque les
sources infrarouges sont trop proches du capteur. Le calcul de cette proximité s'eectue
sur base de paramètres modiables, de façon à pouvoir personnaliser la sensibilité de la
détection des bords.
Supposons que nous avons deux points infrarouges situés de part et d'autre de la paire
de lunettes. Nous pouvons calculer la distance entre la caméra et la tête de l'utilisateur
en utilisant les concepts vus dans la section 2.3. Nous savons que la distance entre les
deux points suit une loi de type Hyperbolique lorsqu'on s'éloigne ou se rapproche de la
caméra, il est donc aisé de calculer la distance entre la tête et la caméra en se basant
sur la distance entre les deux sources infrarouges. Ce calcul est eectué par la méthode
getRealDistance(x1, y1, x2, y2) de la classe
3DMath, qui utilise les deux points détectés
(x1,y1) et (x2,y2) pour calculer la distance.
Une fois la distance calculée, nous calculons l'angle horizontal et vertical entre la normale de la caméra (située au centre de celle-ci) et la position courante de la tête (le point
qui représente la tête est situé au milieu des deux sources infrarouges). Nous pouvons utiliser ces informations pour calculer la position exacte de la tête dans notre monde virtuel,
grâce aux principes des trigonométries sur le triangle rectangle évoqués à la section 2.3.
Sur base de toutes ces informations, les variables contenues dans l'objet Camera sont
1
mises à jour, et lors du prochain appel par Java à la fonction Display() , la fonction
GluLookAt() sera appelée avec ces nouveaux paramètres modiés. Il en résultera donc un
déplacement de la caméra sur la scène, reproduisant le mouvement eectué par l'utilisateur.
3.3.4 WiimoteHandler
Pour gérer les intéractions avec la Wiimote, la classe WiimoteHandler a été implémentée. Il s'agit d'une classe générique qui implémente l'interface WiimoteListener. Toutes les
méthodes de cette interface y sont donc dénies. La principale utilité de cette classe est la
présence de deux fonctions qui sont reactivateIRTrack() et reconnectWiimote(). Ces deux
méthodes permettent respectivement, comme leur nom l'indique, de réactiver la caméra
infrarouge de la Wiimote connectée (car l'activation ne fonctionne pas toujours) et de
reconnecter la Wiimote, lorsque la connexion a échoué.
1. Pour information, la fonction Display est une fonction appellée en boucle par java de l'interface
GLEventListener qui permet de dessiner la scène en OpenGL. Pour plus de détails, consulter la documentation de Jogl [14].
51
Deux classes plus spéciques héritent de WiimoteHandler, il s'agit de WiimoteHeadAndHand et de WiimoteHand.
3.3.4.1 WiimoteHeadAndHand
Le but de cette classe est de gérer les évènements infrarouges captés par la Wiimote
et d'envoyer les informations concernant l'évènement aux classes qui se chargeront de calculer la position de la caméra et, éventuellement, de la main.
WiimoteHeadAndHand, il faut passer un CameraHandler, et éventuellement un HandHandler. Comme nous l'avons précisé plus haut,
cette classe hérite de WiimoteHandler. Elle implémente donc l'interface WiimoteListener,
Lors de la construction d'un objet de type
par transitivité. Pour donner un comportement plus spécique à la méthode onIREvent(),
nous l'avons redénie dans cette classe. Son travail est très simple, elle est chargée d'appeler les méthodes handleEvent() de l'objet de type CamerHandler passé en paramètre, et
handleEventSimply() de l'objet de type HandHandler éventuellement passé en paramètre.
Eventuellement, car le passage d'un HandHandler est optionnel. Deux constructeurs sont
dénis dans la classe WiimoteHeadAndHand, le premier a déjà été cité, et le second ne
nécessite pas de HandHandler. Lorsque l'appel à la fonction onIREvent() est déclenché
par un évènement infrarouge, la présence d'un HandHandler est testée. S'il n'a pas été
passé lors de la construction alors l'appel à la fonction handleEventSimply() n'est pas
eectué.
3.3.4.2 WiimoteHand
WiimoteHandler, est lié au handtraWiimoteHand, il faut lui passer en paramètre
du constructeur un HandMovementsDetector, dont le but est de détecter les mouvements
Le comportement de cette classe, qui hérite de
cking. Pour construire un objet de type
simples de la main (voir section 3.5.3). Tout comme sa soeur, la classe WiimoteHand surcharge la méthode onIREvent() pour lui donner un comportement plus spécique. Cette
fonction est chargée d'appeler la méthode HandleEvent() de l'objet HandMovementsDetector passé en paramètre du constructeur.
3.3.4.3 Utilité du WiimoteHandler
Nous l'avons compris, les classes héritant de WiimoteHandler servent de relais entre la
librairie WiiuseJ et notre module. C'est ce relais qui rend notre module réutilisable, car il
ne dépend pas directement de la librairie utilisée. De cette façon, il est possible d'utiliser
une autre librairie pour récupérer les sources infrarouges sur le capteur de la Wiimote. Il
est également possible d'utiliser un autre type de matériel, comme une webcam ou une
caméra stéréoscopique pour eectuer la détection. Tout ce que le module a besoin, c'est
de récupérer deux sources ponctuelles placées au niveau de la tête, peu importe la façon
dont ces sources sont obtenues.
52
3.3.5 3DMath
Cette dernière classe dépendante du module headtracking, regroupe toutes les fonctions statiques qui sont utilisées pour eectuer des calculs sur les données. L'utilisation
d'une classe auxiliaire pour les fonctions annexe donne une meilleure structure au code,
car tout ce qui peut être séparé de la gestion de la wiimote ou du headtracking se trouve
dans cette classe. Nous retrouvons par exemple les fonctions suivantes :
distance(double x1, double y1, double x2, double y2), dont le but est de calculer la
distance euclidienne entre deux points (x1,y1) et (x2,y2).
realdist(double x1, double y1, double x2, double y2), qui calcule la distance réelle à
laquelle se trouve l'utilisateur par rapport au dispositif de capture, en fonction de
la position des deux sources infrarouges (x1,y1) et (x2,y2).
getMilieu(IRSource[] s), permet de calculer le point milieu entre deux sources infrarouges. Ce point sera alors utilisé pour représenter la position de la tête sur le
capteur (la présence de deux points n'est utile que pour le calcul de la distance, et
de l'inclinaison de l'utilisateur).
nearestFromBorderH(IRSource[] ir) / nearestFromBorderH(IRSource[] ir), qui se
chargent de déterminer, parmi les sources infrarouges quel est le point le plus proche
d'un des bords horizontal ou vertical. Cette fonction est utile pour la gestion des
angles.
calcNormal(XYZ p, XYZ p1, XYZ p2), qui calcule la normale entre les deux vecteurs
v1=(p,p1) et v2=(p,p2). Utilisé dans la génération des formes en 3D.
normalise(XYZ p), qui permet de normaliser un point p=(x,y,z).
3.4 Fonctionnalités étendues
Pour améliorer le concept de headtracking, une série de fonctionnalités additionnelles
ont été développées. Ces fonctionnalités ont pour but d'améliorer la qualité des modules,
en fournissant des méthodes qui détectent les sorties du champ de vision de la caméra,
ou qui limitent les tremblements de l'image lorsque l'utlisateur se trouve à une distance
plus importante de la caméra.
3.4.1 Gestion des angles
Lors de l'utilisation d'une application qui met en pratique le concept de headtracking,
nous avons remarqué qu'il arrive très souvent à l'utilisateur de sortir du champ de vision
de la caméra. Rappelons que notre capteur possède un champ de vision assez peu étendu.
Pour remédier à ce problème, nous avons ajouté à notre module une fonctionnalité permettant d'éviter de sortir du champ de vision sans s'en rendre compte.
Pour pallier au problème de sortie du champ de vision, nous avons procédé de la façon
suivante : lorsqu'un mouvement est détecté sur le capteur infrarouge et que la méthode
handleEvent() de la classe CameraHandler est appelée, les sources infrarouges récupérées
sont passées à des fonctions qui se chargent de calculer le point le plus proche de chaque
bord. Nous calculons ensuite la diérence en pixels entre chaque bord et le point le plus
proche de celui-ci. Cette valeur est comparée à un seuil variable dont la valeur par défaut
53
est de 150 pixels. Si la distance est inférieure à ce seuil, alors un évènement de type AngleEvent est généré par la méthode handleEvent(). L'évènement généré déclenche alors
l'appel d'une méthode de l'interface AngleEventListener.
C'est La classe BorderPainter qui se charge de récupérer cet évènement car c'est elle
qui implémente l'interface AngleEventListener. Les deux méthodes de cette interface sont
angleLimitReached(AngleEvent ae) et AngleLimitLeaved(AngleEvent ae). La première
méthode est appelée à chaque fois qu'une source est trop proche d'un bord du capteur.
Dans l'objet de type AngleEvent, sont stockées des informations sur la proximité des
points par rapport aux bords. De cette façon, plus un point se rapproche du bord, plus
le bord correspondant de l'écran sera peint d'un rouge vif.
Par exemple, si le seuil est de 150 pixels, et que le point le plus proche d'un des bords
est à 140 pixels de ce bord, celui-ci sera peint dans un rouge très pale (transparent), qui
s'intensiera à mesure que la source infrarouge se rapproche du bord du capteur. De cette
façon, l'utilisateur sera capable de contrôler en temps réel sa position pour éviter de sortir
du champ de vision grâce à ce que nous appellerons la détection des bords du capteur.
3.4.2 Gestion des tremblements
Une observation faite lors de l'utilisation de notre module, est que nous observons des
tremblements de la part de la caméra virtuelle lorsque l'utilisateur se trouve à plus de
deux mètres de la caméra. Ceci s'explique par le fait que lorsque l'utilisateur s'éloigne
de la caméra, la distance en pixel entre les deux sources infrarouges devient de plus en
plus faible, et une diérence de un ou deux pixels correspond à un mouvement de 10
à 20 centimètres de l'utilisateur. De plus, il est naturel que la tête d'utilisateur debout
oscille en permanence de quelques centimètres, ce mouvement est retranscrit sur le capteur infrarouge, et la source perçue se déplace alternativement d'un pixel à l'autre sur le
capteur. Pour éviter ce problème, nous devrons mettre en place un système de détection
de tremblements.
Deux solutions ont été implémentées pour limiter ce phénomène de tremblement. La
première solution utilise une boite pour limiter les tremblements lorsque l'utilisateur ne
bouge pas, alors la seconde consiste en une interpolation des précédentes positions qui est
alors eective à tout moment.
3.4.2.1 ShakeDetector
La première solution mise en place permet de limiter les tremblements de la caméra
lorsque l'utilisateur ne bouge plus. Les mouvements de tremblements dans l'image étant
moins gênants lorsque l'utlisateur se déplace. Le système mis en place à été implémenté
dans une classe appelée
ShakeDetector. Le principe utilisé par cette classe est très simple.
Si l'utilisateur reste pendant quelques dixièmes de seconde sans se déplacer, nous xons
sa position. Plus aucun mouvement n'est alors eectué par la caméra, empêchant alors les
erreurs de calculs et les tremblements qui s'en suivent d'apparaître à l'écran. La principale
diculté consiste alors à savoir quand le mouvement peut reprendre.
54
Détecter l'absence de mouvements
Pour détecter l'absence de mouvements, nous
avons procédé de la sorte. Si la position de l'utilisateur n'a changé que de quelques centimètre (nous avons choisi arbitrairement 5 centimètres) durant les 200 dernières millisecondes, nous pouvons considérer qu'il y a absence de mouvements. Une variable est
alors placée à true pour indiquer qu'il n'y a pas eu de mouvements les 200 dernières
millisecondes.
Chacune des variables présentée ci-dessus a été choisie avec soin car leur utilisation
donnait un résultat assez bon. Toutefois, ces valeurs sont tout à fait modiables, car elles
ont été plaçées dans des variables, qu'il est possible de modier.
Détecter la reprise de mouvements
la partie la plus compliquée consiste à savoir
quand le mouvement peut reprendre. Si nous bloquons les mouvements après 200 millisecondes sans mouvement important, il est nécessaire de pouvoir reprendre le mouvement
lorsque l'utilisateur se remet à bouger de façon signicative. Nous devons donc dénir la
notion de bouger de façon signicative.
Nous pouvons considérer que la tête peut osciller d'une dizaine de centimètres lorsque
nous sommes debout. Un mouvement d'une amplitude supérieure doit alors être considéré
comme une intention de reprendre le headtracking. Pour mettre en place ce principe, nous
plaçons chaque source infrarouge dans une boite virtuelle lorsque l'absence de mouvement
est détectée. Cette boite est de forme circulaire et possède un rayon calculé de telle sorte
que sa taille en pixel équivaut à un mouvement d'environ dix centimètres. En eet, si
l'utilisateur se trouve à cinquante centimètres du capteur, un mouvement de dix centimètres correspond à un déplacement de environ cent pixels sur le capteur. A deux mètres,
le même mouvement correspond à un déplacement quatre fois inférieur des sources sur le
capteur.
Lorsqu'une des deux source infrarouge dépasse de la boite, celle-ci est immédiatement
retirée et le headtracking est remis en fonction.
3.4.2.2 Interpolation des précédents évènements
Bien que les tremblements de la caméra soient moins gênants durant le déplacement
de la caméra, ceux-ci entraînent tout de même un certain inconfort à l'utilisation. An de
limiter ce phénomène de tremblement qui se passe presque exclusivement sur la profondeur
(l'erreur de calcul est majoritairement commise sur le calcul de la distance. Le calcul de
la position horizontale ou verticale ne soure pas de ce problème car il sut de calculer
la position de la source sur le capteur pour calculer l'angle par rapport à la normale.
Ce calcul n'est donc pas dépendant d'une loi hyperbolique) nous gardons en mémoire les
vingt dernières positions calculée de l'utilisateur sur l'axe de profondeur, et nous intégrons
ces valeurs dans le calcul de la nouvelle position. Nous avons choisi une valeur de vingt
positions précédentes car une valeur inférieure ne lisse pas susament le déplacement,
alors qu'une valeur supérieure le rend trop peu réactif, car trop la position de la caméra
dépend de trop de positions précédentes. La formule utilisée pour le lissage est basée sur
une formule souvent utilisé en réseaux pour l'estimation du Round Trip Time
2
:
2. Le round trip time ou RTT correspond au temps qui s'écoule entre l'envoi d'un segment TCP et
son acquittement.
55
EstimatedP osition = (1 − α) ∗ EstimatedP osition + α ∗ newP osition
Dans notre cas, nous avons choisi
α = 0.5,
et
EstimatedPosition
situé à l'intérieur de
l'aectation a été remplacé par la moyenne des 20 précédentes positions. De cette façon,
nous donnons un poids assez important aux anciennes valeurs de la position.
3.4.3 Gestion de plusieurs Wiimotes
Nous avons déjà remarqué que l'angle de vue du capteur que nous utilisons est assez
faible (environ 40° horizontalement et 35° verticalement). Pour permettre une plus grande
liberté à l'utilisateur, Il pourrait être intéressant d'améliorer notre module an qu'il puisse
prendre en charge plusieurs caméras. Cette amélioration n'a pas été implémentée, mais
elle a été le sujet d'une réexion dont nous exposerons les conclusions ci-dessous.
Les caméras utilisées devront être plaçées l'une à côté de l'autre (ou l'une en dessous
de l'autre, si nous souhaitons élargir le champ de vision vertical) et regarder dans des
directions diérentes de sorte que l'angle obtenu par l'ensemble des caméras connectées
soit plus important qu'avec une seule. De plus, il faut que chaque caméra possède un
recouvrement de quelques degrés avec ses caméras voisines (celles dont le champ de vision
est juste à gauche et/ou à droite de celle-ci). Ce recouvrement est nécessaire pour éviter
d'avoir des angles non couverts, et donc de ne pas pouvoir détecter une source d'infrarouges dans cet angle mort.
Pour mettre en oeuvre cet ajout, une solution simple serait de raisonner en utilisant le
même principe que les ordinateurs connectés à plusieurs écrans, avec pour seul diérence
qu'un léger recouvrement est nécessaire. Lorsqu'une machine est connectée à plusieurs
écrans, et que l'achage n'est pas dupliqué, une résolution virtuelle est utilisée. Si les
deux écrans possèdent une résolution de 1024x768 pixels, et qu'il sont plaçés l'un a côté
de l'autre, l'ordinateur peut créer un écran virtuel ayant une résolution de 2048x768 pixels.
Notre gestion de plusieurs Wiimotes peut fonctionner de la même façon. Comme nous
connaissons la résolution du capteur, il sut de connaître la position d'une caméra par
rapport à l'autre, ainsi que le recouvrement, pour pouvoir calculer cette résolution virtuelle. Ce calcul peut se faire via un étalonnage. Au lancement de l'application, nous
pouvons demander à l'utilisateur de déplacer une source infrarouge à une vitesse relativement lente de la gauche vers la droite (en supposant que les deux capteurs sont positionnés
pour étendre le champ de vision horizontal), pour passer d'une caméra à l'autre et ainsi
calculer le recouvrement existant entre les deux caméras. Ensuite, il ne reste plus qu'à
eectuer les calculs pour connaître l'angle total couvert par les deux Wiimotes. Ce calcul
est assez simple.
Puisqu'une Wiimote possède un angle vertical de 42° et une résolution horizontale de
1024 pixels, chaque pixel compte pour 0,04101°. La résolution totale des deux caméras est
alors :
2 ∗ 42 − 0, 04101 ∗ recouvrement
56
où
recouvrement
correspond à la largeur en pixel de la zone couverte par les deux
Wiimotes.
Un problème se pose alors. Supposons que les deux Wiimotes ne sont pas orientées
exactement de la même façon. Le recouvrement en pixel entre les deux Wiimotes est
alors diérent au dessus du capteur par rapport au recouvrement présent dans le bas du
capteur. Deux solution sont envisageables, la première, très simple consiste à s'assurer
que les deux Wiimotes sont bien plaçées. La seconde méthode, plus technique, consiste à
eectuer deux étalonnages, un sur la partie supérieure du capteur, et l'autre sur la partie inférieure. Il faut ensuite calculer la droite qui passe par les deux points de jonction
entre les deux caméras. Cette sorte de régression linéaire nous permettra alors de calculer
l'overlap correct entre les deux capteurs, et la résolution virtuelle pourra être ajustée en
conséquence.
Dans tous les cas, la première technique est préférable, et la seconde ne sert que de
roue de secours, dans le cas où la diérence d'overlap entre le haut et le bas du capteur
n'excède pas quelques pixels. Si cet diérence est trop grande, la mauvaise orientation
des caméras risque de poser un problème. En eet, lors d'un déplacement parfaitement
horizontal de l'utilisateur, la source infrarouge sur le capteur ne sera pas toujours située à
la même hauteur, si le capteur n'est pas positionné de façon horizontale. Les calculs sont
alors faussés, et la qualité du headtracking est dégradée.
Pour améliorer l'angle de vision supporté par la Wiimote, une autre solution est possible. Nous pourrions placer la caméra sur un socle mobile, qui entraine une rotation de la
caméra (grâce à un servomoteur) lorsque les sources se rapprochent des bords du capteur.
La solution n'est techniquement pas dicile à mettre en place, en revanche, calculer avec
précision la rotation eectuée par le servomoteur est un problème plus délicat qu'il ne
faut pas négliger. Cette solution mérite tout de même d'être étudiée, et éventuellement
testée, pour améliorer l'angle de vision, sans pour autaut utiliser une seconde Wiimote.
3.4.4 3D stéréoscopique
Un ajout intéressant à notre module concerne la gestion de la 3D
stéréoscopique.
La
stéréoscopie est un procédé utilisé en photographie ou en vidéo permettant de percevoir
un relief en 3 dimensions à partir de deux images planes. En eet, notre vision du monde
en 3 dimensions est due à nos deux yeux. Chaque oeil voit une image légèrement décalée
par rapport à l'autre, et notre cerveau rassemble ces deux images pour nous permettre de
distinguer la profondeur . Dans notre cas, à partir de lunette anaglyphes (cf section 2.1)
et d'une image composée de deux images surperposées (destinées chacune à un oeil) nous
pouvons faire apparaître cet eet de profondeur dans notre application. Ces deux images
superposées devront présenter un décallage cohérent ainsi que des couleurs complémentaires correspondant aux couleurs des ltres présents sur les lunettes.
Pour développer une application qui génère des images stéréoscopique, une marche à
suivre bien précise doit être suivie. Il est bon de noter que toute scène utilisant la primitive gluLookAt() pour positionner la caméra peut être transformée en une scène en trois
57
dimensions, grâce à la générations d'images stéréoscopiques.
Pour illustrer de quelle façon il est possible de générer des anaglyphes en temps réel
avec OpenGL, nous partons d'une scène normale, et expliquons quelles sont les modications nécessaires pour transformer le rendu de l'image, en un anaglyphe généré en temps
réel.
Supposons que la scène soit dessinée dans une fonction render(). Le squelette de permettant de dessiner la scène est le suivant :
gl.glMatrixMode(GL.GL_PROJECTION) ;
gl.glLoadIdentity() ;
glu.gluPerspective(45.0f, screenAspect, .01, 1000.0) ;
gl.glMatrixMode(GL.GL_MODELVIEW) ;
gl.glLoadIdentity() ;
glu.gluLookAt(cx, cy, cz, xl, yl, zl, ux,uy,uz) ;
render() ;
Pour modier la scène en une scène anaglyphe, il faut prendre deux images, distantes
d'environ 6,5 centimètres (la distance moyenne entre les yeux, chez l'être humain), qui
regardent le même point. Ces deux images devront ensuite être passée à un ltre de couleur
diérent pour chaque image (un correspondant à la couleur du ltre équipé sur l'oeil droit,
et l'autre correspondant à la couleur du ltre de l'oeil gauche). Les deux images générées
doivent ensuite être superposées sur une seule image et achées. Appliquons toutes ces
modications à notre squelette, et visualisons le résultat :
gl.glMatrixMode(GL.GL_PROJECTION) ;
gl.glLoadIdentity() ;
glu.gluPerspective(45.0f, screenAspect, .01, 1000.0) ;
gl.glMatrixMode(GL.GL_MODELVIEW) ;
gl.glLoadIdentity() ;
glu.gluLookAt(cx - EYE_SEP/2, cy, cz, xl, yl, zl, ux,uy,uz) ;
gl.glColorMask(false, true, true, false) ;
render() ;
gl.glClear( GL.GL_DEPTH_BUFFER_BIT ) ;
gl.glMatrixMode(GL.GL_PROJECTION) ;
gl.glLoadIdentity() ;
glu.gluPerspective(45.0f, screenAspect, .01, 1000.0) ;
gl.glMatrixMode(GL.GL_MODELVIEW) ;
gl.glLoadIdentity() ;
glu.gluLookAt(cx + EYE_SEP/2, cy, cz, xl, yl, zl, ux,uy,uz) ;
gl.glColorMask(true, false, false, false) ;
render() ;
gl.glColorMask(true, true, true, true) ;
58
Nous voyons dans ce squelette que le buer de profondeur doit être vidé entre l'achage
de la partie gauche et celui de la partie droite. Entre ces deux achages, la position de
la caméra a une diérence de EYE_SEP, ayant une valeur de 0,065, ce qui correspond
à 6,5 centimètres dans le choix de nos unités en OpenGL. La fonction glColorMask()
permet quant à elle de ltrer les composantes qui peuvent être achées. La signature de
la méthode étant glColorMask(boolean red, boolean green, boolean blue, boolean alpha).
Le booléen est mis à true si et seulement si la composante correspondante peut être
achée. Ce squelette est le point de départ de toute scène anaglyphe.
3.4.5 WindowedView
La classe
WindowedView
permet de calculer la portion visible d'une image, en suppo-
sant que l'écran est une fenêtre et que l'utilisateur équipé de lunettes infrarouges regarde
à travers cette fenêtre. Pour calculer la zone visible, la fonction getImageOset() de la
classe WindowedView a besoin d'une série d'informations concernant l'image et l'écran :
Le premier paramètre est la caméra, qui est nécessaire pour connaitre la position de
l'utilisateur par rapport à l'écran. C'est cette position qui sera utilisée pour calculer
la portion visible de l'image en fonction de la position de l'utilisateur par rapport à
l'écran.
La distance réelle qui existe entre l'image représentée et l'objectif de l'appareil qui
a pris cette image.
La hauteur de l'écran, et son ratio, qui nous permettront de calculer sa largeur. Ces
informations sont nécessaires puisque l'écran représente la fenêtre à travers laquelle
la scène doit être visible.
La taille réelle que représente la scène, sous la forme d'un objet Point, qui contient
en abscisse la longueur en centimètres de la scène et en ordonnée sa largeur, toujours
en centimètres.
Le schéma de la gure 4.1 peut être utile à la compréhension du calcul de la portion
visible de l'image qui est également détaillé à la section 2.3.3. Grâce à ces informations, la
méthode getImageOset() peut calculer la calibration de l'image, qui est alors renvoyée
dans un objet de type ImageOset. Cet objet contient quatre variables qui correspondent
au bords haut, bas , gauche et droite qui dénissent la portion visible de l'image.
3.5 Module Handtracking
Passons a présent à une brève explication des quelques classes importantes du module handtracking. Deux types de handtracking ont été développés, le premier permet de
déplacer des objets en temps réel, en déplaçant la main, alors que le second détecte des
mouvements simples pour piloter une interface.
3.5.1 HandTracking
Tout comme le headtracking doit connaître la position de la tête dans l'espace pour
acher la scène, la partie du handtracking qui permet de déplacer des objets doit connaître
la position de la main, dans ce même espace. Pour ce faire, nous avons développé une
59
interface qui permet de caractériser la position de la main. Cette interface est composée
de six accessors et mutators, dont l'utilité est expliquée ci-dessous.
1. setHandX(double x) / getHandX() permet de récupérer ou de modier la position
de la main le long de l'axe des x.
2. setHandY(double y) / getHandY() fait le même travail que ci-dessus, mais avec
l'axe des y
3. setHandZ(double z) / getHandZ() fait la même chose que 1, mais avec l'axe des z.
4. setHandXInit(double x) / getHandXInit() permet de modier la position initiale de
la main dans le monde virtuelle. Typiquement dans le jeu de pong, cela permet de
placer la raquette à une position initiale qui nous convient. En général la position
de la main est dépendante de la position du visage, et cette fonction n'a pas grand
intérêt si on mixe headtracking et handtracking, mais peut avoir plus d'intérêt si le
handtracking seul est utilisé, pour positionner la main dans la scène OpenGL.
5. setHandYInit(double y) / getHandYInit() possède le même comportement que 4,
avec l'axe y
6. setHandZInit(double z) / getHandZInit() fait la même chose que 4, mais avec l'axe
des z.
3.5.2 HandHandler
la classe HandHandler est le succédané de la classe CameraHandler, appliqué au
concept de Handtracking. Elle est composée d'une méthode principale
ply()
handleEventSim-
dont le but est de calculer la position de la main dans l'espace et de stocker cette
position dans un objet implémentant l'interface HandTracking, qu'il est nécessaire de passer en paramètre du constructeur, à la création d'un objet HandHandler.
Pour détecter la position de la main, nous n'avons à notre disposition qu'une seule
led, c'est pourquoi le calcul de la distance entre la caméra et la main est impossible. Nous
devons donc nous baser sur la position de la tête pour déterminer à quelle profondeur
se situe la main, et placer celle-ci à un distance arbitraire devant la tête. Cette distance
est déterminée par la variable HAND_HEAD_DIST, dont la valeur par défaut est 1.5,
signiant que la main se trouvera toujours un mètre cinquante devant la tête. Le calcul
de la position de la main étant dépendant de la position de la tête, nous n'avons dans ce
cas d'autre choix que de passer en paramètre du constructeur, la référence vers un objet
implémentant l'interface HeadtrackedCamera, qui contiendra la position de la tête, calculée grâce à la classe CameraHandler. Cette dépendance est malheureusement inévitable
si nous n'avons qu'une seule led pour détecter la position de la main.
Pour distinguer la main du visage, nous avons développé une méthode très naïve.
C'est la raison pour laquelle la méthode qui calcule la position de la main se nomme
handleEventSImply(). Tout au long du tracking, celle-ci ne peut pas se trouver au dessus
du visage. Cette limitation n'est pas vraiment restrictive, car l'utlisateur a très souvent
tendance à interagir avec les mains situées plus bas que le visage.
60
Une seconde méthode basée sur une heuristique de suivi a été implémentée, mais sa
mauvaise performance nous a poussé à déprécier cette méthode. Les mauvaises performances sont dues au nombre d'évènements générés par seconde qui est parfois beaucoup
trop irrégulier. Il est alors dicile de suivre les points lorsque l'utilisateur déplace la main
très rapidement et que l'application n'a pu rafraîchir la position de la main que 25 fois
durant la précédente seconde. Cette méthode pourrait être améliorée, mais un travail
supplémentaire est requis pour lui donner une certaine robustesse, c'est pourquoi nous
avons préféré déprécier cette méthode, qui reste toutefois disponible dans le code source
de l'application.
3.5.3 HandMovementsDetector
La seconde implémentation de headtracking proposée permet de détecter quelques
mouvements très simples. Cette détection est eectuée dans la classe HandMovementsDetector.
Pour détecter les mouvements, nous utilisons une heuristique que nous appelons heuristique de scoring. Le terme de scoring est utilisé car nous utilisons un score pour détecter
les mouvements. La détection à lieu de la façon suivante : lorsque au moins trois évènements consécutifs accusent un déplacement de la source infrarouge sur le capteur dans une
certaine direction, un score de zero est attribué à ce déplacement (la direction est alors
gardée en mémoire). Pour chaque nouvel évènement ou la coordonnée de la led continue
de se déplacer dans la même direction (haut, bas, gauche ou droite), nous augmentons
la valeur du score de un. Si par contre l'évènement indique une direction opposée à celle
actuellement en mémoire, nous diminuons la valeur du score de 2. L'idée est de pénaliser
plus fortement les mouvements qui nuisent au déplacement, pour éviter de considérer les
petits mouvements non contrôlés de l'utilisateur comme un mouvement valide.
Une fois que le score atteint une certaine valeur, nous pouvons armer avec une
certaine conance que le mouvement est valide, et le listener MovementListener, passé en
paramètre de la classe HandMovementsDetector, est notié de ce mouvement.
Si par contre le score atteint une valeur négative, alors la direction qui avait été retenue
est supprimée.
Le listener MovementListener est un interface contenant une série de signatures de
méthodes correspondant aux mouvements que la classe HandMovementsDetector est capable de détecter. Toute application souhaitant attribuer un comportement à chacun de
ces mouvements doit donc implémenter l'interface MovementListener.
Au total, la classe HandMovementsDetector est capable de distinguer huit mouvements
diérents. Ces mouvements correspondent aux signatures de méthodes présentes dans
l'interface MovementListener. Ces signatures sont citées ci-dessous.
1. leftMovementDetected() est déclenché lorsqu'un mouvement de la main, allant de
la droite vers la gauche est détecté, avec un certain degré de conance, grâce à
l'heuristique de scoring.
2. rightMovementDetected() est appelée lorsqu'un mouvement de gauche à droite de
la main est détecté.
61
3. upMovementDetected() correspond à un mouvement de bas en haut de la main.
4. downMovementDetected() est appelé lorsqu'un mouvement de haut en bas de la
main est détecté.
5. zoomedIn(double factor) est une méthode qui est appelée lorsque deux sources sur
le capteur infrarouge s'éloignent. factor correspond alors au taux de zoom courant.
6. zoomedOut(double factor) est appelée lorsque deux sources se rapprochent sur le
capteur infrarouge. Le taux de zoom est alors disponible dans la variable factor.
7. straX(double pas), est une méthode qui est appelée si le taux de zoom possède
une valeur positive. Si celui-ci à une valeur négative, la méthode ne peut pas être
appelée. La méthode est prend une valeur positive en paramètre lorsque la source
infrarouge sur le capteur se situe plus à droite que l'évènement précédent. Sinon, le
valeur est négative. Cette méthode est décelenchée à chaque évènement, si la source
s'est déplacée horizontalement (sur l'axe des X du capteur).
8. straY(double pas) possède le même comportement que straX(), mais appliqué
sur la direction haut / bas de la source infrarouge. La méthode est appelée avec
une valeur positive si la source courante est plus haute que celle de l'évènement
précédent.
62
Chapitre 4
Applications
4.1 Jeu de Pong
Le jeu de Pong est la première application développée pour illustrer notre module
headtracking. Son principe est très simple, il s'agit d'un jeu de tennis de table où l'utilisateur contrôle la position de la raquette en se déplaçant dans l'espace, devant son écran.
Le but étant de tenter d'intercepter la balle pour qu'elle puisse être renvoyée à votre adversaire (l'ordinateur).
Pour développer un jeu de tennis de table, plusieurs éléments clés sont à distinguer.
Dans la suite de cette section, nous détaillerons l'implémentation du jeu, de la détection
de collisions, à la gestion des règles du jeu.
4.1.1 Trajectoire de la balle
Pour modéliser la trajectoire de la balle, nous avons utilisé les équations de mouvements issues des lois de mécanique (voir Section 2.3.4). La balle se déplace, sans frottements, et donc sans aucune force opposée à son déplacement. De cette façon, le calcul de
sa position est grandement simplié. Pour modéliser son déplacement, un mouvement de
type rectiligne uniforme est utilisé pour son déplacement latéral et d'avant en arrière, tandis qu'un mouvement rectiligne uniformément accéléré est utilisé pour son déplacement
vertical, soumis à la force de la gravité.
En utilisant un procédé numérique de résolution d'équations diérentielles (tel que
Runge-Kutta par exemple), nous aurions pu intégrer ces forces dans les équations de
mouvements. Cependant, mettre en place ce système de façon ecace pourrait se révéler
très couteux en temps CPU. De plus, l'intérêt du jeu de pong est davantage lié au concept
de headtracking, pour lequel un jeu simple sut à démontrer le principe.
4.1.2 Gestion des collisions
Lors du développement d'un jeu de
pong, un élément clé est la gestion des collisions.
Celle-ci doit être eectuée pour détecter les moments où la balle doit rebondir, que ce soit
sur la table, sur le let, ou encore sur le sol.
63
Pour gérer les collisions, nous avons créé une classe
Trajectoire
dans laquelle toute la
gestion de collisions est mise en place. Lors du déplacement de la balle, dont la position est
remise à jour avant chaque achage, nous vérions si un rebond ne doit pas être eectué.
Pour ce faire, nous calculons le moment de la prochaine collision, qui se fera soit avec la
table, soit avec la raquette.
Nous commençons par tester à chaque mise à jour de la position de la balle, si
une collision ne doit par être gérée entre celle-ci et la raquette (du joueur ou de
l'ordinateur). Ce test est nécessaire, car les règles du tennis de table permettent de
rattraper la balle avant son rebond sur la table. Si une collision est détectée, alors
celle-ci est gérée et la balle est renvoyée dans la direction opposée, avec un angle
de réexion vertical égal à l'angle d'incidence avec lequel la balle est arrivée sur la
raquette. L'angle horizontal est quant à lui généré aléatoirement, avec une tendance
à diriger la balle vers le centre de la table pour le joueur humain (cela évite d'avoir à
gérer soi-même l'angle d'inclinaison de la raquette, rendant alors le jeu encore plus
dicile).
Nous calculons ensuite le moment auquel la balle aura atteint la hauteur de la table
durant sa chute (la table se trouve à 76 centimètres du sol). Si le temps courant est
supérieur au temps calculé pour le rebond, nous appelons la méthode checkTable()
qui vérie si la balle se trouve bien sur la table lorsqu'elle se trouve à une hauteur de
76 centimètres. Si la balle ne se trouve pas dans les bornes de la table, le mouvement
continue jusqu'au niveau du sol.
Si par contre, la balle se trouve sur la table, nous générons le rebond à l'aide de
la méthode makeRebond(). Cette méthode est chargée de mettre à jour l'équation
du mouvement et de réinitialiser le temps permettant le calcul de l'équation. Un
problème se pose alors : imaginons que le jeu soit utilisé sur une machine peu
performante ou la position de la balle ne peut être remise à jour que 15 fois par
seconde. Comme nous attendons que le temps courant soit supérieur au temps de
collision avec la table, il est possible que celui-ci soit fortement supérieur au temps
de collision réel. Dans ce cas, la balle se trouve en-dessous de la table, et le rebond
sera calculé à partir de la position courante, en-dessous de la table, entraînant alors
une erreur dans son mouvement.
Pour éviter ce problème, au lieu de faire rebondir la balle à partir de sa position
verticale courante, nous initialisons l'équation à une hauteur de 76 centimètres,
avec un temps initial égal à t - tc , où t correspond au temps courant, et tc au temps
calculé pour la collision entre la table et la balle. De cette façon, le mouvement de
la balle est indépendant du nombre d'images par seconde pouvant être générées,
rendant alors le comportement de la balle parfaitement déterministe quelle que soit
la machine utilisée.
4.1.3 Règles du jeu et Calcul du score
Une autre partie importante concerne l'application des règles du jeu et le calcul du
score, tout au long de la partie.
64
4.1.3.1 Règles du jeu
En fonction de la position de la balle sur la table lorsqu'elle doit rebondir, le rebond
doit être autorisé ou refusé. L'exemple suivant montre une situation qui doit être refusée.
Exemple 1
Supposons que le service a été fait par l'ordinateur. Si le joueur
humain
fait rebondir la balle dans son camp lorsqu'il la renvoie à l'ordinateur, le rebond doit être
refusé, et un point doit être accordé à l'ordinateur.
Nous pouvons grâce à cet exemple dégager les éléments qui déterminent l'acceptation
ou le refus du rebond :
Il est nécessaire de savoir si c'est le service, auquel cas le joueur a le droit (et même
l'obligation) de faire rebondir la balle dans son camp.
Si c'est le service, il faut savoir si le premier rebond a eu lieu dans la partie de la table
appartenant au serveur, et si le second rebond a eu lieu sur la partie appartenant à
l'adversaire.
Le sens de la balle est lui aussi important, pour savoir de quel côté doit avoir lieu
le rebond. La balle peut aller du joueur vers l'ordinateur ou inversément.
Si ce n'est pas le service, il faut savoir si l'unique rebond a bien lieu dans la partie
de la table opposée à celui qui envoie la balle.
etc.
Les élements ci-dessus, ainsi que certains autres éléments non cités, constituent
l'état de la
partie. Nous pouvons donc nous baser sur ces éléments pour déterminer si un rebond doit
être accepté ou non. Au niveau de l'implémentation, nous pouvons voir cela comme une
machine à états, où le rebond sera accepté ou refusé en fonction de l'état de la trajectoire.
Lorsqu'un rebond est refusé, il est nécessaire de savoir à qui le point du jeu doit être
accordé. Nous pouvons remarquer que l'attribution du point est dépendante de l'état de la
trajectoire au moment du refus. C'est donc l'état qui détermine à qui le point est attribué,
et de façon plus générale, les règles du jeu sont déterminées par une machine à états.
Exemple 2
Si la balle se déplace du joueur vers l'ordinateur, et que le premier rebond
a lieu en dehors de la table, le point est attribué à l'ordinateur.
Lorsqu'un jeu se termine, la partie est mise en pause, l'appui sur la touche
P
permet
de lancer un nouveau jeu. Le service est alors automatiquement envoyé, y compris lorsque
c'est au tour du joueur de servir. Au niveau du service, chaque joueur a le droit de servir
5 fois, ensuite le service est passé à l'adversaire.
4.1.3.2 Calcul du score
Lorsqu'un point est attribué à un joueur, le score doit être remis à jour. Ce score est
calculé et maintenu à jour dans la classe
Score. La méthode principale de cette classe est
la méthode score(boolean isHuman) qui permet d'ajouter un point au joueur si le booléen
passé en paramètre est vrai, ou à l'ordinateur s'il est faux.
65
Dans les règles du tennis de table, le premier joueur qui marque onze points remporte
le set. Pour remporter un set, le score doit avoir un écart d'au moins deux. Si le score
est dix partout, alors la partie continue au moins jusqu'à douze. Le set continue ainsi
tant qu'il n'y a pas deux points d'écart entre les deux joueurs. Le premier joueur ayant
remporté trois sets gagne la partie.
L'implémentation du score est eectuée de la même façon qu'expliqué ci-dessus. Pour
savoir si une partie est terminée, la méthode score() renvoie un booléen vrai si et seulement
si la partie est terminée. Le gagnant est alors le joueur qui a remporté trois sets.
4.1.4 Intelligence articielle
Notre jeu de pong se joue contre l'ordinateur. Nous avons donc développé un intelligence articielle très simple dont nous expliquons le comportement ci-dessous.
En cours de partie, l'ordinateur calcule la trajectoire de la balle, pour savoir ou placer
sa raquette lorsque celle-ci arrive dans sa direction. Le déplacement de la raquette est effectué au moment du rebond de la balle sur le côté de la table appartenant à l'ordinateur.
Ce mouvement est opéré de façon simple et brutale. Pour améliorer le réalisme apporté
par le jeu, une amélioration intéressante serait de déplacer la raquette progressivement
vers la balle, plutôt que de façon brutale. Une fois la raquette placée au bon endroit, l'ordinateur attend patiemment que la balle rebondisse sur cette dernière. La balle est alors
envoyée dans une direction horizontale variable. Nous avons implémenté une fonction qui
génère un angle pour le rebond. Cet angle se base sur la position de la balle par rapport à
la table. Si la balle se situé du côté gauche de la table, alors l'angle généré aura tendance
à envoyer la balle vers la droite, an de la recentrer.
Pour rendre le jeu amusant, il est nécessaire que l'ordinateur commette des erreurs.
Un facteur aléatoire a donc été ajouté lors de la génération de l'angle pour renvoyer la
balle. De cette façon, il arrivera à l'ordinateur de renvoyer la balle a côté de la table,
permettant alors au joueur de marquer un point. Une autre erreur que peut commettre
l'ordinateur est au niveau de la vitesse de la balle. Lorsqu'il renvoie la balle, l'ordinateur
frappe avec une certaine force, modiant ainsi la vitesse de la balle, qui peut alors sortir
de la table, sans avoir eectué de rebond.
L'intelligence articielle n'est pas la partie la plus développée de l'application. De
nombreuses améliorations pourraient lui être apportées. Par exemple il pourrait être intéressant que la raquette ne parvienne pas toujours à rattraper la balle. Une possibilité est
d'initier le déplacement de la raquette de l'ordinateur, à une vitesse constante, quelques
dixièmes de seconde avant le rebond de la balle. Il est alors possible que la raquette n'ait
pas le temps de se déplacer jusqu'à sa position d'arrivée, ratant alors son coup, et laissant
passer la balle.
66
4.1.5 Handtracking
Un autre but recherché lors de la réalisation du jeu de
pong
est de montrer l'interac-
tion possible entre le headtracking et le handtracking. En plus de piloter la position de la
caméra avec la tête, il peut être intéressant de contrôler un objet virtuel que l'utlisateur
tient en main. Nous avons donc utilisé notre gant infrarouge pour donner la possibilité au
joueur d'utiliser sa main, pour contrôler la position de la raquette, en plus du déplacement
du personnage virtuel, grâce au déplacement de la tête.
Le procédé suivi pour la détection de la position de la main est très simple et similaire
à la détection de la tête (voir section 2.3.2). Nous utilisons pour cette application un gant
équipé d'une seule led infrarouge. Il est alors possible de déplacer la raquette horizontalement et verticalement. En ce qui concerne la position en profondeur, nous pouvons
supposer que la raquette suit les mouvements du personnage virtuel.
En cours de partie, il sut de déplacer la main équipée du gant infrarouge, pour déplacer la raquette, dont les mouvements suivent alors ceux de la main. Pour faire rebondir
la balle, il n'est pas nécessaire d'eectuer un mouvement simulant une frappe. Le simple
fait de positionner la raquette au bon endroit sut à faire rebondir celle-ci à la même
vitesse qu'avant le rebond, mais dans la direction opposée.
4.1.6 Déroulement d'une partie
Au lancement de l'application, la partie est en pause, et c'est au tour de l'utilisateur
de servir. L'appui sur la touche
P
permet de lancer le jeu. Le service est automatiquement
envoyé (l'utilisateur ne doit pas frapper la balle). Le joueur peut alors se déplacer pour
modier la position du joueur virtuel, et éventuellement utiliser sa main, si le handtracking
est activé.
La partie continue jusqu'à ce qu'un des deux joueurs commette une erreur. Le jeu
est alors remis en pause, et un point est attribué au joueur qui marque le point. L'appui
sur la touche
P
permet de lancer le jeu suivant. Chaque joueur a droit à cinq services
consécutifs, après quoi le service est passé à l'adversaire.
Au cours de la partie, le joueur doit simplement positionner sa raquette à un endroit
qu'il juge adéquat pour permettre à la balle de rebondir dessus. Lorsque la balle touche la
raquette, la balle est renvoyée avec un angle horizontal variable, qui a tendance à l'envoyer
vers le centre de la table.
4.2 Targets
La seconde application créée est inspirée de celle développée par Johnny Lee (voir
section 1.4.1). Elle a été conçue an de comparer nos deux applications de headtracking.
Tout comme dans la version de Johnny Lee, notre application représente une grille à
l'intérieur de laquelle sont situées une série de cibles, aléatoirement placées. Ces cibles
sont maintenues au fond de la boîte par un cable virtuel, donnant une impression de
profondeur à l'image.
67
Le déplacement de l'utilisateur autour de l'écran entraîne alors une modication en
temps réel de la scène. L'achage réagit de façon proportionnelle au déplacement de l'utilisateur, donnant ainsi l'impression que l'écran n'existe pas et que de vraies cibles sont
placées devant l'utilisateur, juste derrière l'écran.
Pour obtenir des résultats similaires à l'application de J. Lee, nous avons dû utiliser une
matrice de projection légèrement modiée, par rapport à la matrice obtenue en utilisant
la primitive gluPerspective(). C'est la méthode glFrustum() qui a été utilisée pour simuler
l'eet de profondeur donné aux cibles, lorsque l'utilisateur s'éloigne ou se rapproche de
l'écran. Grâce à cette matrice de projection, la dimension de profondeur se comprime
lorsque l'utilisateur se rapproche, et se dilate lorsque celui-ci s'éloigne. Ce comportement
a un eet plus proche de celui obtenu par notre oeil lorsque nous nous éloignons d'un
objet qui possède une certaine profondeur.
Le résultat obtenu avec la primitive gluPerspective() est quant à lui diérent. Lorsque
l'utilisateur s'éloigne de l'écran, la scène s'éloigne, et semble alors plus petite, sans pour
autant comprimer la dimension de profondeur.
L'utilisation de l'une ou l'autre de ces matrices de projection dépend en réalité du
type de scène à acher. Si l'eet recherché lors de l'éloignement est une compression de
la scène, qui ne doit pas donner l'impression que la scène s'éloigne, alors la primitive glFrustum() sera préférée. Si par contre (c'est le cas de notre jeu de
pong ) nous souhaitons
pouvoir nous déplacer sur la dimension de profondeur, il est plus intéressant d'utiliser
gluPerspective().
La réalisation de l'application Targets nous a également permis de tester les fonctionnalités additionnelles qui ont été développées. C'est le cas du degré de liberté supplémentaire ajouté à l'application, qui permet de détecter et de simuler les mouvements de pivot
de la tête sur le côté. Lorsque l'utilisateur pivote la tête sur le coté gauche (resp. droit),
l'achage est alors incliné avec la même pente que celle entre les deux sources infrarouges,
mais dans la direction opposée, c'est-à-dire vers la droite (resp. gauche).
La génération d'images stéréoscopiques nous a également permis d'ajouter une fonctionnalité supplémentaire à notre scène
Targets, en comparaison à celle développée par J.
Lee. La scène représentant les cibles a été adaptée pour pouvoir être visionnée avec un
eet de relief, en portant des lunettes à anaglyphes.
4.3 Eet fenêtre
Un concept de headtracking relativement diérent de celui appliqué dans les applications précédemment expliquées est la création d'un eet fenêtre. Par eet fenêtre nous
entendons que l'écran est vu comme une fenêtre à travers laquelle une scène est observée.
Si l'utilisateur se rapproche de celle-ci, il peut voir une plus grande partie de la scène. Si
par contre il s'éloigne, alors il verra une partie moins importante de la scène.
Le concept d'eet fenêtre est illustré à la gure 4.1. Supposons que l'utilisateur se
trouve au point p1, alors la portion visible de la scène est obtenue en traçant les deux
droites partant du point p1, et passant par les bords gauche et droit de l'écran, respecti-
68
Figure 4.1: Eet fenêtre
vement
e_left
et
e_right
sur la gure. L'intersection entre chacune de ces deux droites et
la scène permet d'obtenir les deux points qui correspondent aux bords visibles de la scène
s_l1
(
et
s_l2
sur l'image). Lors du passage du point
p1
au point
portion visible de la scène est légèrement plus large, car le point
p2, nous voyons que la
p2 est plus proche de
l'écran.
C'est ce principe d'eet fenêtre qui fait l'objet d'une de nos applications. A partir
d'une photographie dont nous connaissons la distance entre l'appareil et la scène photographiée, ainsi que la largeur totale représentée par la photo, nous pouvons donner
à l'écran le même comportement qu'une fenêtre. Ce comportement est possible grâce à
l'utilisation du headtracking, qui permet de situer dans l'espace la position du visage par
rapport à l'écran. Cette position correspond au point
p1
sur la gure 4.1. En ajoutant aux
informations ci-dessus, les informations concernant la dimension de l'écran. Il est possible
de calculer de façon exacte quelle est la portion de la scène qui est vue, en fonction de la
position de l'utlisateur par rapport à l'écran.
Grâce au headtracking, la portion visible de la scène peut être remise à jour en temps
réel, donnant alors un impressionnant eet de réalisme à l'écran qui semble être transformé
en une fenêtre à travers laquelle l'utilisateur regarde.
4.4 Formes 3D
La génération d'images anaglyphes est plus ecace lorsque celles-ci sont représentées
en niveaux de gris. Nous avons vu que cela est dû au fait que nous utilisons des ltres de
69
couleurs pour faire apparaître le relief de l'image. Les couleurs ne peuvent alors plus être
utilisées de la même façon pour colorer l'image. En eet, si nous utilisons des lunettes
équipées de ltres rouge et cyan, la présence de rouge sur une image ne pourra être détectée que par l'oeil équipé du ltre rouge. La composante verte et bleue de l'image étant
nulle, l'autre oeil ne pourra pas voir cette partie de l'image, et la stéréoscopie ne donnera
pas un bon eet.
Pour illustrer ce principe, nous avons développé au départ d'un travail eectué par
Paul Bourke [1] (initialement développé en C), une application représentant diérentes
formes en trois dimensions, en utilisant uniquement les deux couleurs correspondant aux
ltres de couleurs des lunettes à anaglyphes. Ces formes sont dessinées en temps réel, en
utilisant la primitive GL_LINE, ainsi que la méthode
l de fer
d'OpenGL, qui permettent
de dessiner des formes géométriques en utilisant uniquement des lignes. Pour les formes
plus complexes telles que le Lorenz, des points sont utilisés au lieu de traits. Pour une
liste complète des formes achables par l'application, se référer à la section 5.2.
Pour le rendu en trois dimensions, la caméra OpenGL est placée à deux endroits,
distants d'environ 6,5 centimètres (la distance qui sépare nos yeux). Une image est prise
de chacune de ces deux positions et passée à un ltre dont la couleur correspond à celui
équipé sur l'oeil auquel l'image est destinée. Les deux images ltrées sont alors superposées et achées à l'écran.
En plus du rendu tridimensionnel, le module de headtracking est également utilisable
dans cette application. Il permet ainsi de proter davantage de l'eet apporté par la
stéréoscopie, en modiant la position de la caméra, qui entraîne à son tour une redénition
de l'image en trois dimensions, et par conséquent, une modication de l'espace entre les
deux images. Cet espace est réadapté en fonction de la position de l'utilisateur par rapport
à l'écran. Grâce à l'ajout du headtracking, l'eet de profondeur est alors intensié.
4.5 Explorateur de Fichiers
Si nous nous intéressons plus particulièrement au concept de handtracking, il est possible de développer une multitude d'applications qui mettent en pratique ce concept.
Une idée intéressante pourrait être de développer une application qui met en pratique
le handtracking pour piloter une interface. Imaginons un explorateur de chier tel que
Nautilus [3] qui puisse être entièrement piloté à distance grâce aux mouvements des mains.
Nous pourrions piloter le curseur en déplaçant la main, le clic serait simulé en joignant le
pouce et l'index, etc.
Dans un dossier contenant des videos, une interface spécique peut être développée.
Elle permettrait par exemple de passer d'une video à l'autre en eectuant un mouvement
de balayage de droite à gauche avec la main, ou encore de faire un arrêt sur image en
joignant le pouce et l'index.
Dans un dossier contenant des images, il serait possible d'agrandir une image en éloignant ses deux mains, ou de la rétrécir en les rapprochant. Nous pouvons aussi déplacer
70
cette image sur l'écran en joignant pouce et index et en déplaçant notre main, l'image
sera alors déplaçée dans le sens correspondant à notre mouvement.
Pour détecter les mouvements de la main, nous pourrions utiliser deux gants infrarouges, comme ceux dont nous avons parlé à la section 2.1.
Cette idée constitue une possibilité d'utilisation du concept de handtracking, et n'a
pas été implémentée dans le cadre de ce travail. Ce concept constitue toutefois un sujet
de recherche qu'il serait intéressant d'approfondir.
4.6 Visualisateur de photos
L'explorateur de chier est une idée intéressante qui met en pratique l'utilisation du
handtracking. Toutefois, la mise en place d'une telle application demande beaucoup de
temps. Pour illustrer les possibilités du handtracking de façon simple, nous avons décidé
de développer une application de visualisation de photos, pouvant être pilotée avec les
mains. L'utilisation de cette application requiert la présence de deux gants infrarouges,
équipés chacun d'une unique led.
Lors du lancement de l'application, un dossier spécique est parcouru, à la recherche
de photographies. Celles-ci sont alors chargées en mémoire, et la première d'entre elles
est achée à l'écran. Pour passer à l'image suivante (resp. précédente), il sut alors
d'eectuer un mouvement de la main, de droite à gauche (resp. de gauche à droite). Le
mouvement est détecté par la Wiimote et envoyé à l'ordinateur, qui utilise une simple
heuristique de scoring (voir section 3.5.3) pour détecter des mouvements simples tels que
un déplacement de la main vers la gauche, la droite, vers le haut ou vers le bas.
En plus de ces mouvements simples, il est possible de zoomer (resp. dézoomer) sur
l'image, en écartant (resp. rapprochant) les mains. Une fois l'image agrandie, l'utilisation
d'une seule main permet de déplacer l'image dans la direction empruntée par celle-ci. Le
passage à une autre image étant temporairement rendu impossible, tant que le facteur
de zoom est supérieur à un (qui correspond à la taille initialle des images, au lancement
de l'application). Remettre l'image à une taille plus petite réactive automatiquement la
possibilité de passer d'une photo à l'autre, d'un mouvement de la main.
4.7 Interface graphique
Pour utiliser chacune des applications dont nous avons parlé ci-dessus, nous avons
développé une interface graphique intégrant chacune de nos applications. Cette interface
graphique permet de passer d'une application à l'autre, et pour chacune d'elles de fournir
un ensemble d'outils permettant la modication de certains paramètres propres à chaque
scène.
Pour faciliter l'utilisation de l'interface graphique, un guide de l'utilisateur est présent à la section 5.2. Ce guide détaille chacune des fonctionnalités oertes par l'interface
utilisateur.
71
4.7.1 Architecture globale
Dans cette section, nous nous focaliserons plus sur le côté implémentation, contrairement à la section 5.2, qui traite le coté utilisation de l'application.
Le coeur de notre application graphique se trouve dans la classe
MainFrame,
qui se
charge d'interconnecter tous les modules graphiques dans une unique Fenêtre. Cette classe
hérite de la classe
JFrame
qui représente une fenêtre graphique. L'entièreté de l'interface
graphique se trouve dans cette fenêtre (excepté le mode plein écran qui provoque la
création d'un seconde fenêtre).
Le constructeur est divisé en sous fonctions, dont chacune a pour but d'initialiser une
des cinq parties principales de l'application, rangées grâce à un BorderLayout.
1. La première partie est la partie ouest, qui contient les paramètres propres aux mesures et à la Wiimote. Ces paramètres sont respectivement contenus dans des classes
nommées
MesuresSettings
et
WiimoteSettings. Ces deux classes héritent de JPanel,
et contiennent une série de labels et de champs de texte, rangés principalement en
utilisant un LayoutManager de type
GridLayout.
2. La seconde partie est la partie centrale de l'application, qui contient un GLCanvas,
à l'intérieur duquel est géré l'achage de la scène OpenGL.
3. La partie sud de l'application contient un
JLabel
utilisé pour acher la position de
la caméra et du point de vue.
4. Du coté est, est contenu une
JTabbedPane contenant deux onglets. Le premier onglet
ache un panel contenant les paramètres propres à chaque scène. Ce panel qui
est modié lorsque l'utilisateur change la scène représentée sur la partie centrale.
Le second onglet contient un panel graphique déni dans une classe
ControlPan
donnant un aperçu en temps réel de la position de l'utilisateur par rapport à l'écran.
Cet aperçu schématise une vue qui surplombe le lieu d'utilisation de l'application.
L'écran, la Wiimote et l'utilisateur y sont ainsi représentés.
5. La dernière partie de l'application se situe au nord. Elle contient une JComboBox
qui permet de changer de scène. Pour eectuer cette modication, la ComboBox
est attachée à un listener qui récupère l'évènement généré lors de la modication
de la scène séléctionnée par la ComboBox. La partie nord de l'application contient
également un bouton permettant de passer en mode plein écran. Ce bouton est lui
aussi relié à un listener qui créée une nouvelle Fenêtre lorsque le bouton est pressé.
La fenêtre ainsi crée contient uniquement la scène OpenGL, et toute la surface de
l'écran peut être utilisée pour proter de l'application.
4.7.2 Communication avec les applications
Pour permettre une modularité optimale, l'interface graphique est séparée du code
des applications et des modules. Un package
gui
regroupe toutes les classes concernant
l'interface graphique.
Pour communiquer avec les applications et les modules développés, l'interface utilise
les variables disponibles dans chaque application. Par exemple, la classe
72
PongParam
est
destinée à interagir avec le jeu de pong, implémenté principalement dans la classe
Game.
Pong-
Pour modier les paramètres du jeu, la classe PongParam prend paramètre du
constructeur, la classe PongGame qu'elle est sensée contrôler. De cette façon, la modication des paramètres se fait via un appel de méthode sur l'objet PongGame, dans la
classe PongParam. Ainsi la modication de la position horizontale de la raquette se fait
via l'appel de la méthode setRaquetteX(oat raq).
La communication avec les modules est réalisée de la même façon dans les classes
WiimoteSettings
et
MesureSettings.
4.7.3 Intégration des scènes
La représentation des scènes OpenGL utilise un objet de type GLCanvas pour acher
le contexte OpenGL. La classe GLCanvas est une classe qui hérite de Canvas, appartenant au package awt. Notre application graphique étant construite avec le package swing,
il est important de noter que la compatibilité entre awt et swing n'est pas optimale.
En eet, lorsqu'un composant awt est utilisé dans une application utilisant swing, certains problèmes peuvent apparaître lors de l'utilisation de popups. C'est le cas de l'objet
JComboBox, qui déroule un popup lorsqu'on clique sur celui-ci. Si le popup doit s'ouvrir
au-dessus d'une zone contenant un canevas awt, alors celui ci s'ouvre sous le canevas et
est alors invisible. Ce problème est dû au fait que les composants awt sont
alors que les popups de swing sont
heavyweight
lightweight. Java déconseille de mixer ces deux types
de composants.
Pour éviter ce comportement, sans pour autant ne plus utiliser les
JComboBox, il est
possible de désactiver le caractère léger des popups. Ceci est fait par l'appel de la méthode
suivante, avant l'achage des composants.
JPopupMenu.setDefaultLightWeightPopupEnabled(false);
Une autre solution, qui évite de mixer des composants swing et awt, est d'utiliser un
objet de type GLJPanel plutôt que GLCanvas pour stocker le contexte OpenGL. Cette
classe hérite de la classe JPanel. Le principal souci de cet objet est que ses performances
sont quatre fois plus faibles que celles obtenues avec GLCanvas. Nous avons donc préféré
utiliser ce dernier pour la réalisation de notre application.
73
Chapitre 5
Utilisation du module
5.1 Réaliser une application
Les modules de headtracking et de handtracking ont été développés comme une librairie, pouvant être réutilisée par des programmeurs désireux de créer des applications
qui mettent en oeuvre l'un des deux concepts évoqués. Pour faciliter le démarrage d'un
application, nous présenterons un prototype de programme pour chacun de nos deux
modules.
5.1.1 Installation des librairies
Pour réaliser une application de Headtracking utilisant la Wiimote, il faut tout d'abord
installer la librairie WiiuseJ.
Sous Windows
WiiuseJ nécessite, pour fonctionner, l'installation d'un logiciel capable
de gérer le bluetooth. C'est le cas du logiciel BlueSoleil, qui est téléchargeable gratuitement à cette adresse
http://www.bluesoleil.com/Default.aspx.
Une fois le logiciel
installé, il sut de télécharger la librairie WiiuseJ contenant toutes les librairies (des .dll)
nécessaires au fonctionnement du programme.
Avec Eclipse, il reste alors à ajouter ces libraires dans le java build path du projet,
an de pouvoir lancer une application utilisant la librairie WiiuseJ.
Sous Linux
Pour installer WiiuseJ sur linux, il est préférable de compiler soi-même les
librairies natives (en C) sur sa propre machine. Pour ce faire, un tutoriel mis en ligne
par le créateur de la librairie WiiuseJ explique toutes les démarches à eectuer et toutes
les dépendances à installer pour pouvoir compiler la librairie. Le tutoriel est disponible à
l'adresse suivante :
http://code.google.com/p/wiiusej/wiki/CompileWiiusejOnLinux.
Pour compiler les chiers les dépendances suivantes sont à installer :
dev, xlibmesa-gl-dev, libglu1-mesa-dev, libsdl.
libc-dev, libbluetooth-
Téléchargez ensuite la librairie Wiiuse sur sourceforge (http://sourceforge.net/projects/
wiiuse/files/). Une fois la librairie téléchargée, compilez la en utilisant les commandes
suivantes :
74
make
sudo make install
Une fois la librairie compilée, une librairie dynamique au format
.so
est générée, il ne
reste plus qu'à l'ajouter au path de votre projet, qui utilise WiiuseJ.
5.1.2 Headtracking
Développer une application utilisant le module de headtracking est assez simple.
Quelques conventions doivent toutefois être respectées.
Pour développer notre API, nous avons dû eectuer des choix concernant les axes.
Ces choix ne sont pas restrictifs, mais ne pas respecter les conventions peut compliquer
l'utilisation du module. Les conventions sont les suivantes :
L'axe des X représente la direction latérale, c'est-à-dire un déplacement de gauche
à droite de la caméra sur l'écran
L'axe des Y représente la profondeur, qui permet de déplacer la caméra d'avant en
arrière, pour qu'elle rentre dans l'écran ou recule.
Enn, l'axe Z représente la direction verticale, utilisée pour monter ou descendre la
caméra à l'écran.
Le respect de ces conventions lors du développement d'une application 3D facilitera grandement l'intégration du module. Si ces conventions ne sont pas respectées, pas de panique,
il sura de substituer chacun de vos axes à celui qui est utilisé pour eectuer le même
mouvement dans notre API.
Exemple 1
Si l'axe Z est utilisé pour déplacer la caméra de gauche à droite, alors il
faut placer toutes les méthodes concernant l'axe X (qui représente la direction latérale
dans notre module), sur la composante Z de votre application. De façon plus claire, si la
situation est la suivante :
L'axe des X représente la direction verticale
L'axe des Y représente la profondeur
L'axe des Z représente la direction latérale
Alors, lors de l'appel à la fonction gluLookAt(), qui permet de positionner votre caméra,
vous utiliserez les paramètres dans cet ordre :
gluLookAt(cam.getCameraZ(), cam.getCameraY(), cam.getCameraX(), cam.getEyeZ(),
cam.getEyeY(), cam.getEyeX(), cam.getUpZ(), cam.getUpY(), cam.getUpX()) ;
Nous voyons ici que les méthodes de notre module ont été plaçées aux endroits correspondant aux conventions utilisées pour cette scène (le X et le Z ont été inversés, puisque
leurs conventions sont inversées par rapport aux conventions de la librairie).
5.1.2.1 Utiliser la Camera du module
Une fois l'application développée, en tenant compte des conventions citées plus haut,
quelques ajouts sont nécessaires pour utiliser le headtracking. Commencez par créer un
objet de type Camera, cette classe se trouve dans le package objects du module développé,
et doit donc être importée. Pour plus de détails sur le fonctionnement de la classe Camera,
75
voir section 3.3.2. La classe Camera est utilisée pour contenir la position de la caméra
dans l'espace, cette position correspond à celle de l'utilisateur par rapport à son écran.
Une fois l'objet Camera placé en variable d'instance, nous pouvons utiliser les méthodes dénies sur cette classe pour récupérer sa position dans l'espace. Ces méthodes
devront donc être appelées comme paramètre de la primitive gluLookAt(), comme expliqué à l'exemple 1 ci-dessus.
5.1.2.2 Détection des angles
Pour pouvoir utiliser la détection des angles, expliquée à la section 3.4.1, il est nécessaire que la classe qui s'occupe de colorer les bords de l'écran ait connaissance du contexte
OpenGL. C'est un objet de type BorderPainter, situé dans la classe Camera qui s'occupe
de colorer les bords.
Dans la méthode init() de votre application OpenGL (méthode issue de l'interface
GLEventListener, qui représente une scène OpenGL), ajoutez cette ligne de code :
cam.getBorderPainter().setGL(gl) ;
Cette instruction a pour eet d'informer le BorderPainter du contexte OpenGL, en
supposant que le paramètre gl soit créé de cette façon :
public void init(GLAutoDrawable gLDrawable) {
nal GL gl = gLDrawable.getGL() ;
//....
}
Grâce à cette instruction, le BorderPainter connaît le contexte OpenGL, et pourra être
automatiquement appelé lorsque l'utilisateur s'approche des bords de l'angle de vision de
la Wiimote.
5.1.2.3 Lier la caméra au module
La dernière chose à faire pour proter du headtracking dans l'application développée
est de lier la caméra instanciée dans la classée créée au module de headtracking. Pour
ce faire, nous allons devoir récupérer cette caméra, il est donc intéressant de créer une
méthode getCamera() dans le GLEventListener de l'application développée. La marche à
suivre pour utiliser le module de headtracking est la suivante :
Supposons que votre classe qui contient le contexte OpenGL s'appelle
MyClass.
Com-
mençons par créer cette classe :
MyClass scene = new MyClass() ;
Créons à présent un objet CameraHandler, après l'avoir importé du package headtracking du module. Le CameraHandler a besoin de la caméra instanciée dans la scène pour
fonctionner. Ensuite, il faut créer un objet WiimoteHeadAndHand disponible dans le package wiimoteHandle. Cet objet se chargera de communiquer avec la librairie WiiuseJ et
76
de récupérer les informations des sources infrarouges. Enn, un lien est nécessaire entre
le CameraHandler et le WiimoteHeadAndHand, car c'est le Caméra Handler qui gère les
interactions clavier qui permettent par exemple de connecter une Wiimote en appuyant
sur la touche
R.
Le reste des démarches à suivre concerne OpenGL et permet de lier le gestionnaire de
clavier au canevas à l'intérieur duquel se trouve la scène OpenGL.
CameraHandler camh = new CameraHandler(scene.getCamera()) ;
WiimoteHeadAndHand wim = new WiimoteHeadAndHand(camh) ;
camh.setWiimoteHandler(wim) ;
canvas.addGLEventListener(scene) ;
canvas.addKeyListener(camh) ;
animator.start() ;
canvas.requestFocus() ;
Il ne reste plus qu'à lancer l'application, et à activer la Wiimote en appuyant sur R.
Le headtracking est alors utilisable dans la scène développée.
5.1.3 Handtracking
Détaillons à présent comment il est possible de créer une utilisation qui utilise le module de headtracking, basé sur la détection de mouvements simples. Nous procéderons de
façon similaire à notre explication permettant de créer une scène utilisant le headtracking,
en supposant que l'utilisateur a développé une classe nommée
MyClass,
qui implémente
l'interface GLEventListener de OpenGL (MyClass est donc une scène OpenGL).
En plus d'implémenter l'interface GLEventListener, la scène développée, que nous
souhaitons adapter pour le handtracking doit implémenter l'interface MovementListener,
contenant une série de méthodes qui seront appelées par le module de handtracking lorsqu'un mouvement sera détecté. Par exemple, si un mouvement vers la gauche de la main
est détecté, la méthode leftMovementDetected() de l'interface MovementListener sera automatiquement appelée. C'est au développeur de détailler le comportement eectué par la
méthode, en fonction de l'application qu'il souhaite réaliser. Le squelette d'une application
utilisant le handtracking est détaillé ci-dessous :
MyClass scene = new MyClass() ;
handmov = new HandMovementsDetector(scene) ;
WiimoteHand wim = new WiimoteHand(handmov) ;
handmov.setWimoteHandler(wim) ;
canvas.addGLEventListener(scene) ;
canvas.addKeyListener(handmov) ;
animator.start() ;
canvas.requestFocus() ;
77
Figure 5.1: Au lancement de l'interface graphique
L'appui sur la touche
R permet de connecter une Wiimote, une fois l'application lancée.
Il ne reste plus qu'a déplacer les mains pour déclencher automatiquement les méthodes
de l'interface
MovementListener.
5.2 Interface utilisateur
Pour illustrer les concepts de headtracking et de handtracking, nous avons développé
une série d'applications. Chacune dss applications développées a été discutée au chapitre
4. Pour faciliter l'utilisation de ces applications, une interface utilisateur a été développée
grâce à la couche graphique Swing de Java. Dans cette section, nous avons rédigé un
manuel de l'utilisateur de notre interface graphique. Ce manuel a pour but de faciliter la
prise en main de l'application en détaillant les diérentes fonctionnalités de l'interface.
5.2.1 Lancement de l'application
L'application développée est une archive au format jar exécutable. Pour démarrer
l'application, il sut de saisir la ligne de commande suivante dans un terminal :
java -Djava.library.path="chemin_vers_les_ressources" -jar Headtracking.jar
où chemin_vers_les_ressources correspond à un dossier où se trouvent les librairies et
les textures nécessaires à l'utilisation de l'application.
Une fois cette ligne de commande saisie, l'interface graphique s'ouvre. Cette interface
est composée de 5 régions principales que nous nommerons
Centre
Ouest, Est, Nord, Sud et
par référence à leur situation géographique sur l'écran (voir gure 5.1).
5.2.2 Région Ouest
La première partie de l'interface graphique correspond à la partie gauche de l'interface
graphique. Il est possible que cette zone ne soit pas visible au lancement de l'application.
78
Figure 5.2: Région ouest invisible
Si c'est le cas, il sut de cliquer sur l'une des petites èches située en haut à gauche
de l'interface graphique (voir gure 5.2). Ce bouton permet de disposer de plus d'espace
pour acher la partie centrale de l'application (la scène OpenGL).
5.2.2.1 Onglet Mesures
La région ouest permet de paramétrer une série de fonctionnalités qui sont propres
au matériel utilisé. Comme nous le voyons sur la gure 5.3a, il est possible de modier
diérents paramètres :
Distance Lunettes
Lorsque l'utilisateur réalise ses propres lunettes infrarouges, la dis-
tance entre les leds est susceptible d'être diérente de celle sur nos propres lunettes. Pour
permettre une meilleure portabilité, nous laissons à l'utilisateur la possibilité d'indiquer,
en millimètres, la distance qui sépare les deux leds sur ses lunettes infrarouges (ou tout
autre dispositif ). La valeur par défaut de 215mm correspond à la distance entre les leds de
la Sensor-bar de la Wii, que nous avons utilisée lors de la réalisation de notre application.
Hauteur Ecran
En fonction de la taille de l'écran, l'application se comporte diérem-
ment si l'utilisation d'unités écran est activée. Puisque la taille de l'écran est susceptible
de varier d'un utilisateur à l'autre, il est possible via ce champ de texte de modier la
hauteur de l'écran (la largeur sera calculée en fonction de la hauteur, et le ratio peut être
calculé facilement à partir de la résolution de l'écran).
Eet de stra
Lors de l'utilisation du headtracking, deux visions sont envisageables :
Si le bouton
eet de stra
est coché, le point de vue observé par la caméra se
déplace de la même façon que l'utilisateur. Cela signie que le module se comporte
comme si l'utilisateur regardait toujours devant lui, lorsque sa position par rapport
à l'écran est modiée. Si l'utilisateur regarde face à lui-même, le terme
stra
est
couramment utilisé dans les jeux vidéos pour qualier un déplacement latéral qui
entraîne la modication du point de vue, pour suivre les mouvements du personnage.
79
(a) Onglet Mesures
(b) Onlget Wiimote
Figure 5.3: Onglets de la partie Ouest
Dans le cas contraire, l'utilisateur regarde toujours le même point. Un déplacement
de l'utilisateur n'entraîne alors pas de modication du point de vue, et le sujet a
alors l'impression de tourner autour du point de vue xé par la caméra.
En fonction de la scène projetée, il est parfois plus propice d'activer l'eet de stra. C'est
par exemple le cas du jeu de pong, où l'eet est plus intéressant lorsque le stra est activé.
Avec l'application Targets, au contraire, la qualité du headtracking est améliorée lorsque
l'eet de stra est désactivé.
Caméra sous écran
Si les unités écran sont utilisées, il devient indispensable de tenir
compte de la position de la Wiimote par rapport à l'écran. En eet, si l'utilisateur est
situé à moins d'un mètre de l'écran, le visage situé à la hauteur du dessus de l'écran, la
scène doit être achée pour être vue du dessus. Si la position de la Wiimote n'est pas
connue, il est impossible de tenir compte de cette information.
Imaginons les deux situations, sans tenir compte de la position de la Wiimote. Si celleci est placée sous l'écran et que la tête de l'utilisateur se trouve à la hauteur du dessus
de l'écra, alors les leds infrarouges seront situées sur la partie supérieure du capteur.
Inversemment, si la Wiimote est placée au-dessus de l'écran, les leds infrarouges étant
situées à la même hauteur que le capteur, les sources infrarouges seront situées sur la
partie centrale du capteur. Dans le cas où nous utilisons des unités écran, cette diérence
est très importante.
Unités écran
Plutôt que de calculer la position absolue de l'utilisateur par rapport à
l'écran (e.g. le sujet est plaçé 1m devant la caméra et 40cm sur la gauche). Il peut être
intéressant de considérer l'écran comme un cube unitaire à l'intérieur duquel on peut
travailler. Cette fonctionnalité est très utile pour développer des applications où le sujet
est assez proche de son écran et se trouve à la même hauteur que celui-ci. Avec les unités
80
écran, il est plus facile de savoir si l'utilisateur se trouve au dessus de l'écran, auquel cas la
caméra doit être placée au-dessus de la scène, où bien s'il se trouve en dessous de l'écran
(la caméra sera alors placée pour voir la scène du dessous). L'écran apparaît alors comme
une boîte à l'intérieur de laquelle une scène est achée.
Ce mode ne convient pas à des applications où les mouvements doivent simuler la
réalité, tel que le jeu de Pong. Les dimensions d'une table de tennis de table étant bien
connues par l'être humain, l'eet de réalisme ne sera pas optimal si le déplacement de
l'utilisateur ne concorde pas exactement à un déplacement représentant le même mouvement dans la scène OpenGL. Plus précisément, si la largeur de la table de tennis de table
est de 1m52, un déplacement latéral de 1m52 dans la réalité doit permettre de déplacer
la caméra d'un bord à l'autre de l'écran.
Pivoter Tête
Comme précisé à la section 1.5, un apport intéressant au module de
headtracking a été l'ajout d'un degré de liberté supplémentaire, permettant d'interpréter
le roulis de la tête pour le simuler dans nos applications utilisant le headtracking. L'utilisation de cet ajout ne se prête pas à toutes les formes de headtracking, c'est pourquoi
nous laissons la possibilité à l'utilisateur d'activer ou non cette fonctionnalité. Cocher
ce bouton met en place le système de roulis, et l'eet obtenu est alors comparable celui
obtenu lorsqu'une caméra embarquée est plaçée sur une moto qui enchaîne des virages
serrés.
5.2.2.2 Onglet Wiimote
Le second onglet de la partie ouest de notre application contient des informations
propres à la Wiimote. Les paramètres suivants sont modiables (voir gure 5.3b) :
Angle H/V Camera
La Wiimote possède un angle de vision de 42° horizontalement
et 33° verticalement. Ces valeurs sont largement utilisées dans le calcul de la position de
l'utilisateur par rapport à l'écran. Comme nous avons développé un module réutilisable
qui est indépendant du périphérique de capture utilisé, il est nécessaire de pouvoir modier
les angles de vision perçus par le matériel de détection. Si un développeur souhaite ajouter
la possibilité d'utiliser une nouvelle interface de capture, il peut modier ces valeurs pour
qu'elles correspondent aux angles de vision du matériel qu'il utilise.
Largeur/Hauteur Capteur
Dans la même optique que pour l'angle de vision, il est
nécessaire de pouvoir modier la résolution du capteur. Nous savons que la Wiimote est
équipée d'un capteur ayant une résolution de 1024x768 pixels. Ce sont donc les valeurs par
défaut présentes dans les champs de texte
Largeur/Hauteur capteur. Modier ces champs
permet à l'application de travailler avec un capteur ayant une résolution diérente de celle
de la Wiimote.
Pas
Pour étalonner les applications, nous verrons (voir section 5.2.6) qu'il est possible
d'utiliser le clavier. La valeur du pas permet de modier l'amplitude des mouvements
eectués à chaque appui sur une des touches permettant d'étalonner la scène. Par exemple
si le pas est mis à 2, apppuyer sur la touche 'Q' permet de déplacer de 2 unités OpenGL
81
vers la gauche la position de la caméra. La plupart de nos scènes ont comme convention
que une unité OpenGL correspond à un mètre dans la réalité. Par conséquent la longueur
d'une table de tennis de table est de 2,74 unités OpenGL.
La modication de ce paramètre est utile lorsque les unités écran sont utilisées, car la
valeur par défaut de 1 fait basculer la caméra en dehors de la scène. Remplacer le pas par
une valeur plus faible est alors nécessaire.
Sensibilité
Lorsque l'utilisateur dispose d'un espace réduit pour utiliser le headtracking,
il peut être intéressant d'amplier les mouvements perçus par la caméra. La sensibilité
est un facteur multiplicatif de la position réelle calculée. Par conséquent, une sensibilité
supérieure à 1 entraînera des mouvements plus importants à l'écran que les mouvements
réellement eectués, alors qu'une valeur inférieure à 1 (et supérieure à 0) aura tendance
à diminuer l'amplitude des mouvements eectués.
En pratique une valeur de deux pour la sensibilité aura pour eet de doubler l'intensité
de chaque mouvement. Un mouvement de un mètre vers la gauche sera donc traduit dans
l'application par un mouvement de deux mètres dans la même direction.
Largeur bordure
Parmi les fonctionnalités supplémentaires oertes par notre appli-
cation, il est possible de contrôler sa position par rapport à la Wiimote, an d'éviter
de sortir de son champ de vision (voir section 3.4.1). La valeur qui est indiquée dans ce
champ de texte correspond à la largeur brute en pixel à considérer comme en bordure du
capteur. La valeur par défaut est 150, cela signie que sur un capteur d'une résolution de
1024x768, les bords sont sur l'intervalle :
1. X=[0-150] pour le bord droit (Notons que lorsque l'utilisateur se déplace vers la
gauche, les sources se déplacent sur la droite du capteur. Puisque le dispositif de
capture est situé en face de l'utilisateur, la gauche et la droite sont inversées).
2. X=[874-1024] pour le bord gauche.
3. Y=[0-150] pour le bord inférieur
4. Y= [618-768] pour le bord supérieur
Modier la valeur présente dans ce champ de texte permet donc d'utiliser une valeur
diérente pour la coloration des bords de l'écran, lorsque l'utilisateur s'approche du champ
de vision limite de la caméra.
Taille Interpolation
Lorsque l'utilisateur s'éloigne de l'écran, nous savons que le cal-
cul de la distance est de plus en plus dicile et que les erreurs deviennent de plus en
plus importantes. A 1 mètre, l'image est assez stable, mais dès que le sujet se trouve à
plus de deux mètres de la caméra, la résolution du capteur ne permet pas de détecter
avec précision la distance à laquelle il se trouve, à cause du caractère hyperbolique de
la distance entre les sources infrarouges sur le capteur (discuté à la section 2.3). Cette
perte de précision entraîne des tremblements dans l'image, car d'un évènement à l'autre,
la distance calculée entre la caméra et le sujet varie de plusieurs dizaines de centimètres.
Pour limiter ce problème, nous avons ajouté une fonction de lissage qui s'applique
uniquement sur l'axe de profondeur (voir section 3.4.2). Par défaut, nous nous basons
sur les 20 évènements qui précèdent pour lisser le mouvement. La modication de cette
82
valeur permet de tenir compte d'un nombre diérent d'évènements. Une valeur inférieure
limitera l'eet de lissage et aura tendance à intensier les tremblements, alors qu'une
valeur supérieure aura tendance à augmenter le lissage, et par conséquent à diminuer la
réactivité de la profondeur sur la scène. Puisque la mise à jour de la profondeur dépend
des points précédents, un déplacement rapide sera eectué de façon moins réactive que
dans la réalité, car les points concernant les positions précédentes sont impliqués dans le
calcul de la nouvelle position.
Distance sensible
Lorsque la sensibilité est modiée, tous les mouvements sont modi-
és (latéraux, verticaux et en profondeur). Dans certains cas, il peut être intéressant de
ne modier la sensibilité que pour les déplacements verticaux et latéraux, sans aecter
l'intensité donnée aux mouvements de profondeur. En désactivant cette option, la profondeur devient insensible au facteur
Sensibilité. De cette façon, le fait de s'éloigner ou de se
rapprocher ne dépend pas de la sensibilité.
Cette fonctionnalité a de l'intérêt pour jouer au Pong, qui est plus facile à jouer avec
une Sensibilité de deux, mais en laissant la profondeur insensible à ce facteur.
5.2.3 Région Est
La région
est
de notre application est composée de deux onglets, le premier concerne
les paramètres propres à chacune des scènes, alors que le second permet d'acher une vue
virtuelle de la position de l'utilisateur par rapport à l'écran.
5.2.3.1 Onglet Paramètres
L'onglet paramètres a pour but de contenir les variables qui sont propres à chaque
scène achable avec notre application graphique. Pour chacune d'entre elles, diérentes
congurations spéciques existent. Les paramètres propres à un jeu de pong n'étant pas
les mêmes que ceux d'un acheur de forme, il était nécessaire d'avoir un panneau qui soit
modié en fonction de la scène achée.
Jeu de Pong
Les premiers paramètres dont nous discuterons concernent le jeu de Pong.
Les variables propres à cette scène ainsi que leur utilité sont expliquées ci-dessous et
représentées sur la gure 5.4.
Raquette X/Y/Z
Lorsque l'application
Pong
est lancée, la raquette se trouve par
défaut juste un mètre en avant de la caméra. Les trois sliders Raquette X/Y/Z permettent
de modier la position de la raquette pour l'adapter à la convenance du joueur. De cette
façon, il est par exemple possible d'adapter le jeu pour un gaucher qui tient sa raquette
avec la main gauche. Déplacer le slider X permet de déplacer la raquette latéralement,
alors que le Slider Y permet de faire avancer ou reculer la raquette. Enn, le dernier slider
permet de postionner la raquette verticalement.
Vitesse Balle
La balle de tennis de table suit les équations issues des lois de mé-
canique simple. Les mouvements selon les axes latéraux et de profondeur suivent une
équation de type rectiligne uniforme (MRU), alors que le déplacement vertical est soumis
83
à la pensanteur et est par conséquent représenté par une loi rectiligne uniformément accélérée (MRUA). A travers un écran et à vitesse réelle, les mouvements sont bien souvent
perçus comme trop rapides, car la taille de l'écran fait apparaître une balle plus petite
qu'en réalité, laquelle est assez dicile à situer dans l'espace durant le jeu. Pour parer
à ce problème, le slider
Vitesse Balle
permet de modier la vitesse de la balle. Ralentir
cette dernière rend alors le jeu plus facile alors que l'accélérer le rend plus dicile. Par
défaut, la balle se déplace à 50% de sa vitesse réelle, une valeur raisonnable pour un joueur
moyen.
Taille Raquette
Dans notre application, la taille de la raquette de ping pong est
d'environ 12 centimètres de diamètre. Il s'agit approximativement de la taille ocielle.
Malheureusement, dans le monde des jeux vidéos, les constantes de taille peuvent rarement
être respectées, car cela rend le jeu tellement dicile qu'il risque de ne pas être amusant.
Pour ce faire, les dimensions sont souvent revues. Lorsque le but du jeu est d'attraper
une balle avec une raquette, sa taille est très souvent supérieure à la taille réelle de la
raquette, an d'augmenter la jouabilité. Un slider a donc été intégré, an de permettre à
l'utilisateur de régler la taille de la raquette à sa convenance.
Raquette Transparente
Augmenter la taille de la raquette à une valeur deux fois
plus grande que sa taille originale peut poser un problème de visibilité. Lorsque celle-ci
grandit, elle cache une partie de la table et il devient dicile de suivre la trajectoire de la
balle de façon confortable. Pour permettre au joueur de voir la balle derrière la raquette,
même lorsque celle-ci a une taille importante, le bouton
raquette transparente
permet,
comme son nom l'indique, de rendre la raquette transparente, an de distinguer ce qui se
trouve derrière. De cette façon, le jeu est beaucoup plus confortable.
Utiliser Main
Jouer au pong en utilisant la tête pour déplacer le personnage virtuel
est un concept assez intéressant, mais pas forcément pratique. Au niveau du gameplay,
cela donne tout simplement l'impression d'avoir une raquette collée sur le torse ! Pour
améliorer l'expérience, nous avons ajouté la possibilité le contrôler la raquette en utilisant
la main. Grâce à cette fonctionnalité supplémentaire, il est possible de déplacer son personnage virtuel en bougant la tête, mais il est également permis de déplacer la raquette
de façon latérale et verticale, en étant équipé du gant infrarouge dont nous avons parlé
à la section 2.1.2. Ce gant infrarouge n'étant équipé que d'une seule led, il n'est pour le
moment pas encore possible de modier la profondeur de la raquette indépendamment de
celle du visage, mais cet ajout pourrait faire l'objet d'une des premières mises à jour de
l'application.
Vue en 3D
Ce bouton permet de visualiser la scène en anaglyphe, orant alors un
eet de relief au joueur équipé de lunettes à anaglyphes. La vue en 3D projette une image
pour chaque oeil, comme expliqué à la section 2.1.1.
Targets
Passons à présent à l'explication des paramètres mobiles de l'application tar-
gets. Ces paramètres sont visibles sur la gure 5.7 qui nous donne un aperçu complet de
l'application lorsque la scène visualisée est
Targets.
84
Figure 5.4: Paramètres du Jeu de Pong
Brouillard
Le slider
brouillard
permet de modier l'intensité du brouillard. Le
brouillard aché suit une fonction linéaire, et la valeur achée sur le slider correspond
au pourcentage de clarté désiré. Plus la valeur est faible, moins la boîte à l'intérieur de
laquelle se trouvent les cibles est visible. Lorsqu'aucun brouillard n'est présent, la présence
de la grille est assez prononcée. Il est également possible de la rendre invisible lorsque le
pourcentage de visibilité est proche de zéro.
Replacer Cibles
Ce bouton permet de repositionner les cibles de façon aléatoire à
l'intérieur de la boîte. La fonction de positionnement implémentée agit de telle sorte à
ce qu'au moins 3 cibles se trouvent à l'avant plan. Grâce à cette génération aléatoire, il
est possible d'avoir une multitude de dispositions diérentes, qui peuvent servir de base
à un jeu de tir où le but du jeu serait d'éliminer le plus rapidement possible les cibles
présentes à l'écran. Ce petit jeu constitue une possibilité d'évolution de la scène
Targets.
L'élimination des cibles peut se faire grâce à un curseur qu'il serait possible de déplacer
en utilisant les mains. L'action de tir peut être eectuée grâce à un gant contenant deux
leds infrarouges situées à l'extrémité du pouce et de l'index. Un algorithme serait alors
chargé de détecter la jointure des doigts, qui déclencherait alors le tir.
Nb cibles
Ce petit champ de texte est destiné à modier le nombre de cibles achées
à l'écran. Par défaut dix cibles sont achées. La présence de cette variable se justie elle
aussi par la possibilité d'évolution de la scène en un petit jeu de tir/réexe. Le nombre de
cibles constituerait alors la diculté. Une fois le jeu lancé, l'idée est de laisser 10 ou 20
secondes à l'utilisateur pour détruire le plus de cibles possible. L'intérêt du jeu est assez
limité avec des cibles qui ne bougent pas, mais la principale utilité de l'application est de
donner un aperçu des possibilités oertes par le headtracking.
Vue Frustum/perspective
En OpenGL, plusieurs types de matrices de projection
existent, parmi celles-ci, deux sont particulièrement intéressantes pour notre application
gluPerspective, qui dénit une projection perspective pour laquelle les objets éloignés apparaissent plus petits (voir gure 5.5). La fonction gluPerspective() calcule la
85
Figure 5.5: Projection Perspective [7]
pyramide visible en tenant compte de l'angle de vision (fovy), de l'aspect de l'écran
(aspect), de la zone visible la plus proche (glNear), et du point visible le plus éloigné
(glFar). La fonction appelle ensuite glFrustum pour générer cette pyramide.
glFrustum est une fonction de plus bas niveau, qui ore plus de possibilités que
gluPerspective. La spécication de la pyramide d'achage est déterminée par 6 paramètres qui sont les plans de clipping gauche (left), bas(bottom), haut (top) et
droit (right), ainsi que le point le plus proche visible (zNear) et le point le plus
éloigné (zFar). En utilisant directement cette projection, la profondeur agit diéremment lorsque la caméra s'éloigne ou s'approche du point de vue. Lorsque la
caméra s'éloigne du point de vue, la scène se comprime sur l'axe de profondeur et
lorsqu'elle se rapproche, la scène se dilate sur ce même axe. Utilisée en combinaison
avec le headtracking, cette projection donne davantage l'impression que l'écran est
une boîte, à l'intérieur de laquelle se trouvent des cibles. Avec une projection perspective traditionnelle, les cibles s'éloignent lorsque l'utilisateur s'écarte de l'écran.
Avec l'utilisation explicite de glFrustum, la grille se comprime et semble alors moins
profonde lorsque l'utilisateur s'en éloigne.
La présence des boutons
vue frustum
et
vue perspective
permet donc de choisir entre ces
deux types de projection. Le comportement de l'axe de profondeur est alors diérent en
fonction du choix eectué.
Vue en 3D
Tout comme pour le jeu de Pong, il est possible de visualiser cette scène
avec un eet de relief en portant des lunettes à anaglyphes.
86
Figure 5.6: Projection Frustum [7]
Espace yeux
La matrice de projection issue de glFrustum se comportant d'une fa-
çon diérente à la matrice de projection gluPerspective, l'espacement entre les yeux est
diérent lorsque cette projection est utilisée. Il est alors nécessaire d'étalonner l'espacement entre les deux images projetées. Cet étalonnage est rendu possible grâce au slider
espace yeux.
De plus, cet étalonnage permet également d'adapter la 3D à l'utilisateur.
En eet, nous avons remarqué que certains utilisateurs perçoivent mieux le relief lorsque
l'écart entre les deux images est plus important.
Eet Fenêtre
Discutons à présent des paramètres modiables pour la scène
nêtre. La partie graphique concernant ces variables est visible sur la gure 5.8.
Charger Image
Eet Fe-
Ce champ de texte a pour but de modier l'image utilisée pour
l'eet fenêtre, en spéciant le lien complet vers cette image. Notons que pour importer
des textures en OpenGL, il est nécessaire que leur taille soit une puissance de deux. Nous
avons donc redimensionné les images à l'aide d'un logiciel externe.
Largeur Champ
Lorsqu'une image est chargée, le programme doit connaître la
largeur, en centimètres, que représente cette image. Par exemple si une photo d'une cuisine
est montrée, une largeur possible est 500 centimètres. Cette valeur est nécessaire pour que
le programme puisse calculer correctement l'achage.
87
Figure 5.7: Paramètres des cibles
Figure 5.8: Paramètres de l'eet fenêtre
88
Figure 5.9: Paramètres des Formes 3D
Ratio Image
Les images chargées par OpenGL doivent avoir une taille en pixels
qui est une puissance de deux. Les images chargées sont donc redimensionnées et par
conséquent déformées. Connaître le ratio de l'image est indispensable pour acher celleci avec le bon format.
Hauteur Ecran
L'écran doit agir comme une fenêtre. Il est donc nécessaire de
connaître la hauteur de cette fenêtre an de calculer correctement la partie de l'image qui
doit être achée. Ce champ de texte permet donc de spécier la hauteur, en centimètres,
de l'écran.
Ratio Ecran
Pour calculer exactement les dimensions de l'écran, connaître son ratio
est nécessaire, pour déduire sa largeur à partir de sa hauteur. Ce champ de texte est prévu
pour indiquer le ration de l'écran, sous la forme d'un seul nombre à virgule ottante.
Distance Image
Le dernier paramètre nécessaire au calcul de la zone visible de
l'image est la distance à laquelle se trouve l'image. Cette distance doit être spéciée en
centimètres.
Par exemple, si nous reprenons le cas où l'image représentée est une cuisine, une
distance possible est 300 centimètres.
Formes 3D
Lors de la visualisation de formes en 3D, certains paramètres sont également
modiables. L'interface graphique correspondant à cette scène est visible à la gure 5.9
et la description des variables est détaillée ci-dessous.
Forme
Cette Combo box permet de choisir quelle forme nous souhaitons représen-
ter sur notre scène, chacune des formes est représentée avec la primitive
89
l de fer
pour
permettre une meilleure visualisation du relief en 3D. Il est possible de choisir parmi les
formes suivantes :
Mesh, représente une simple grille
Box, représente un cube
Sphere
Pulsar, il s'agit d'une représentation simpliée d'un pulsar.
Knot, une forme géométrique qui représente une sorte de courbe de bézier noueuse.
Tritorus, un tore en trois dimensions.
Lorenz, qui représente une gure de Lorenz.
Vitesse de Rotation
Chacune des formes représentée à la possibilité de pivoter sur
elle même. Le déplacement de ce slider permet de modier la vitesse de rotation de la
forme représentée.
Taille Points/Lignes
Ce slider permet de tracer des lignes ou des points plus épais.
Cela est intéressant car en fonction de l'écran utilisé, l'eet de relief est de meilleure qualité
lorsque les traits sont plus épais. Cela donne aussi un léger lissage aux formes générées
qui semblent moins pixélisées qu'avec une épaisseur de 1 seul pixel.
Vue en 3D
De façon identique aux autres scènes présentées plus haut, il est possible
de visualiser les formes en trois dimensions. C'est d'ailleurs là que se situe tout l'intérêt
de cette scène, qui a été initialement conçue pour acher des formes en 3D, grâce à la
technique d'anaglyphe.
Idées concernant le Visualisateur de Photos
Nous terminerons par le visualisateur de photo. Ce dernier ne possède encore aucun paramètre modiable. Toutefois, certains ajouts pourraient s'avérer intéressants, comme par
exemple la possibilité de charger un dossier sur la machine, an d'en visualiser les photos.
La version existante du visualisateur de photos étant réalisée dans un but plutôt pratique,
elle n'intègre pas la possibilité de changer le dossier visité. Toutefois, il est possible de
visualiser d'autres photos en les ajoutant dans le dossier parcouru par l'application, qui
se chargera alors de les importer pour les acher.
Un autre paramètre intéressant serait de pouvoir faire varier la vitesse de rotation et
d'accélération du cube, pour adapter sa vitesse à la convenance de l'utilisateur. Il pourait
également être intéressant de modier l'intensité du facteur de zoom et de déplacement,
lors d'un mouvement sur une image zoomée.
5.2.3.2 Onglet Contrôles
Le but de l'onglet
contrôles est de fournir un aperçu rapide de la position de l'utilisateur
par rapport à la caméra. Sur cette partie de l'interface graphique sont dessinés de façon
schématique l'écran, la Wiimote, l'utilisateur et le champ de vision de la Wiimote (voir
gure 5.10). Ce schéma est vu du dessus, les mouvements verticaux ne sont donc pas
modélisés sur ce petit écran. L'utilisateur est représenté par un point, et est relié par un
segment à la Wiimote virtuelle. Cette visualisation permet d'apporter à l'utilisateur un
certain contrôle de sa position horizontale et en profondeur, cet écran peut être utilisé en
90
Figure 5.10: Onglet contrôles
Figure 5.11: Région Nord de l'application
plus du système de détection des bords pour anticiper toute sortie du champ de vision.
Cette petite fenêtre graphique permet également de vérier en temps réel la qualité du
headtracking.
5.2.4 Région Nord
Une autre partie importante de l'interface graphique est la région nord. Cette région
possède deux fonctionnalités détaillées ci-dessous, qui sont visibles sur la gure 5.11
Changer de scène
Notre interface graphique a pour but de regrouper, dans une seule
application graphique, toutes les scènes créées pour illustrer les concepts développés dans
le cadre de ce travail. Un point clé de l'application est donc la possibilité de pouvoir
basculer d'une scène à l'autre. C'est cette possibilité qui est oerte par la présence de la
Combo
changer de scène. Cette boîte de dialogue permet de choisir quelle scène acher
dans la région centrale.
Mode plein écran
Un autre intérêt de l'application est de pouvoir basculer en mode
plein écran, an de proter de toute la surface d'achage pour représenter la scène.
L'appui sur le bouton plein écran permet de créer une nouvelle fenêtre ayant pour taille la
résolution de l'écran et ne possédant pas de décoration (le bord de la fenêtre qui permet
notamment de la réduire ou de la fermer). Ce plein écran est donc assez articiel, mais il
constitue la façon la plus simple de réaliser une fenêtre graphique utilisant tout l'espace
de l'écran. Notons que la création d'un vrai plein écran en Java est assez dicile et ne
fonctionne pas toujours, en fonction de la machine sur laquelle on utilise l'application.
91
Figure 5.12: Région Sud de l'application
Le but n'étant pas de créer l'application graphique la plus performante possible, nous ne
nous sommes donc pas focalisés sur cet aspect.
5.2.5 Région Sud
La partie sud de l'application possède un petit label de texte, dont le but est d'acher
en temps réel, durant l'utilisation de l'application, la position de la caméra et de l'oeil
(voir gure 5.12). Ces positions sont mises à jour à chaque évènement. La raison de leur
présence est similaire celle de l'onglet
contrôles
dont nous avons discuté au point 5.2.3.2.
La présence de ces labels permet de contrôler en temps réel la position de l'utilisateur par
rapport à la caméra, ainsi que l'évolution de cette position.
5.2.6 Région Centrale
La partie principale de notre application se trouve sur la région centrale. C'est à cet
endroit qu'est représentée la scène 3D. Lorsque l'utilisateur souhaite modier celle-ci via
la combo box située sur la région nord, ce panel est alors rafraîchi pour acher la scène
choisie.
Utilisation du clavier
Chaque scène étant liée à un gestionnaire de clavier, il est
possible d'utiliser ce dernier pour modier certains paramètres génériques applicables sur
l'ensemble des scènes proposées. Pour pouvoir utiliser le clavier, il est nécessaire que le
focus de l'application soit situé sur la région centrale. Pour prendre le focus, il sut de
cliquer avec le bouton gauche de la souris sur la scène 3D.
L'appui sur les touches Z et S du clavier permet respectivement d'avancer ou de
reculer le long de l'axe de profondeur (c'est la caméra de OpenGL qui est déplacée).
Les touches Q et D permettent quant à elles de déplacer la caméra vers la gauche
ou la droite.
L'appui sur une des touches A ou E permet de se déplacer verticalement, de bas en
haut en appuyant sur A, et de haut en bas en appuyant sur E.
Les èches directionnelles du clavier permettent de modier le point de vue xé par
la caméra. Alors que les touches Haut / Bas permettent de déplacer le point de vue
de haut en bas, les touches Gauche / Droite peuvent être utilisées pour le déplacer
de gauche à droite. Enn, les touches W et X permettent respectivement de faire
avancer ou reculer le point de vue sur l'axe de profondeur.
Il est possible d'utiliser les touches
Page Up et Page Down, pour augmenter ou dimi-
nuer la sensibilité. L'augmentation de la sensibilité permettant en outre d'amplier
ou de réduire l'intensité des mouvements virtuels par rapport aux mouvements réels.
Ces touches modient également la sensibilité des touches Z, S, Q, D, A, E, W, X,
Haut, Bas, Gauche et Droite.
L'appui sur la touche R permet d'activer la connexion d'une Wiimote à la scène.
Pour lier la Wiimote, il est nécessaire d'appuyer simultanément sur les boutons A et
92
B de la petite télécommande. Après avoir appuyé sur ces boutons, les leds de la Wiimotent se mettent à clignotter, et 30 secondes sont disponibles pour la synchroniser
à l'ordinateur, en appuyant sur la touche R du clavier. Une fois la synchronisation
eectuée, la manette émet une légère vibration et le détecteur infrarouge est automatiquement activé. L'utilisateur peut alors se déplacer pour proter du système
de headtracking.
La touche I permet, quant à elle, d'activer le détecteur infrarouge. L'ajout de cette
fonction a été nécessaire car l'activation de la caméra infrarouge ne fonctionne pas à
tous les coups. Il est donc parfois nécessaire d'appuyer plusieurs fois sur ce bouton
pour activer la caméra infrarouge de la Wiimote, qui doit avoir été préalablement
connectée à l'ordinateur, grâce à l'appui sur la touche R.
93
Chapitre 6
Conclusion
Le headtracking est un procédé utilisé dans le cadre de la réalité virtuelle qui a un
bel avenir devant lui, tant les applications sont nombreuses et intéressantes. Ce document
nous a permis de nous familiariser avec le principe de headtracking et de comprendre
comment il est possible de développer un programme qui met en application ce procédé.
Tout au long de notre travail, nous avons présenté notre approche du problème qui
consiste à utiliser la caméra infrarouge de la wiimote pour détecter des sources d'infrarouges posées de part et d'autre d'une paire de lunettes portées par un utilisateur. Nous
avons également détaillé le matériel utilisé pour mettre en place notre système de headtracking ; il s'agit principalement d'une wiimote, d'une paire de lunettes équipées de leds
infrarouges et d'un gant infrarouge, pour détecter la position de la main (Le terme handtracking est alors employé). En plus des outils utilisés, nous avons également parlé de
moyens alternatifs utiles à la mise en oeuvre d'un système de headtracking plus performant (utilisation de lunettes à polarisation, remplacement de la Wiimote par une caméra
stéréoscopique, ...). Ensuite, nous avons présenté les concepts théoriques qui ont été utilisés pour implémenter notre module. En eet, nous avons vu comment calculer la distance
entre l'utilisateur et la caméra, ainsi que l'angle formé entre la normale de cette caméra et
la position l'utilisateur, en utilisant les règles de trigonométrie dans un triangle rectangle.
Quelques rappels de mécanique de base ont également été proposés, ceux-ci étant utilisés
pour la modélisation de la trajectoire de la balle, dans notre application de tennis de table.
Après avoir développé notre approche, nous avons parlé des diérentes fonctionnalités
de notre module, dont les plus importantes sont comment le headtracking est géré, comment éviter que l'utilisateur ne sorte du champ de vision de la caméra, et comment gérer
les tremblements perçus à une plus grand distance. Nous avons aussi parlé d'autres fonctions telles que la gestion de plusieurs caméras ou encore l'ajout de la 3D stéréoscopique.
Pour terminer, nous avons introduit quelques exemples d'applications du concept de
headtracking qui ont été réalisés pour mettre en application les outils développés. Parmi
ces applications, un jeu de tennis de table, un eet fenêtre, un visualisateur de formes, ou
de photographies ont notamment été présentés.
94
Bibliographie
B
[1] Paul Bourke.
Creating and viewing anaglyphs,
~pbourke/texture_colour/anaglyph/.
http://local.wasp.uwa.edu.au/
D
[2] Téléchargement de Cwiid.
http://packages.debian.org/fr/sid/libcwiid-dev.
[3] Explorateur de chiers Nautilus.
[4] Eet
de
http://doc.ubuntu-fr.org/nautilus.
http://en.wikipedia.org/wiki/File:TV_ghosting_
ghosting.
interference.jpg.
http://psp-white.blog.playersrepublic.fr/
images/medium_playstation_eye_cam_sc1.jpg.
[5] Image de la Playstation Eye.
[6] Site Web de OpenGL.
http://www.opengl.org/.
[7] Matrices de projection.
vbaopengl/?page=page_3.
[8] Téléchargement
E
de
http://arkham46.developpez.com/articles/office/
WiiRemoteJ
(Documentation
world-of-cha0s.hostrocket.com/WiiRemoteJ/.
incluse).
http://www.
Playsation
Eye.
http://www.generation-nt.com/
ps3-playstation-eye-head-tracking-actualite-69060.html.
[9] Sony
G
[10] GDC.
H
53.
http://www.computer.org/portal/web/csdl/doi/10.1109/MPRV.2008.
[11] Chris Harrison.
[12] Webcam
http://www.chrisharrison.net/.
Headtracking.
headtracking+webcam\&aq=f.
http://www.youtube.com/results?search_query=
95
I
[13] Java Native Interface.
http://java.sun.com/docs/books/jni/.
J
[14] JOGL.
https://jogl.dev.java.net/.
L
[15] Johnny Chung Lee.
http://johnnylee.net/projects/wii/.
M
[16] Java
O
Virtual
Machine.
Java.
[17] OpenCV.
http://fr.wikipedia.org/wiki/Machine_virtuelle_
http://opencv.willowgarage.com/wiki/.
P
[18] projectnatal wiki.
R
MPRV.2008.53.
http://www.computer.org/portal/web/csdl/doi/10.1109/
[19] Statistically rigorous java performance evaluation.
S
1297105.1297033.
http://doi.acm.org/10.1145/
http://a10.idata.over-blog.com/0/00/47/87/photos/
pr-060117-3dvx3-3qtr_lg.jpg.
[20] Caméra stéréoscopique.
T
[21] Eye Toy.
http://www.eyetoy.com/shared/locale.asp?returnURL=/index.asp.
http://flightsimx.s3.amazonaws.com/
wp-content/uploads/2009/05/001-550x470.jpg.
[22] dispositif
de
capture
Track
IR.
infrarouge Track IR.
http://www.trackir.fr/19-67-thickbox/
classic-bundle-trackir-pro-trackclip-pro.jpg.
[23] Emetteur
96
W
http://images.wikio.com/images/s/2da/
creative-webcam-live-effects.jpeg.
[24] Webcam.
[25] Wiibrew.
http://wiibrew.org/wiki/Wiimote/.
[26] sur Sourceforge Wiiuse.
http://sourceforge.net/projects/wiiuse/.
[27] Project Hosting on Google Code WiiuseJ.
[28] Wikipedia.
2008.53.
http://code.google.com/p/wiiusej/.
http://www.computer.org/portal/web/csdl/doi/10.1109/MPRV.
97