Saltar al contenido

Aplicando transformaciones afines para simular perspectiva

agosto 26, 2018
Aplicando transformaciones afines para simular perspectiva Motor Render desde 0 en C++

Las transformaciones nos permitirán modificar la rasterización de los puntos del modelo 3D simulando así perspectiva en nuestra cámara y modelo.

Espacios 2D

Transformaciones Lineales

Una transformación lineal sobre un plano puede representarse con una matriz.

Sea un punto $(x, y)$, cualquier transformación puede escribirse como:

$$\begin{bmatrix}
a &b \\
c & d
\end{bmatrix} \begin{bmatrix}
x \\
y
\end{bmatrix}  = \begin{bmatrix}
ax + by \\
cx + dy
\end{bmatrix}$$

La transformación identidad es aquella que no mueve ningún punto:

$$\begin{bmatrix}
1 & 0 \\
0 & 1
\end{bmatrix} \begin{bmatrix}
x \\
y
\end{bmatrix}  = \begin{bmatrix}
x \\
y
\end{bmatrix}$$

Transformación de escalado

Los coeficientes de la diagonal principal de la matriz de transformación nos permiten escalar los ejes. Por ejemplo la siguiente transformación de escalado:

$$\begin{bmatrix}
\frac{3}{2} & 0 \\
0 & \frac{3}{2}
\end{bmatrix} \begin{bmatrix}
x \\
y
\end{bmatrix}  = \begin{bmatrix}
\frac{3}{2} x \\
\frac{3}{2} y
\end{bmatrix}$$

transformacion escalado

Escala la figura blanca a la figura amarilla.

Utilizando matrices podemos aplicar fácilmente la transformación a todos los puntos de una figura:

$$\begin{bmatrix}
\frac{3}{2} & 0 \\
0 & \frac{3}{2}
\end{bmatrix} \begin{bmatrix}
-1 & 1 & 1 & 0 & -1 \\
-1 & -1 & 0 & 1 & 1
\end{bmatrix}  = \begin{bmatrix}
-\frac{3}{2} & \frac{3}{2} & \frac{3}{2} & 0 & -\frac{3}{2} \\
-\frac{3}{2} & -\frac{3}{2} & 0 & \frac{3}{2} & \frac{3}{2}
\end{bmatrix}$$

En este caso, la matriz de transformación (izquierda) es la misma que en el ejemplo anterior, y la matriz derecha contiene los puntos de la figura. Como resultados obtenemos una matriz con todos los puntos de la figura con la transformación aplicada.

En muchos casos, necesitamos aplicar transformaciones a muchos puntos. Incluso podemos aplicar muchas transformaciones encadenadas sobre una gran cantidad de puntos. Esto supone una gran cantidad de operaciones, aunque se puede simplificar utilizando operaciones con matrices.

La clave reside en que podemos pre-multiplicar todas las transformaciones, para luego aplicarla a los vértices de nuestro objeto solo una vez.

Transformación de transvección – Shearing

La transvección consiste en desplazar un eje de tal forma que los ejes dejen de ser perpendiculares. Es decir, transforma un rectángulo en un paralelogramo o un círculo en una elipse.

Se trata de un mapeo equiárea ya que las lineas que son paralelas a los ejes conservan su longitud pero la longitud de las otras se modifica.

Una transvección se puede representar de la siguiente forma:

$$\begin{bmatrix}
1 & \frac{1}{3} \\
0 & 1
\end{bmatrix} \begin{bmatrix}
x \\
y
\end{bmatrix}  = \begin{bmatrix}
x + \frac{y}{3} \\ y
\end{bmatrix}$$

transformacion transveccion

El primer elemento de la antidiagonal transvecciona la figura en el eje X, mientras que el segundo la transvecciona en el eje Y.

Transformación de rotación

Una transformación de rotación (alrededor del origen) se puede representar como una composición de 3 transvecciones (shears).

Fijaros en el siguiente ejemplo: el objeto blanco se transforma en el rojo, luego al verde y finalmente el azul.

transformacion rotacion transvecciones

Para simplificar las cosas, podemos utilizar directamente la matriz de rotación siguiente:

$$\begin{bmatrix}
cos(\alpha) & -sen(\alpha) \\
sen(\alpha) & cos(\alpha)
\end{bmatrix} \begin{bmatrix}
x \\
y
\end{bmatrix}  = \begin{bmatrix}
x \cdot cos(\alpha) – y \cdot sen(\alpha) \\
x \cdot sen(\alpha) + y \cdot cos(\alpha)
\end{bmatrix}$$

Recordar que el producto de matrices no es conmutativo, no es lo mismo rotar y luego transveccionar que transveccionar y luego rotar.

Transformaciones afines en 2D

Las transformaciones lineales en el plano son composiciones de escalado y transvección. Si realizamos cualquier transformación lineal no se moverá el origen.

Sin embargo, también nos interesa poder realizar traslaciones, pero estas no son lineales.

Lo que haremos será realizar la traslación después de aplicar las transformaciones lineales, de esta forma:

$$\begin{bmatrix}
a & b \\
c & d
\end{bmatrix} \begin{bmatrix}
x \\
y
\end{bmatrix} + \begin{bmatrix}
e \\
f
\end{bmatrix} = \begin{bmatrix}
ax + by +e \\
cx + dy + f
\end{bmatrix}$$

De esta forma, podemos escalar, rotar, transveccionar o trasladar la figura.

Sin embargo, queremos componer varias transformaciones a la vez. Así es como nos queda una composición de dos transformaciones (y vamos a componer muchas de ellas):

$$\begin{bmatrix}
a_2 & b_2 \\
c_2 & d_2
\end{bmatrix} ( \begin{bmatrix}
a_1 & b_1 \\
c_1 & d_1
\end{bmatrix}  \begin{bmatrix}
x \\
y
\end{bmatrix} + \begin{bmatrix}
e_1 \\
f_1
\end{bmatrix} ) + \begin{bmatrix}
e_2 \\
f_2
\end{bmatrix}$$

Como veis la cosa empieza a ponerse fea, y solo hemos compuesto dos transformaciones. Necesitamos encontrar una forma de simplificar el proceso.

Homogeneizando las transformaciones en un espacio de dimensión superior

Lo que haremos será proyectar nuestro espacio 2D sobre el plano z=1 en un espacio 3D. Esto implica añadir una fila y columna a nuestra matriz de transformación y una coordenada igual a 1 ($z = 1$) al vector que vamos a transformar:

$$\begin{bmatrix}
a & b & e \\
c & d & f \\
0 & 0 & 1
\end{bmatrix} \begin{bmatrix}
x \\
y \\
1
\end{bmatrix} = \begin{bmatrix}
ax + by + e \\
cx + dy + f \\ 1
\end{bmatrix}$$

Al multiplicar la nueva matriz de transformación por el vector aumentado con 1 obtenemos otro vector con 1 en el último componente. Además, los otros dos componentes tienen la forma que deberían tener después de aplicar las transformaciones.

De esta forma, para las traslaciones que no son lineales en espacios 2D, podemos insertar nuestro espacio 2D en el plano $z=1$ de un nuevo espacio 3D y aplicar una transformación lineal sobre el nuevo espacio 3D.

Ahora, tenemos que volver a proyectar el espacio 3D resultante en nuestro plano 2D inicial. Esto lo haremos dividiendo por la tercera componente del vector:

$$\begin{bmatrix}
x \\
y \\ z
\end{bmatrix} \rightarrow \begin{bmatrix}
x/z \\
y/z
\end{bmatrix}$$

Lo que hacemos es dibujar una linea entre el origen al punto que queremos proyectar y buscar la intersección con el plano $z=1$.

  • El punto $(x,y,z)$ se proyecta sobre $(x/z, y/z)$.
  • El punto $(x,y,1)$ se proyecta sobre $(x,y)$.
  • El punto $(x,y,1/2)$ se proyecta sobre $(2x, 2y)$.
  • El punto $(x,y,1/4)$ se proyecta sobre $(4x, 4y)$.

Si continuamos el proceso acercándonos a un valor de $z=0$, entonces la proyección se desplazará del origen en la dirección $(x,y)$.

Visto de otra forma, el punto $(x, y, 0)$ se proyecta sobre un punto infinitamente lejano en la dirección $(x,y)$, lo que se entiende como un vector.

De esta forma, las coordenadas homogéneas nos permiten distinguir entre un punto o un vector:

  • Los elementos con $z= 0$ serán vectores.
  • Los elementos con $z \neq 0$ serán puntos.

Visto desde una forma práctica:

  • Vector + Vector = Vector ó Vector – Vector = Vector
  • Punto + Vector = Punto

Composición de transformaciones homogénea

En la practica tendremos que componer muchas transformaciones.

Por ejemplo, para rotar un objeto 2D alrededor de un punto $(x_0, y_0)$:

  1. Trasladamos el punto $(x_0, y_0)$ al origen.
  2. Rotamos.
  3. Invertimos la traslación del paso 1.

$$T = \begin{bmatrix}
1 & 0 & x_0 \\
0 & 1 & y_0 \\ 0 & 0 & 1
\end{bmatrix} \begin{bmatrix}
cos(\alpha) & -sen(\alpha) & 0 \\
sen(\alpha) & cos(\alpha) & 0 \\ 0 & 0 & 1
\end{bmatrix}\begin{bmatrix}
1 & 0 & -x_0 \\
0 & 1 & -y_0 \\ 0 & 0 & 1
\end{bmatrix}$$

Cuando trabajemos en 3D, las secuencias de acciones serán un poco mas largas, pero la idea será misma.

Espacios 3D

En este caso trabajaremos con una proyección central. Es decir, todas las lineas proyectantes del dibujo pasarán por un punto (nuestra cámara).

Dado un punto $P = (x, y ,z)$, queremos proyectarlo sobre el plano $z = 0$. Nuestra cámara estará fija en un punto del eje Z, en el punto $(0, 0, c)$.

Proyeccion 3D

Los triángulos $\overline{ABC}$ y $\overline{ODC}$ son semejantes. Es decir, $\frac{|AB|}{|AC|} = \frac{|OD|}{|OC|}$.

Lo que implica que $\frac{x}{c – z} = \frac{x’}{c}$. Por lo tanto, si ahora nos fijamos en los triángulos $\overline{CPB}$ y $\overline{CP’D}$:

$$x’ = \frac{x}{1-z/c}$$

$$y’ = \frac{y}{1-z/c}$$

Transformaciones afines 3D en espacios 4D

Igual que hemos hecho con las transformaciones afines en 2D, para los puntos en 3D haremos lo mismo, utilizaremos las coordenadas homogéneas.

Realizamos un proceso similar al anterior:

  1. Un punto $(x, y, z)$ se aumenta con un 1 al espacio 4D (x, y, z, 1).
  2. Realizamos la transformación en el espacio 4D.
  3. Proyectamos el espacio 4D de nuevo sobre el 3D.

Podemos realizar, por ejemplo, la siguiente transformación:

$$\begin{bmatrix}
1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & r & 1
\end{bmatrix} \begin{bmatrix}
x \\
y \\ z \\ 1
\end{bmatrix} = \begin{bmatrix}
x \\ y \\ z \\ rz + 1
\end{bmatrix}$$

Y si volvemos a proyectar sobre el espacio 3D

$$\begin{bmatrix}
x \\ y \\ z \\ rz + 1
\end{bmatrix} \rightarrow \begin{bmatrix}
\frac{x}{rz+1} \\ \frac{y}{rz+1} \\ \frac{z}{rz+1}
\end{bmatrix}$$

Si nos fijamos en el resultado del apartado anterior, estamos realizando esta transformación pero con un valor $r = \frac{-1}{c}$.

Calculando la proyección central con una cámara en el eje Z a una distancia c

Para calcular la proyección de una perspectiva central con una cámara situada en el eje Z, y a una distancia c del origen:

  1. Transformamos el punto 3D a un punto 4D con un 1.
  2. Aplicamos la transformación multiplicando por su matriz correspondiente.
  3. Volvemos a proyectar sobre el espacio 3D.

$$\begin{bmatrix}
x \\ y \\ z
\end{bmatrix} \rightarrow \begin{bmatrix}
x \\ y\\ z \\ 1
\end{bmatrix}$$

$$\begin{bmatrix}
1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0 \\ 0 & 0 & -1/c & 1
\end{bmatrix} \begin{bmatrix}
x \\ y\\ z \\ 1
\end{bmatrix} = \begin{bmatrix}
x \\ y\\ z \\ 1 – z/c
\end{bmatrix}$$

$$\begin{bmatrix}
x \\ y \\ z \\ 1 – z / c
\end{bmatrix} \rightarrow \begin{bmatrix}
\frac{x}{1 – z/c} \\ \frac{y}{1 – z/c} \\\frac{z}{1 – z/c}
\end{bmatrix}$$

Deformamos el modelo de forma que dibujamos la perspectiva sobre él interpretando los valores de z.