Romain V ERGNE [email protected] visualisation non-photoréaliste de scènes urbaines sur terminaux mobiles août 2007 Encadrant : Xavier G RANIER INRIA Futurs, LABRI, Domaine Universitaire, 351, cours de la Libération, 33405 TALENCE Contexte et objectifs du stage L’objectif de ce stage est d’améliorer le rendu de façades stylisées de Quillet et al. [QTM05, QTG+ 06] dans le cadre d’un contrat avec France Télécom. Leur travail consiste à utiliser les techniques de rendus non photoréalistes pour détecter et afficher les lignes des façades sur un grand modèle 3D urbain. Il s’agit d’un plugin de la plateforme Elkano [Mar04] dont le but est de visualiser rapidement une grande quantité de données sur terminaux mobiles avec une approche client serveur. Leur méthode est la suivante : 1. détection des arêtes sur les textures des façades des batiments (précalculs), 2. vectorisation de ces arêtes : création de couples de points définissant le point d’origine et le point d’arrivé de chaque ligne (précalculs), 3. pendant l’affichage, le serveur envoie ces couples sur le réseau, 4. le client reçoit les points et affiche les lignes correspondantes. F IG . 1: Pipeline utilisé par Quillet et al. [QTM05, QTG+ 06]. Les points forts – Une faible quantité de données transite sur le réseau. les lignes sont envoyées à la place d’une texture et cela peut diminuer la taille des données à envoyer d’un facteur de 10. – leur méthode est progressive. Le serveur n’envoit pas toutes les lignes d’un seul coup. L’affichage des lignes coté client se fait donc petit à petit. – Ils utilisent une approche multirésolution. Suivant la distance d’une façade par rapport à l’observateur, le nombre de lignes affichées ne sera pas le même. Plus la façade est proche, plus il y aura de détails et inversement. Les points faibles – Les lignes reçues par le client ne sont pas affichées sous forme de texture, mais avec des traits dans l’espace 3D. Ils sont placés à coté des façades (car cela provoque des erreurs de gestion de profondeurs en les positionnant exactement sur les murs), il y a donc un espace entre les façades et les lignes. Il en résulte aussi un problème lié aux faces arrières. Lorsqu’on est positionné derrière un bâtiment, ses façades ne s’affichent pas forcément, mais les traits le sont obligatoirement. Il apparait donc parfois les lignes d’un bâtiment que l’on ne voit pas. – Leur méthode est limitée à l’affichage des lignes. Elle ne permet pas de faire d’autres types de rendu non-photoréalistes. 2 Objectifs du stage – Généraliser les travaux de Quillet et al. [QTM05, QTG+ 06] en utilisant des textures vectorielles [Gro03]. Ces textures ont la particularité d’être indépendantes de la résolution souhaitée et sont généralement beaucoup moins volumineuses que les textures matricielles. – Utiliser la spécification d’OpenVG [wg07] qui permettra d’obtenir un rendu rapide avec les clients dotés d’un processeur graphique vectoriel. – Créer de nouveaux rendus pour illustrer la généricité de la méthode. Fonctionnement Vue d’ensemble La plateforme Elkano lit des fichiers vrml [vrm97] et fonctionne avec un graphe de scène. Il est possible de personnaliser les noeuds du graphe de scène comme on l’a fait ici pour afficher des textures vectorielles. Prenons l’exemple d’un fichier vrml simple : DEF objet Transform { children Shape { appearance Appearance { texture ImageTexture { url "grid.svg" } } geometry Box { size 3 3 3 } } translation 1 0 0 } Un visualiseur quelconque ne pourrait pas lire ce fichier car il ne connait pas le format SVG pour une texture. Nous avons mis en place un plugin permettant de les lire. Celui-ci est alors automatiquement appelé lorsqu’une telle texture est trouvée. Le pipeline utilisé est illustré par la Figure 2 : F IG . 2: Pipeline (simplifié) utilisé pour le plugin de rendu de textures vectorielles Le serveur lit le fichier SVG (avec la bibliothèque libsvg [lib05]), récupère chacun des éléments dans un tableau, puis les envoit au client (qui peut lui en demander seulement un certain nombre). Le client utilise alors la biblothèque OpenVG [wg07] pour afficher les éléments dans une texture qui est ensuite plaquée sur les façades des bâtiments. 3 Principales méthodes La classe TextureVecNodeCreator contient les méthodes qui associent la plateforme Elkano à notre plugin (PLGTextureVec). La méthode getExtensions permet de dire pour quel type de fichier un noeud TextureVecNode doit être créé. Il est aussi possible d’aller chercher des informations dans les fichiers de configuration. Nous permettons au client d’influencer la taille des packets qui transitent sur le réseau car nos textures sont progressives (fichier Magellan/Scripts/{Linux|Win32|WinCE}/client.cfg), nous y reviendrons plus tard. La classe TextureVecNode contient les méthodes principales pour lire coté serveur et afficher coté client. Voici une description des principales méthodes publiques utilisées : – computeVS (héritée de MGFNode) : méthode appelée à chaque rendu et utilisée par le client pour demander un certain nombre d’éléments SVG au serveur. – settleInPVS (héritée de MGFNode) : méthode utilisée pour créer et initialiser la texture qui sera affichée (PVS=Potentially Visible Set). Au cas où on y passe plusieurs fois, on dispose d’une variable qui permet de savoir si la texture concernée est déjà visible (et dans ce cas, pas besoins de la créer de nouveau). – removeFromPVS (héritée de MGFNode) : destruction de la texture. – serialize (héritée de MGFNodeRemote) : le serveur utilise cette méthode pour transmettre des données au client via le réseau. – unserialize (héritée de MGFNodeRemote) : le client reçoit les données du serveur. – mergeImport (héritée de MGFProgressiveNode) : si ce n’est pas la première fois que le client reçoit des données du serveur, alors le noeud du graphe de scène qui reçoit ces données n’est pas le client mais un noeud temporaire. Cette méthode est donc utilisée par le noeud temporaire pour récupérer les variables nécessaires au calcul de la progression dans le véritable client. Nous l’utilisons ici pour récupérer le contexte EGL, obligatoire pour créer des éléments OpenVG. Cette fonction est bloquante, il ne faut donc pas trop mettre de calculs à l’intérieur. – mergeCompute (héritée de MGFProgressiveNode) : on vient de récupérer les variables avec la méthode précédente ; on peut donc s’en servir ici. On l’utilise pour activer le contexte EGL associé aux éléments que l’on veut créer (celui qui correspond au noeud du “vrai” client). – mergeExport (héritée de MGFProgressiveNode) : méthode opposée à mergeImport. Le noeud temporaire va envoyer les changements au client. Il demande au client de créer les éléments OpenVG qu’il a reçu (fonction bloquante). – activate (héritée de MGFTextureNode) : activation de la texture par le client. – inactivate (héritée de MGFTextureNode) : désactivation de la texture par le client. – refreshResident (héritée de MGFResidentNode) : méthode appelée à chaque rendu et utilisée par le client pour raffraichir la texture (seulement si nécessaire : si on a desssiné de nouveaux éléments). Seule la fonction serialize est utilisée par le serveur. Toutes les autres sont spécifiques au client. Déroulement Lorsqu’une texture SVG est détectée dans le fichier vrml, deux noeuds TextureVecNode sont créés : le premier pour le serveur et le second pour le client. Ces noeuds sont créés par le biais de la classe TextureVecNodeCreator qui va au préalable chercher la taille des paquets qui transitent sur le réseau dans le fichier de configuration du client. La fonction serialize du serveur est alors appelée pour envoyer les premières données au client. La première fois que l’on passe dans cette fonction, le fichier SVG est parcouru grâce à la bibliothèque libsvg [lib05]. 4 On dispose alors d’un graphe composé d’éléments svg. Ces élements sont placés dans un tableau (fonction serializeRec) pour faciliter leur parcours. La méthode findSizeMinMax est ensuite utilisée pour calculer les dimensions de la texture SVG : largeur, hauteur et taille totale en octets. En effet, une texture vectorielle n’est pas forcément centrée sur l’origine. Une fois ces paramètres calculés, ils sont envoyés au client qui les reçoit avec la fonction unserialize. Celui-ci calcule le nombre d’éléments SVG qui doit être envoyé par le serveur dans chacun des paquets. La client demandera toujours à recevoir au minimum un élément, même si la taille moyenne d’un élément est plus grande que la taille d’un paquet. Il ne sert a rien de demander la moitié d’un élément puisqu’il ne pourrait pas être affiché correctement. Le contexte EGL [wg05a] qui permet de mettre en place une surface de dessin pour les méthode d’OpenVG [wg07] est alors créé. EGL permet de dessiner sur plusieurs types de surfaces : – dans une fenêtre existante (comme glut), – dans un “pbuffer” qui, par exemple, peut être une image OpenVG ou une texture OpenGL ES, – dans un “pixmap” (une grille). On se sert ici d’un pixmap qui est simple d’utilisation et qui permet de transformer la grille en texture OpenGL facilement, mais il faudra surement essayer les pbuffer pour dessiner directement dans une texture (puisque c’est ce qu’on veut au final). Pour le moment il y a encore une petite transition entre le pixmap et la texture que nous verrons plus loin. Le contexte ainsi créé doit enfin être activé. A noter qu’un élément OpenVG créé dans un contexte ne peut pas être dessiné sur une surface qui est associée à un autre contexte. Il faut donc penser à activer le contexte courant du noeud sur lequel on travaille avant d’utiliser les méthodes OpenVG (un nouveau contexte est créé pour chacune des textures SVG du fichier vrml). Si la façade du bâtiment est visible, la fonction settleInPVS est appelée. On y crée la texture OpenGL et on l’associe au pixmap. C’est aussi dans cette méthode que l’on dit à Elkano que le noeud est résident, de manière à ce que la fonction refreshResident soit appelée à chaque rendu (pas la peine de raffraichir la texture si elle n’a pas encore été créée). La fonction activate est aussi appelée à chaque rendu pour activer la texture qui correspond au noeud courant. La méthode suivante est computeVS dans laquelle le client demande au serveur de lui envoyer des éléments, par rapport au nombre d’éléments contenus dans un paquet et à ceux qui ont déjà été reçus. C’est ici que le client recharge le noeud courant, de manière à ce que la méthode serialize du serveur soit appelée de nouveau (et ceci, jusqu’à ce que tous les éléments soient reçu). Coté serveur, c’est la seconde fois que la fonction serialize est appelée. On envoie donc le nombre d’éléments souhaité par le client sur le réseau (fonctions serializeTable et serializeStyle). Une des contraintes du stage a été de ne pas utiliser d’autres bibliothèques que OpenVG et OpenGL du coté du client pour que le plugin puisse être utilisé par le plus grand nombre de clients possible. Il faut donc convertir les éléments SVG du serveur (qui sont propres à la bibliothèque libsvg) avant de les envoyer. Chacun des éléments SVG est associé à un style (taille des traits, couleur des traits, couleur de remplissage, ...) qui est aussi envoyé au client. Pour le moment, nous avons mis en place un style minimal (couleurs et tailles des traits) et nous traitons seulement les chemins et les rectangles pour les éléments. Comme on l’a vu tout à l’heure, ce n’est pas le client qui reçoit ces éléments, mais un noeud temporaire (car ce n’est pas la première fois que l’on passe dans la fonction unserialize). On crée alors un tableau d’éléments SVG avec une structure temporaire (voir fichier Inc/Plugin/svgData.h). On pourrait vouloir construire directement les éléments OpenVG, mais cela est impossible car on ne connait pas le contexte qui doit leur être associé. En effet, les fonctions relatives aux noeuds progressifs qui nous auraient permis de récupérer ce 5 contexte ne sont appelées qu’après, d’ou la nécessité de se servir de tableaux temporaires pour les éléments et leur styles. Seulement ensuite, les fonctions mergeImport, mergeCompute et mergeExport du noeud temporaire sont appelées successivement. On récupère alors le contexte dans la première, on l’active dans la seconde et on crée les éléments OpenVG dans le client avec la troisième (sans oublier de détruire les éléments temporaires au fur et à mesure). Les fonctions permettant de créer les éléments OpenVG sont createVGPaint (pour le type) et createVGPath et createVGRect pour les éléments. Lorsque tous les éléments ont été créés, les variables du client sont mises à jour (notamment le fait que la texture doit être rafraichie) et le noeud temporaire est détruit. Lorsque la fonction refreshResident du client est appelée, les nouveaux éléments sont dessinés dans le pixmap avec les fonction d’OpenVG (drawElements) et la texture associée est rafraichie. Le client peut alors demander de nouveaux éléments s’il ne les a pas déjà tous reçu. Travaux futurs – Tester l’application sur un client disposant d’une accélération vectorielle. Pour le moment, le dessin des éléments OpenVG est très lent (et c’est normal). Pour essayer sous Linux ou Windows, il serait intéressant d’utiliser la bibliothèque AmanithVG [ama06] qui est entièrement développée à partir d’OpenGL et qui permet donc d’émuler l’accélération vectorielle. C’est impossible pour le moment car cette bibliothèque utilise son propre système de fenêtrage pour dessiner, mais elle devrait implémenter les fonctionnalités d’EGL sous peu. A suivre ... – La texture actuellement utilisée est une texture OpenGL. Elle devra être convertie en texture OpenGL ES pour pouvoir être utilisée avec des clients tels que les PDA ou les téléphones mobiles. Cette conversion devrait être très rapide : les fonctions utilisés sont toutes présentes (à quelques différences près) dans la spécification d’OpenGL ES [wg05b]. – Utiliser un pbuffer (et non un pixmap) comme surface EGL et l’associer à OpenGL ES pour dessiner les éléments OpenVG directement dans une texture OpenGL ES (c’est expliqué dans la spécification d’EGL [wg05a]). Les données contenues dans les pixmaps sont stokées sous la forme ABGR, il est donc nécessaire de les modifier avant de créer la texture pour obtenir des pixels RGBA. Pour contourner ce problème il serait possible d’utiliser l’extension GL_EXT_abgr que possède la plupart des cartes graphiques pour le format des textures. Néanmoins, l’utilisation d’une texture OpenGL ES comme surface éviterait l’appel à glTexImage2D et permettrait un rendu plus rapide que l’utilisation de pixmaps. – Seule la génération automatique des mipmaps est utilisée pour afficher les textures. Celle-ci n’est pas toujours la plus efficace, notamment pour l’affichage des lignes dans lesquelles les mipmaps provoquent un effet de flou avec des lignes qui deviennent grises lorsque les textures s’éloignent de l’observateur. Par exemple, le rendu correspondrait beaucoup plus à nos attentes avec la méthode de Barla et al. [BTS05]. Ils permettent de fusionner des lignes sans pour autant changer leur densité lorsque les textures s’éloignent. – Lire le fichier SVG dans son intégralité. Pour le moment, on ne s’occupe que des chemins et des rectangles. Ils existe beaucoup d’autres éléments possibles : cercles, ellipses, lignes, textes et images. Même chose pour le style associé à chacun des éléments. Pour le moment, on ne s’occupe que des couleurs simples. Il faut gérer les couleurs de type “gradient” et “pattern”, ainsi que les différents styles d’écriture, de jointures pour les lignes, ... – Compresser les données qui transitent sur le réseau pour gagner en vitesse. 6 Compilation / exécution Bibliothèques nécessaires Les bibliothèques doivent être placées dans le répertoire Libraries de Magellan – Expat (version 2.0.1) [exp07] : utile pour libsvg et disponible sur http ://expat.sourceforge.net/ en précompilé. Il existe aussi des packages pour Linux. – libsvg (version 0.1.4) [lib05] : utile au serveur pour lire le fichier SVG et disponible sur http ://cairographics.org/. Cette bibliothèque est disponible sous forme de packages pour Linux. Néanmoins, il est préférable d’utiliser la version donnée avec Elkano car elle contient les fichiers utiles à la compilation sous windows. De plus, on utilise ici un fichier interne à la bibliothèque (fichier svgint.h) qui n’est pas disponible en installant simplement un package. – OpenVG (version 1.0.1) [wg07] : utile au client pour dessiner les éléments SVG et disponible sur http ://www.khronos.org/openvg/. Cette bibliothèque est disponible en précompilée seulement pour Windows. Elle a été légèrement modifiée pour compiler sous Linux. Il est donc encore une fois conseillé d’utiliser celle qui est fournie avec Elkano. Compilation du plugin La compilation se fait de la même manière que pour les autres plugins : répertoire Bld/vc8.0/ pour Windows et répertoire Bld/Unix/ pour Linux. Exécution Avant tout, mettre à jour les variables d’environnement dans les scipts d’exécution (répertoire Magellan/Scripts/{Linux|Win32|WinCE}/). Par exemple, sous Linux, il faut ajouter les lignes setenv LD_LIBRARY_PATH ${LD_LIBRARY_PATH}:${EXTERN_LIB_DIR}/ri_package/lib setenv LD_LIBRARY_PATH ${LD_LIBRARY_PATH}:${EXTERN_LIB_DIR}/libsvg-0.1.4/lib setenv LD_LIBRARY_PATH ${LD_LIBRARY_PATH}:${PLUGINS_DIR}/PLGTextureVec/Lib/${TARGET} dans les fichiers client, server, builder, clientLt et clientSW Il faut aussi ajouter l’existence du plugin dans les fichiers de configuration (dans le même répertoire). Ajouter les lignes <Plugin url="plgTextureVec.so"> </Plugin> dans les fichiers builder.cfg et server.cfg, et les lignes <Plugin url="plgTextureVec.so" > <nodeCreator name="TextureVec"> <packetSize val= 512/> </nodeCreator> </Plugin> dans le fichier client.cfg, en modifiant si besoins la taille des paquets qui transitent sur le réseau. Modifier aussi le chemin du répertoire contenant les modèles 3D dans le fichier server.cfg : 7 <Config> <!-- Path to a vrml base --> <rootPath value="{votre répertoire}/projects/protected/Bases" /> <!-- <rootPath value="Y:\" /> --> </Config> Enfin, mettre à jour la base de données des modèles 3D qui se servent de ce plugin. Par exemple, ajouter les lignes suivantes dans le fichier Bases/db.xml : <Scene <Scene <Scene <Scene <Scene <Scene <Scene url="Vectorial/wrl_test_image/testImage.wrl" desc="a textured image" /> url="Vectorial/wrl_test_image2/testImage.wrl" desc="2 textured cube" /> url="Vectorial/BordeauxToon/bordeaux.wrl" desc="city of bordeaux (toon)" /> url="Vectorial/BordeauxToonLines/bordeaux.wrl" desc="city of bordeaux (toon and lines)" /> url="Vectorial/BordeauxLines/bordeaux.wrl" desc="city of bordeaux (lines)" /> url="Vectorial/BordeauxLum/bordeaux.wrl" desc="city of bordeaux (intensity)" /> url="Vectorial/BordeauxLines2/bordeaux.wrl" desc="city of bordeaux (lines 2)" /> Il est maintenant possible d’exécuter l’application : – Lancer le serveur (fichier Magellan/Scripts/{Linux|Win32|WinCE}/server) – Cliquer sur le menu “Database”, puis sur “Generate” (les scènes du fichier db.xml doivent alors être trouvées) – Lancer le client (fichier Magellan/Scripts/{Linux|Win32|WinCE}/client) – Cliquer sur le menu “File” puis “Browse server” – Dans Url, mettre “mgf ://localhost”, puis cliquer sur Ok (ici encore le client doit trouver les scènes) – Cliquer sur le menu Scenes, puis choisir celle que vous voulez afficher Parmis les scènes disponibles, on distingue notamment des scènes de test et la scène de Bordeaux stylisée avec plusieurs méthodes : effet cartoon, détection des arêtes, seuillage. Pour transformer les textures originales en textures SVG, nous nous sommes servis du logiciel Inkscape (www.inkscape.org). 8 Bibliographie [ama06] Amanithvg. 2006. [BTS05] Pascal Barla, Joëlle Thollot, and François Sillion. Geometric clustering for line drawing simplification. In Siggraph technical sketch : SIGGRAPH’2005, 2005. [exp07] The expat xml parser. 2007. [Gro03] The SVG Working Group. Scalable vector graphics (svg) 1.1 specification. 2003. [lib05] libsvg. 2005. [Mar04] Jean-Eudes Marvie. Visualisation interactive d’environnements virtuels complexes à travers des réseaux et sur des machines à performances variables, oct 2004. [QTG+ 06] Jean-Charles Quillet, Gwenola Thomas, Xavier Granier, Pascal Guitton, and Jean-Eudes Marvie. Using expressive rendering for remote visualization of large city models. In Web3D 2006 : Proceedings of the 11th International Conference on 3D Web Technology. ACM, ACM Press, apr 2006. [QTM05] Jean-Charles Quillet, Gwenola Thomas, and Jean-Eudes Marvie. Client-server visualization of city models through non photorealistic rendering. Technical report, INRIA, 2005. [vrm97] Vrml97 and related specifications. 1997. [wg05a] The Khronos EGL working group. Khronos native platform graphics interface (egl version 1.2). 2005. [wg05b] The Khronos OpenGL ES working group. Opengl es common profile specification 2.0. 2005. [wg07] The Khronos OpenVG working group. Openvg 1.0.1 specification. 2007. 9