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