Table of Contents

Utilisation d'EasyAR dans un moteur 3D

Pour utiliser EasyAR dans un moteur 3D, il est nécessaire de rendre l'image de la caméra et les objets virtuels. L'alignement des objets virtuels avec l'image de la caméra nécessite une attention particulière. Lors du rendu de l'image de la caméra, certains paramètres lors de la génération et de l'affichage de l'image peuvent ne pas correspondre, par exemple la position, l'orientation, le format ou le rapport d'aspect de la caméra physique peuvent différer de ceux de l'affichage. Si vous devez intégrer EasyAR à un moteur 3D non pris en charge, portez une attention particulière aux détails suivants.

Recadrage du remplissage des bords de l'image de la caméra

Le recadrage, la transposition et l'encodage de l'image nécessitent des calculs importants. Pour réduire la charge de calcul et la latence, des formats bruts sont généralement utilisés. Pour faciliter l'encodage vidéo, les images produites par les caméras physiques sont souvent alignées sur des blocs de 8x8, 16x16, 32x32 ou 64x64. Par exemple, sur certains téléphones, une résolution sélectionnée de 1920x1080 peut produire une image de 1920x1088 car 1080 n'est pas un multiple de 64.

image avec remplissage

Il est donc nécessaire de supprimer ces zones de remplissage excédentaires lors du rendu. Plusieurs approches sont possibles : spécifier la largeur lors du transfert de l'image vers la mémoire vidéo (par exemple, avec glPixelStorei(GL_PACK_ROW_LENGTH, ...) dans OpenGL) ou calculer manuellement les coordonnées UV dans le fragment shader, en tronquant les parties excédentaires lors de l'échantillonnage.

Rendu en fonction de l'orientation de l'écran

Sur les téléphones, l'image de la caméra physique est généralement fixe par rapport au boîtier et ne tourne pas avec l'orientation de l'affichage. Cependant, l'orientation du boîtier affecte notre perception des directions (haut, bas, gauche, droite). Lors du rendu, l'orientation actuelle de l'écran influence également la direction de l'image affichée.

Il est généralement nécessaire de déterminer un angle de rotation θ de l'image de la caméra physique par rapport à l'orientation d'affichage de l'écran.

Si \(\theta_{screen}\) représente la rotation horaire (en radians) de l'image de l'écran par rapport à son orientation naturelle, et \(\theta_{phycam}\) la rotation horaire nécessaire pour afficher correctement l'image de la caméra physique sur un écran en orientation naturelle, alors \(\theta\) représente la rotation horaire nécessaire pour afficher l'image de la caméra physique sur l'écran actuel.

Pour la caméra arrière :

\[ \theta = \theta_{phycam} - \theta_{screen} \]

Par exemple, sur Android en orientation naturelle : \(\theta_{screen} = 0\), \(\theta_{phycam} = \frac{\pi}{2}\), donc \(\theta = \frac{\pi}{2}\).

Pour la caméra frontale, si un retournement horizontal est appliqué après rotation :

\[ \theta = \theta_{phycam} + \theta_{screen} \]
Note

Lorsque l'orientation de l'écran change, \(\theta\) doit être recalculé immédiatement à la première frame après la rotation, sous peine d'une incohérence transitoire de l'orientation.

Rendu de l'arrière-plan de la caméra et des objets virtuels

Pour aligner les objets virtuels avec l'image de la caméra sur mobile, ceux-ci doivent être placés dans un espace virtuel correspondant exactement à l'espace réel, avec le même angle de champ et le même rapport d'aspect que la caméra physique. La transformation de projection perspective appliquée à l'image de la caméra et aux objets virtuels est presque identique, à une différence près : la projection de l'image de la caméra se produit principalement dans la caméra physique, tandis que celle des objets virtuels est entièrement calculée.

En adoptant les conventions OpenGL (axes caméra : x vers la droite, y vers le haut, z sortant de l'écran ; axes de clipping : x droite, y haut, z sortant, w virtuel), la matrice de projection perspective pour le rendu de l'image de la caméra est :

\[ P_i=\left( \begin{array}{cccc} (-1)^{\text{flip}} & \phantom{0} & \phantom{0} & \phantom{0} \\ \phantom{0} & 1 & \phantom{0} & \phantom{0} \\ \phantom{0} & \phantom{0} & 1 & \phantom{0} \\ \phantom{0} & \phantom{0} & \phantom{0} & 1 \\ \end{array} \right)\left( \begin{array}{cccc} \cos (-\theta ) & -\sin (-\theta ) & \phantom{0} & \phantom{0} \\ \sin (-\theta ) & \cos (-\theta ) & \phantom{0} & \phantom{0} \\ \phantom{0} & \phantom{0} & 1 & \phantom{0} \\ \phantom{0} & \phantom{0} & \phantom{0} & 1 \\ \end{array} \right)\left( \begin{array}{cccc} s_x & \phantom{0} & \phantom{0} & \phantom{0} \\ \phantom{0} & s_y & \phantom{0} & \phantom{0} \\ \phantom{0} & \phantom{0} & 1 & \phantom{0} \\ \phantom{0} & \phantom{0} & \phantom{0} & 1 \\ \end{array} \right) \]

Où :

  • flip vaut 1 si l'image est retournée horizontalement, 0 sinon.
  • \(\theta\) est l'angle de rotation horaire (radians).
  • \(s_x\), \(s_y\) sont des facteurs d'échelle pour le remplissage/mise à l'échelle, dépendants de \(\theta\).

Cette matrice applique successivement : mise à l'échelle, rotation, puis retournement. Le rendu utilise un rectangle couvrant l'écran (ex: sommets à \((-1, -1, 0)\), \((1, -1, 0)\), \((1, 1, 0)\), \((-1, 1, 0)\)) avec des coordonnées UV aux coins.

Pour les objets virtuels, la matrice de projection est :

\[ P=P_i\left( \begin{array}{cccc} 1 & \phantom{0} & \phantom{0} & \phantom{0} \\ \phantom{0} & 1 & \phantom{0} & \phantom{0} \\ \phantom{0} & \phantom{0} & -\frac{f+n}{f-n} & -\frac{2 f n}{f-n} \\ \phantom{0} & \phantom{0} & -1 & \phantom{0} \\ \end{array} \right)\left( \begin{array}{cccc} \frac{2}{w} & \phantom{0} & \phantom{0} & \phantom{0} \\ \phantom{0} & \frac{2}{h} & \phantom{0} & \phantom{0} \\ \phantom{0} & \phantom{0} & 1 & \phantom{0} \\ \phantom{0} & \phantom{0} & \phantom{0} & 1 \\ \end{array} \right)\left( \begin{array}{cccc} 1 & \phantom{0} & \phantom{0} & \phantom{0} \\ \phantom{0} & -1 & \phantom{0} & \phantom{0} \\ \phantom{0} & \phantom{0} & -1 & \phantom{0} \\ \phantom{0} & \phantom{0} & \phantom{0} & 1 \\ \end{array} \right)\left( \begin{array}{cccc} f_x & \phantom{0} & c_x & \phantom{0} \\ \phantom{0} & f_y & c_y & \phantom{0} \\ \phantom{0} & \phantom{0} & 1 & \phantom{0} \\ \phantom{0} & \phantom{0} & \phantom{0} & 1 \\ \end{array} \right)\left( \begin{array}{cccc} 1 & \phantom{0} & \phantom{0} & \phantom{0} \\ \phantom{0} & -1 & \phantom{0} & \phantom{0} \\ \phantom{0} & \phantom{0} & -1 & \phantom{0} \\ \phantom{0} & \phantom{0} & \phantom{0} & 1 \\ \end{array} \right) \]

Où :

  • \(n\), \(f\) : paramètres near/far du clipping.
  • \(w\), \(h\) : largeur/hauteur en pixels de l'image.
  • \(f_x\), \(f_y\) : focales pixels ; \(c_x\), \(c_y\) : position du point principal.

Cette matrice combine :

  1. La projection perspective des paramètres intrinsèques (avec adaptations d'axes pour OpenCV/OpenGL).
  2. La transformation du système de coordonnées pixels vers le rectangle image.
  3. Le clipping near/far.
  4. La projection perspective utilisée pour l'image de la caméra.

Après simplification :

\[ P=P_i\left( \begin{array}{cccc} \frac{2 f_x}{w} & \phantom{0} & 1-\frac{2 c_x}{w} & \phantom{0} \\ \phantom{0} & \frac{2 f_y}{h} & -1+\frac{2 c_y}{h} & \phantom{0} \\ \phantom{0} & \phantom{0} & -\frac{f+n}{f-n} & -\frac{2 f n}{f-n} \\ \phantom{0} & \phantom{0} & -1 & \phantom{0} \\ \end{array} \right) \]

Le rendu s'effectue donc en deux passes : d'abord l'image de la caméra, puis les objets virtuels par-dessus.

Certains moteurs expriment la projection via l'angle de champ horizontal \(\alpha\) et le rapport d'aspect \(r\). En ignorant rotation, retournement et décalage du point principal : \(\alpha=2 \arctan{\frac{w}{2 f_x}}\), \(r=\frac{w}{h}\).

Note : Ce processus ne tient pas compte de la distorsion de l'objectif, généralement négligeable sur les caméras de téléphones modernes.