Donner vie à la Terre du Milieu avec WebGL pour mobile
Historiquement, il a été difficile de proposer des expériences interactives, basées sur le Web et axées sur le multimédia sur les mobiles et les tablettes. Les principales contraintes ont été les performances, la disponibilité des API, les limites de l'audio HTML5 sur les appareils et l'absence de lecture vidéo intégrée fluide.
Plus tôt cette année, nous avons commencé un projet avec des amis de Google et de Warner Bros. pour créer une expérience Web mobile axée sur le mobile pour le nouveau film sur le Hobbit, Le Hobbit: La désolation de Smaug. Créer une expérience Chrome mobile multimédia a été une tâche très inspirante et stimulante.
L'expérience est optimisée pour Chrome pour Android sur les nouveaux appareils Nexus, où nous avons désormais accès à WebGL et Web Audio. Toutefois, une grande partie de l'expérience est également accessible sur les appareils et les navigateurs non WebGL grâce à la composition accélérée par matériel et aux animations CSS.
L'ensemble de l'expérience est basé sur une carte de la Terre du Milieu, ainsi que sur les lieux et les personnages des films Le Hobbit. L'utilisation de WebGL nous a permis de dramatiser et d'explorer le riche univers de la trilogie du Hobbit, tout en laissant les utilisateurs contrôler l'expérience.
Défis de WebGL sur les appareils mobiles
Tout d'abord, le terme "appareils mobiles" est très large. Les spécifications des appareils varient beaucoup. En tant que développeur, vous devez donc décider si vous souhaitez prendre en charge davantage d'appareils avec une expérience moins complexe ou, comme nous l'avons fait dans ce cas, limiter les appareils compatibles à ceux capables d'afficher un monde 3D plus réaliste. Pour "Parcours dans la Terre du Milieu", nous nous sommes concentrés sur les appareils Nexus et cinq smartphones Android populaires.
Pour l'expérience, nous avons utilisé three.js, comme nous l'avons fait pour certains de nos projets WebGL précédents. Nous avons commencé l'implémentation en créant une version initiale du jeu Trollshaw qui s'exécuterait correctement sur la tablette Nexus 10. Après quelques tests initiaux sur l'appareil, nous avions en tête une liste d'optimisations qui ressemblait beaucoup à celle que nous utilisons normalement pour un ordinateur portable bas de gamme:
- Utiliser des modèles low-poly
- Utiliser des textures basse résolution
- Réduire autant que possible le nombre d'appels de dessin en fusionnant la géométrie
- Simplifier les supports et l'éclairage
- Supprimer les effets post-traitement et désactiver l'anticrénelage
- Optimiser les performances JavaScript
- Affichage du canevas WebGL à moitié de la taille et mise à l'échelle avec CSS
Après avoir appliqué ces optimisations à notre première version brute du jeu, nous avons obtenu une fréquence d'images stable de 30 FPS, ce qui nous a satisfaits. À ce stade, notre objectif était d'améliorer les visuels sans affecter négativement la fréquence d'images. Nous avons essayé de nombreuses astuces: certaines ont eu un impact réel sur les performances, d'autres n'ont pas eu l'effet espéré.
Utiliser des modèles low-poly
Commençons par les modèles. L'utilisation de modèles low-poly réduit le temps de téléchargement et le temps d'initialisation de la scène. Nous avons constaté que nous pouvions augmenter considérablement la complexité sans trop affecter les performances. Les modèles de trolls que nous utilisons dans ce jeu comptent environ 5 000 visages, et la scène environ 40 000 visages. Cela fonctionne très bien.

Pour un autre emplacement (pas encore disponible) de l'expérience, nous avons constaté un impact plus important sur les performances en réduisant le nombre de polygones. Dans ce cas, nous avons chargé des objets à moins de polygones pour les appareils mobiles que pour les ordinateurs. La création de différents ensembles de modèles 3D nécessite un travail supplémentaire et n'est pas toujours nécessaire. Tout dépend du niveau de complexité de vos modèles.
Lorsque nous travaillons sur de grandes scènes avec de nombreux objets, nous essayons d'être stratégiques dans la façon dont nous divisons la géométrie. Cela nous a permis d'activer et de désactiver rapidement les maillages moins importants afin de trouver un paramètre qui fonctionne pour tous les appareils mobiles. Nous pouvons ensuite choisir de fusionner la géométrie en JavaScript au moment de l'exécution pour une optimisation dynamique ou de la fusionner en préproduction pour économiser des requêtes.
Utiliser des textures basse résolution
Pour réduire le temps de chargement sur les appareils mobiles, nous avons choisi de charger différentes textures dont la taille était la moitié de celle des textures sur ordinateur. Il s'avère que tous les appareils peuvent gérer des tailles de texture jusqu'à 2 048 x 2 048 pixels, et la plupart peuvent gérer 4 096 x 4 096 pixels. La recherche de texture sur les textures individuelles ne semble pas poser de problème une fois qu'elles sont importées dans le GPU. La taille totale des textures doit tenir dans la mémoire du GPU pour éviter que les textures ne soient constamment mises à jour et téléchargées, mais ce n'est probablement pas un gros problème pour la plupart des expériences Web. Toutefois, il est important de combiner les textures dans un nombre aussi faible que possible de feuilles d'éléments pour réduire le nombre d'appels de dessin. Cela a un impact important sur les performances sur les appareils mobiles.

(taille d'origine : 512 x 512 px)
Simplifier les supports et l'éclairage
Le choix des matériaux peut également avoir un impact important sur les performances et doit être géré judicieusement sur mobile. Nous avons utilisé MeshLambertMaterial
(calcul de la lumière par sommet) dans three.js au lieu de MeshPhongMaterial
(calcul de la lumière par texel) pour optimiser les performances. En gros, nous avons essayé d'utiliser des nuanceurs aussi simples que possible, avec le moins de calculs d'éclairage possible.
Pour voir l'impact des matériaux que vous utilisez sur les performances d'une scène, vous pouvez remplacer les matériaux de la scène par un MeshBasicMaterial
. Vous obtiendrez ainsi une bonne comparaison.
scene.overrideMaterial = new THREE.MeshBasicMaterial({color:0x333333, wireframe:true});
Optimiser les performances JavaScript
Lorsque vous développez des jeux pour mobile, le GPU n'est pas toujours le plus grand obstacle. Le processeur est très sollicité, en particulier pour les animations physiques et squelettiques. Un truc qui peut parfois aider, selon la simulation, consiste à n'exécuter ces calculs coûteux qu'une fois sur deux. Vous pouvez également utiliser les techniques d'optimisation JavaScript disponibles pour le pool d'objets, la récupération de mémoire et la création d'objets.
La mise à jour d'objets préalloués dans des boucles au lieu de créer de nouveaux objets est une étape importante pour éviter les "à-coups" de récupération de mémoire pendant le jeu.
Prenons par exemple le code suivant:
var currentPos = new THREE.Vector3();
function gameLoop() {
currentPos = new THREE.Vector3(0+offsetX,100,0);
}
Une version améliorée de cette boucle évite de créer des objets qui doivent être collectés:
var originPos = new THREE.Vector3(0,100,0);
var currentPos = new THREE.Vector3();
function gameLoop() {
currentPos.copy(originPos).x += offsetX;
//or
currentPos.set(originPos.x+offsetX,originPos.y,originPos.z);
}
Dans la mesure du possible, les gestionnaires d'événements ne doivent mettre à jour que les propriétés et laisser la boucle de rendu requestAnimationFrame
gérer la mise à jour de l'étape.
Un autre conseil consiste à optimiser et/ou à précalculer les opérations de lancer de rayon. Par exemple, si vous devez attacher un objet à un maillage lors d'un mouvement de chemin statique, vous pouvez "enregistrer" les positions pendant une boucle, puis lire ces données au lieu de lancer des rayons sur le maillage. Vous pouvez également utiliser le lancer de rayons pour rechercher les interactions de la souris avec un maillage invisible basse polyvalence plus simple, comme nous l'avons fait dans l'expérience Rivendell. La recherche de collisions sur un maillage haute fidélité est très lente et doit être évitée dans une boucle de jeu en général.
Affichage du canevas WebGL à moitié de la taille et mise à l'échelle avec CSS
La taille du canevas WebGL est probablement le paramètre le plus efficace que vous pouvez modifier pour optimiser les performances. Plus le canevas que vous utilisez pour dessiner votre scène 3D est grand, plus de pixels doivent être dessinés à chaque frame. Cela a bien sûr un impact sur les performances.Le Nexus 10, avec son écran haute densité de 2 560 x 1 600 pixels, doit afficher quatre fois plus de pixels qu'une tablette basse densité. Pour optimiser cela pour les appareils mobiles, nous utilisons un astuce : nous définissons la taille du canevas sur la moitié (50%) et la mettons à l'échelle de sa taille prévue (100%) à l'aide de transformations CSS 3D accélérées par matériel. L'inconvénient est que l'image est pixellisée, ce qui peut poser problème pour les lignes fines. Toutefois, sur un écran haute résolution, l'effet n'est pas si mauvais. Les performances supplémentaires en valent vraiment la peine.

Les objets comme éléments de base
Pour créer le grand labyrinthe du château de Dol Guldur et la vallée sans fin de Rivendell, nous avons créé un ensemble de modèles 3D de blocs de construction que nous réutilisons. La réutilisation d'objets nous permet de nous assurer qu'ils sont instanciés et importés au début de l'expérience, et non au milieu.

Dans Rivendell, nous avons un certain nombre de sections de sol que nous repositionnons constamment en profondeur Z au fur et à mesure du parcours de l'utilisateur. Lorsque l'utilisateur passe des sections, elles sont repositionnées à l'horizon.
Pour le château de Dol Guldur, nous voulions que le labyrinthe soit régénéré pour chaque partie. Pour ce faire, nous avons créé un script qui regénère le labyrinthe.
Si vous fusionnez l'ensemble de la structure dans un grand maillage dès le début, vous obtiendrez une scène très volumineuse et des performances médiocres. Pour y remédier, nous avons décidé de masquer et d'afficher les composants principaux selon qu'ils sont visibles ou non. Dès le départ, nous avons pensé utiliser un script de raycaster 2D, mais nous avons finalement utilisé le culling de frustum intégré à Three.js. Nous avons réutilisé le script de raycaster pour faire un zoom avant sur le "danger" auquel le joueur est confronté.
La prochaine grande chose à gérer est l'interaction utilisateur. Sur ordinateur, vous avez la souris et le clavier. Sur les appareils mobiles, vos utilisateurs interagissent avec l'écran tactile, le glissement, le pincement, l'orientation de l'appareil, etc.
Utiliser l'interaction tactile dans les expériences Web mobiles
Il n'est pas difficile d'ajouter la compatibilité avec le mode tactile. Il existe de excellents articles sur ce sujet. Cependant, de petites choses peuvent rendre la tâche plus compliquée.
Vous pouvez utiliser à la fois l'écran tactile et la souris. Le Chromebook Pixel et les autres ordinateurs portables tactiles sont compatibles avec la souris et l'écran tactile. Une erreur courante consiste à vérifier si l'appareil est compatible avec l'écran tactile, puis à n'ajouter que des écouteurs d'événements tactiles et aucun pour la souris.
Ne mettez pas à jour le rendu dans les écouteurs d'événements. Enregistrez plutôt les événements tactiles dans des variables et réagissez-y dans la boucle de rendu requestAnimationFrame. Cela améliore les performances et fusionne également les événements en conflit. Assurez-vous de réutiliser des objets au lieu d'en créer de nouveaux dans les écouteurs d'événements.
N'oubliez pas qu'il s'agit d'une technologie multipoint: event.touches est un tableau de toutes les touches. Dans certains cas, il est plus intéressant d'examiner event.targetTouches ou event.changedTouches et de ne réagir qu'aux gestes qui vous intéressent. Pour distinguer les pressions des balayages, nous utilisons un délai avant de vérifier si le point de contact a bougé (balayer) ou s'il est immobile (appuyer). Pour obtenir un pincement, nous mesurons la distance entre les deux premiers points de contact et l'évolution de cette distance au fil du temps.
Dans un monde en 3D, vous devez décider de la façon dont votre caméra réagit aux actions de la souris et aux gestes de balayage. Une méthode courante consiste à suivre le mouvement de la souris. Vous pouvez le faire par commande directe à l'aide de la position de la souris ou par mouvement delta (changement de position). Vous ne souhaitez pas toujours que le comportement sur un appareil mobile soit le même que dans un navigateur pour ordinateur. Nous avons effectué de nombreux tests pour déterminer la version la plus adaptée.
Lorsque vous travaillez avec des écrans plus petits et des écrans tactiles, vous constaterez que les doigts de l'utilisateur et les graphiques d'interaction de l'interface utilisateur gênent souvent ce que vous souhaitez afficher. C'est quelque chose auquel nous sommes habitués lorsque nous concevons des applications natives, mais nous n'avons pas vraiment eu à y penser auparavant avec les expériences Web. C'est un véritable défi pour les concepteurs et les concepteurs UX.
Résumé
Notre expérience globale de ce projet est que WebGL sur mobile fonctionne très bien, en particulier sur les appareils haut de gamme plus récents. En ce qui concerne les performances, il semble que le nombre de polygones et la taille des textures affectent principalement les temps de téléchargement et d'initialisation. Les matériaux, les nuanceurs et la taille du canevas WebGL sont les éléments les plus importants à optimiser pour les performances mobiles. Toutefois, c'est la somme des éléments qui affecte les performances. Tout ce que vous pouvez faire pour les optimiser compte.
Cibler les appareils mobiles signifie également que vous devez vous habituer à penser aux interactions tactiles. Il ne s'agit pas seulement de la taille en pixels, mais aussi de la taille physique de l'écran. Dans certains cas, nous avons dû rapprocher la caméra 3D pour voir ce qui se passait.
Le test est lancé, et ce fut un parcours fantastique. J'espère qu'il vous plaira.
Pour essayer, Partez pour votre propre voyage dans la Terre du Milieu.