Blog

Dans les yeux des Koujis

Cet Article est une traduction d’un article publié sur Medium par mes soins en 2018.

Kouji est une application permettant de créer son double virtuel et de le mettre en scène dans des vidéos en Réalité Augmentée à envoyer à tous ses amis. Si vous n’êtes pas encore familiers avec ces petits personnages, allez d’abord voir ce petit trailer. Vous pouvez aussi en savoir plus sur la section “Portfolio” de mon site.

Les Koujis ne payent pas de mine: ils paraissent aussi simples que mignons. Mais, en soulevant le capot, on s’aperçoit qu’ils sont mus par de nombreux mécanismes. Comme ils doivent vous ressembler (à vous comme à n’importe qui d’autre, en vérité) ils doivent être hautement customisables. Il doivent également bouger comme des personnages de dessins animés, le tout en restant aussi mignons que possible.

Et bien sûr, tout cela doit tourner sans accroc dans une application utilisant simultanément de l’enregistrement vidéo, du traitement sonore, de la réalité augmentée… sur téléphone mobile.

Ainsi, chaque aspect des Kouijs devient un challenge technique : comment creuser une bouche aux formes complexes et stylisées sans utiliser une topologie trop lourde ? Comment offrir à nous personnages une large combinaison de silhouettes, accoutrements et couleurs sans finir avec une application de 10Go ?

Derrière ce cortège de questions importantes, viennent d’autres problèmes d’apparence plus anodine auxquels il nous faut néanmoins répondre.

C’est l’une des ces questions apparemment innocentes qui nous intéresse aujourd’hui : Comment animer le regard des Koujis?

Un regard en arrière…

fig 1. Guide d’expression des Koujis

Suite à des considérations autant artistiques que techniques nous avons décidé de donner aux visages de nos Koujis un aspect 2D, comme si leurs yeux et leurs bouches était peints.

En vérité, si l’on veut être tatillon, ils sont peints sur des îlots de géométrie spécifiques superposés à la géométrie de la tête. Les expressions faciales sont ensuite gérées avec une combinaison de blendshapes qui viennent déformer cette géométrie et de shader. Les pupilles viennent s’ajouter au dessus de tout cela.

Afin que la forme de nos pupilles reste constante malgré le changement de forme des yeux, nous avons tout d’abord opté pour la technologie de “projector” d’Unity. Pour schématiser, c’est comme si l’on utilisait un projecteur de cinéma pour projeter la texture des pupilles sur les yeux, qui deviennent ici des “écrans”.
L’avantage d’utiliser cette technique dans Unity est que le même procédé était utilisé sur notre rig dans le logiciel d’animation. Il n’y avait donc qu’à coller des projecteurs sur des bones dédiés et l’animation des pupilles était facilement et fidèlement transférée.

fig2: Les bones projecteurs

Rendez-vous chez l’oculiste

Cette solution a été un succès! Pendant un temps en tout cas…

Car, si tout s’est bien passé sur les premières versions de l’application, l’adoption d’ARKit (la technologie de RA d’Apple) a changé la donne. Nous avons alors vu apparaître des problèmes de clipping qui, de temps à autre, désactivent les projecteurs et font disparaitre les pupilles.

Comme le problème arrive très rarement et que nous avons d’autres priorités, nous avons décidé de régler le problème un peu plus tard.

Mais un changement (la sortie d’iOS 11) nous a forcé la main: nos tests préliminaires nous montrent que la manière dont les projecteurs sont utilisés dans nos shaders ne sont désormais plus pris en charge.

Nous devons donc trouver une solution… et vite !

Notre propre solution :

Nous avons donc commencé à réfléchir à une nouvelle approche.

Par chance, la géométrie de nos yeux est relativement plate et coïncide bien avec le plan XZ de l’objet. En d’autres mots, les positions en XZ des vertices peuvent aisément être utilisés en temps que coordonnées UVs.

fig3. Position des Vertices en tant que coordonnées UVs

fig4. Coordonnées remappées (voilà qui est mieux !)

En manipulant ces coordonnées, il est alors facile d’obtenir des valeurs plus commodes pour un œil seulement. Par exemple, avec les formules suivantes :

u = x * 2.0
v = z * 2.0–1.0

On peut transformer les coordonnées de l’image de gauche ci-dessus en celles de l’image de droite.

En utilisant les coordonnées des vertices comme des UVs, la texture n’est donc plus affectée par les blendshapes des yeux, puisque les UVs se déplacent en même temps que les vertices.

Par exemple, si le personnage ferme les yeux, les UVs s’adapteront et la texture de la pupille restera en place.

En fait, tout agit comme si les pupilles étaient suspendues en l’air et que la géométrie recevait les pixels de la texture à utiliser. Exactement comme avec un projecteur et un écran !

Mais cet avantage à une contrepartie malheureuse : le skinning affecte aussi la position des vertices ! Ainsi, lorsque le personnage est animé, les pupilles restent sur place alors que la tête se déplace. C’est un peu comme si le projecteur était posé sur un trépied fixe mais que l’écran se déplaçait. Le résultat est plutôt insolite :

fig 5. L’exorciste.

Comment donc avoir les avantages sans les inconvénients ?

Baking des UVs

Prenons le temps de synthétiser :

  • Nous voulons garder l’adaptation des UVs au différents blendshapes.
  • Nous devons nous débarrasser de l’altération des UVs par le mouvement des personnages.

Quand on y pense, la seule information qui nous est nécessaire et le différentiel entre la position originelle de chaque vertex et sa position finale pour chaque blenshape. Cette information nous permettrait de corriger les UVs pour contrer le mouvement de chacun des blendshapes.

Pour obtenir ces informations, nous avons baké les position des vertices pour chaque blendshape et rassemblé le tout dans un atlas.
Pour cela, nous avons écrit un shader transposant les coordonnées de chaque pixel en une couleur selon les formules utilisées plus haut. X est en rouge (R) et Z en vert (G) :

fig 6. Coordonnées UVs transposées en couleurs.

fig 7.  Atlas des positions des vertices bakées pour chaque blendshape.

Corrections des UVs

Dès lors que la position de chaque vertex est stocké pour chaque blendshape, il était temps de retourner sur Unity.

L’idée est d’écrire un shader corrigeant en temps réel les UVs grâce aux fameux différentiels de position évoqués précédemment.
Pour les obtenir, il suffisait de soustraire les coordonnées en position neutre de celle de chaque blendshape.

delta = BSPosition — originPosition

Bien entendu, il faut pondérer chaque blendshape en fonction de son actuel degré d’activation.

weightDelta = delta * BSWeight

Pour obtenir la nouvelle position il faut enfin ajouter ce différentiel à la position originale de chaque vertex :

originPosition += delta * BSWeight

Finalement, après avoir répété le procédé pour chaque Blendshape, cette partie du shader ressemble à ça :

Et pour bouger les pupilles ? Il suffit de décaler les UVs ! Un simple lerp basé sur la position de chaque bone projecteur suffit.

Et voilà !

Post-Mortem

Même si cette solution est fonctionnelle demeurent deux problèmes :

  • La précision des UVs est étroitement liée à la précision des couleurs de l’atlas dans lequel les positions des vertices sont bakées. Même en utilisant une texture de 16 bits non compressée (ce qui a déjà un coût non négligeable) le résultat n’est pas sans défaut. Cependant, comme cela est surtout visible lorsque l’on est très près du visage du personnage (ce qui n’est pas le cas 99% du temps), ce n’est rébarbatif.
  • Chaque nouveau blendshape devra être baké et rejoindre les autres dans l’atlas.

Je ne doute pas qu’il y ait d’autres solutions que nous n’avons pas encore explorées. Qui sait ? Peut-être l’avenir nous permettra d’améliorer cette méthode.

Merci à Julien Balestrieri qui a travaillé avec moi sur la partie technique des Koujis (notamment la partie Unity). Le travail présenté dans cet article est le fruit d’une intense collaboration entre nous.