Après notre application de présentation technique pour The Sub® de Heineken, utilisée comme élément interactif dans un popup store à Paris et tournant sur des iPad Air 2, on nous a demandé de construire une version navigateur, désormais accessible au grand public, dans le cadre du site web de The Sub®.
La palette de couleurs allait changer, mais toutes les fonctionnalités devaient être conservées.
Unity permet bien d’exporter en WebGL, mais le build WebGL, très lourd, et les résultats inattendus obtenus lors de nos premiers essais nous ont poussés à opter pour Three.js et à tout reconstruire depuis zéro. Ce serait plus léger et on aurait un contrôle plus fin sur le résultat.
Nous avons choisi d’écrire ça en Haxe. Pas de raison précise à ce choix, si ce n’est un code plus propre… et pour coder plus vite… même si nous ne visions pas un autre langage.
Le modèle
Le modèle utilisé pour la version mobile de cette app était bien trop complexe. On pourrait même encore le considérer comme trop haute résolution, mais il fallait un résultat de bonne qualité, quitte à exclure certains navigateurs. Cette app ne tournerait que sur les navigateurs et appareils compatibles WebGL, dans une div au milieu d’un site web complet autour. Les autres visiteurs auraient droit à une image de The Sub® décrivant tous ses éléments clés. 
Pour l’exporter de FBX vers Three.js, la première idée fut d’utiliser le SDK FBX de scaleform avec un script d’export en Python. Ça a parfaitement fonctionné. Mais… qu’en était-il de tous les objets Unity additionnels présents dans les scènes Unity d’origine ?
Pour que les icônes tactiles disparaissent une fois cachées derrière l’objet quand on tourne autour, un ensemble de colliders (boîtes, capsules, cylindres) avait été placé sur l’objet afin de vérifier en continu si l’objet bloquait la vue, et de faire passer l’icône d’un alpha de 0.5 à 1 selon la visibilité. Comme il nous fallait la même fonctionnalité, le même ensemble de colliders était idéalement nécessaire pour ne pas faire de raycast sur l’objet complet mais sur des formes simples, sans gaspiller de temps CPU.
Des sphères avaient aussi été placées pour valider les taps à l’écran là où se trouvaient les icônes : un rayon était lancé et si cette sphère était « touchée », on zoomait vers la vue du point d’intérêt.
Les positions de caméra, les rotations et le FOV avaient également été réglés dans l’éditeur Unity pour coller au plus près aux maquettes des artistes. Ici, il nous fallait exactement les mêmes positions de caméra pour chaque point d’intérêt. Pour rappel, voici la vidéo de la version Unity :
The Sub by Heineken par Da Viking Code sur Vimeo.
Vous voyez donc que de nombreux objets 3D sans mesh servaient à identifier, placer et repérer des emplacements à l’intérieur et autour des objets… On en avait besoin nous aussi, et copier à la main toutes les données de Transform de l’éditeur Unity vers des fichiers js aurait pris beaucoup trop de temps.
On est tombés sur ce plugin exportateur Three.js pour Unity : https://www.assetstore.unity3d.com/en/#!/content/40550
Heureusement, il exportait tout ce dont on avait besoin… y compris le modèle lui-même !!!
Bon, malheureusement ça faisait perdre le lissage des surfaces (smooth shading), on a donc dû utiliser les fonctions de géométrie de Three.js pour recalculer les normales et ainsi de suite, mais le résultat reste excellent pour ce dont on avait besoin.
Shaders
La version Unity est entièrement rendue (sauf pour les shaders de sprites) avec des shaders custom créés avec Shaderforge. Mais ils étaient assez simples à réécrire pour le WebGL. On a utilisé l’éditeur de shaders en ligne appelé ShaderFrog pour coder en direct et les visualiser avant de les implémenter dans Three.js. Un vrai gain de temps.
Les fonctionnalités de shader importantes dont on avait besoin ici étaient les contours (outlines). L’effet fresnel subtil sur les objets « intérieurs » est assez simple, mais pour les contours, il fallait un shader en deux passes, ce qui malheureusement n’était pas possible avec Three.js — du moins pas à notre connaissance à l’époque — donc on a un peu bricolé.
En gros, les objets avec contour sont rendus dans une scène différente, et deux fois. Une première fois en gris foncé avec les positions des sommets mises à l’échelle pour que tout soit un peu plus gros (mise à l’échelle depuis le centre — c’est ça qui fait le contour), puis une seconde fois avec les bons paramètres de couleur/matériau.
Avant chaque rendu, on se contente de bien régler les paramètres du matériau. On obtient ainsi facilement l’effet de contour, même si c’est assez coûteux en performance — ça reste un aspect clé du rendu « dessin technique », impossible de s’en passer. Il existe sans doute des techniques plus intéressantes, mais celle-ci était rapide à mettre en place.
Animation
Il y a une vue très importante où le liquide se comprime et s’écoule hors d’un tube. Dans Unity, cela impliquait des « blend shapes » animés et un shader custom pour le tube (expliqué dans l’autre article).
La version shaderlab du shader de tube a été recréée facilement pour la version Three.js : on utilise toujours un dégradé du noir au blanc avec une valeur de seuil pour faire apparaître le liquide au-dessus du seuil et pas en dessous, ainsi animer cette valeur fait monter et descendre le liquide dans le tube.

Pour la capsule en revanche, les blend shapes ne pouvaient pas être exportés. On a opté pour un vertex shader custom pour comprimer la capsule de façon programmatique. Pourquoi ne pas déporter un peu d’animation de mesh sur le GPU ?
Tout le reste fonctionne comme dans la version Unity. Le ventilateur tourne sur son axe z local, les bulles dans le verre sont des particules… Et presque chaque matériau est animé lors du zoom avant ou arrière de la caméra.
Comme ce projet n’était pas prévu lors de la création de la première version Unity, les choix faits n’ont pas permis d’avoir ici une solution vraiment multiplateforme. Contrairement à d’autres projets, on ne partage pas 99% de la base de code entre les deux versions.
Mais en réalité, ces deux projets sont plus graphiques que fonctionnels. L’important était de transférer les données de la scène d’origine pour les utiliser dans Three.js (paramètres, positions, rotations, ids), et aussi de retranscrire l’aspect visuel en traduisant les shaders.
Donc quand j’ai écrit plus tôt qu’il avait fallu tout refaire depuis zéro, en réalité, comme la plupart des données nécessaires étaient stockées et accessibles dans la scène Unity, et grâce à l’extensibilité de l’éditeur Unity, on a pu extraire les infos nécessaires et les réutiliser. Il ne nous restait plus qu’à coder les quelques aspects spécifiques au js.
On peut même aller plus loin et conclure que construire des scènes 3D pour des apps dans l’éditeur Unity, destinées à Three.js ou tout autre framework, est — grâce à l’extensibilité de l’éditeur Unity — une solution viable pour se rapprocher d’une véritable solution multiplateforme entre natif et navigateur.
Plus d’informations sur notre site web : https://davikingcode.com/fr/projets/heineken-sub-3d