Table of Contents

Utilizzo di EasyAR nel motore 3D

Per utilizzare EasyAR in un motore 3D, è necessario eseguire il rendering dell'immagine della fotocamera e degli oggetti virtuali. Rendering degli oggetti virtuali richiede l'allineamento con l'immagine della fotocamera. Durante il rendering dell'immagine della fotocamera, alcuni parametri al momento della generazione e della visualizzazione dell'immagine potrebbero non corrispondere, ad esempio la posizione, l'orientamento, il formato immagine, le proporzioni della fotocamera fisica potrebbero differire da quelle dello schermo; questo deve essere considerato durante il rendering. Se è necessario integrare EasyAR in un motore 3D non supportato, prestare particolare attenzione ai seguenti dettagli.

Ritaglio del riempimento ai bordi dell'immagine della fotocamera

Il ritaglio, la trasposizione e la codifica delle immagini richiedono una potenza di calcolo significativa. Per ridurre i calcoli e la latenza, si utilizzano generalmente formati più grezzi. Per facilitare la codifica video, le immagini prodotte dalle fotocamere fisiche vengono spesso allineate su griglie come 8x8, 16x16, 32x32, 64x64. Ad esempio, su alcuni telefoni con risoluzione selezionata a 1920x1080, l'immagine effettivamente prodotta potrebbe diventare 1920x1088 perché 1080 non è un multiplo di 64.

immagine con padding

Ciò richiede di rimuovere queste parti di riempimento in eccesso durante il rendering. Esistono diversi approcci possibili: uno consiste nel specificare la larghezza durante il caricamento dell'immagine nella memoria video, ad esempio in OpenGL è possibile utilizzare glPixelStorei(GL_PACK_ROW_LENGTH, ...); un altro consiste nel calcolare manualmente le coordinate UV nel fragment shader e troncare le parti eccedenti durante il campionamento dell'immagine.

Rendering che segue l'orientamento di rotazione dello schermo

Sui telefoni, l'immagine acquisita dalla fotocamera fisica è generalmente fissa rispetto al corpo del dispositivo e non cambia con l'orientamento dello schermo. Tuttavia, i cambiamenti nell'orientamento del corpo del telefono influenzano la nostra definizione delle direzioni alto/basso e destra/sinistra dell'immagine. Durante il rendering, l'orientamento corrente di visualizzazione dello schermo influenzerà anche la direzione dell'immagine visualizzata.

Solitamente, durante il rendering, è necessario determinare un angolo di rotazione dell'immagine della fotocamera rispetto alla direzione di visualizzazione dello schermo.

Se indichiamo con \(\theta_{screen}\) l'angolo in radianti di rotazione oraria dell'immagine dello schermo rispetto alla sua orientazione naturale, e con \(\theta_{phycam}\) l'angolo in radianti di rotazione oraria necessaria affinché l'immagine della fotocamera fisica venga visualizzata correttamente su uno schermo in orientazione naturale, allora \(\theta\) rappresenta l'angolo in radianti di rotazione oraria necessaria affinché l'immagine della fotocamera fisica venga visualizzata sullo schermo corrente.

Per la fotocamera posteriore, abbiamo

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

Ad esempio, su un telefono Android utilizzato in orientazione naturale, \(\theta_{screen} = 0, \theta_{phycam} = \frac{\pi}{2}\), quindi \(\theta = \frac{\pi}{2}\).

Per la fotocamera anteriore, se dopo la rotazione viene applicato un ribaltamento orizzontale (sinistra/destra), abbiamo

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

Quando l'immagine dello schermo ruota, è necessario ricalcolare immediatamente \(\theta\) nel primo frame successivo alla rotazione, altrimenti potrebbe verificarsi un momentaneo orientamento errato dell'immagine.

Rendering dello sfondo della fotocamera e degli oggetti virtuali

Per eseguire il rendering degli oggetti virtuali su un telefono, è necessario allinearli all'immagine della fotocamera. Ciò richiede di posizionare sia la fotocamera di rendering che gli oggetti in uno spazio virtuale completamente corrispondente allo spazio reale e di utilizzare lo stesso angolo di campo e le stesse proporzioni della fotocamera fisica per il rendering. La trasformazione prospettica subita dall'immagine della fotocamera e dagli oggetti virtuali è quasi identica, con una sola differenza: la maggior parte della trasformazione prospettica per l'immagine della fotocamera avviene nella fotocamera fisica, mentre per gli oggetti virtuali è completamente un processo computazionale.

Qui di seguito si adotta la convenzione OpenGL. Con altre convenzioni, è necessaria una mappatura corrispondente degli assi delle coordinate. Si supponga che il sistema di coordinate della fotocamera sia definito come segue: asse x verso destra, asse y verso l'alto, asse z fuori dallo schermo. Il sistema di coordinate di clipping è definito come: asse x verso destra, asse y verso l'alto, asse z fuori dallo schermo, asse w virtuale.

In questo caso, la matrice di trasformazione prospettica necessaria per il rendering dell'immagine della fotocamera è la seguente:

\[ 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) \]

Dove: flip indica se l'immagine deve essere ribaltata orizzontalmente (1 per sì, 0 per no); \(\theta\) è l'angolo di rotazione oraria dell'immagine in radianti; \(s_x\), \(s_y\) sono fattori di scala per il ridimensionamento o il riempimento mantenendo le proporzioni, e variano con \(\theta\). Questa matrice di trasformazione scala prima l'immagine della fotocamera, poi la ruota e infine la ribalta. Per il rendering, si dovrebbe utilizzare un rettangolo che copra l'intero schermo, ad esempio in OpenGL, i vertici del rettangolo possono essere posizionati a \((-1, -1, 0)\), \((1, -1, 0)\), \((1, 1, 0)\), \((-1, 1, 0)\), con coordinate UV impostate sugli angoli corrispondenti, e quindi utilizzare questa matrice prospettica per il rendering.

La matrice di proiezione prospettica necessaria per il rendering degli oggetti virtuali è la seguente:

\[ 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) \]

Dove: \(n\), \(f\) sono i parametri di clipping vicino e lontano tipicamente utilizzati nelle matrici di proiezione prospettica 3D; \(w\), \(h\) sono la larghezza e l'altezza in pixel dell'immagine della fotocamera; \(f_x\), \(f_y\), \(c_x\), \(c_y\) sono i parametri intrinseci comuni nel modello della fotocamera, con \(f_x\), \(f_y\) che rappresentano le lunghezze focali in pixel, e \(c_x\), \(c_y\) la posizione del punto principale in pixel. Questa matrice di proiezione esegue in sequenza le seguenti trasformazioni: la trasformazione prospettica dei parametri intrinseci (con due trasformazioni del sistema di coordinate dovute alla direzione opposta degli assi y e z nel sistema di coordinate dell'immagine OpenCV rispetto al sistema di coordinate della fotocamera OpenGL), la trasformazione dal sistema di coordinate dei pixel dell'immagine al sistema di coordinate del rettangolo dell'immagine, la trasformazione per il clipping vicino e lontano, e infine la trasformazione prospettica utilizzata per il rendering dell'immagine della fotocamera.

Semplificando, otteniamo:

\[ 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) \]

Da quanto sopra, si evince che il rendering richiede generalmente due passaggi: uno per l'immagine della fotocamera e uno per gli oggetti virtuali, che vengono sovrapposti all'immagine della fotocamera.

Alcuni motori 3D rappresentano la matrice di proiezione prospettica con parametri come l'angolo di campo orizzontale e le proporzioni. Se si ignorano la rotazione, il ribaltamento e l'offset del punto principale, è possibile calcolarla, dove l'angolo di campo orizzontale è \(\alpha=2 \arctan{\frac{w}{2 f_x}}\) e le proporzioni sono \(r=\frac{w}{h}\).

Si noti che questo processo non considera la distorsione della fotocamera, poiché attualmente la distorsione della fotocamera sulla maggior parte dei telefoni è molto lieve.