Introduccion y Vectores

0.0 Introduccion

Las matematicas son una parte fundamental del desarrollo de juegos actual. Desgraciadamente no todos tenemos la base matematica que nos gustaria (o deberiamos) tener, ya sea por que no las recuerdas, o simplemante nunca has tenido ni idea.

Con este articulo, que pretendo que sea una serie, intentaremos (yo me incluyo dentro) aprender un poco mas sobre el tema. Y la mejor forma que se me ocurre es construir (entre todos a ser posible) una libreria generica a la que llamaremos Game Mathematical Library (GML) y que hiremos construyendo en cada entrega. Todo el codigo estara bajo licencia Lesser GPL.

Los primeros capitulos seran mas o menos los siguientes:

  • Capitulo 0: Mini-introduccion
  • Capitulo 1: Vectores
  • Capitulo 2: Matrices
  • Capitulo 3: Transformaciones
  • Capitulo 4: Calculo

Si os interesa el tema podria seguir con : visibilidad, colisiones, incluso algo de fisica…

Intentare seguir la siguiente nomengaltura, escalares en italica (x, y, z, w); vectores en bold y usaremos las letras V, P, Q, R, etc;
matrices en tambien en bold y usando las letras M, N, etc; quaternions en bold y con Q1, Q2, Q3; y mas que ire definiendo por aqui.

Cada entregra ira acompañada de su respectivos codigos en C++, junto el proyecto para Visual C++ v7, ademas de las posibles correcciones y/o ampliaciones de las entregas anteriores.

Todas las aportaciones / sugerencias / correcciones / ideas / etc seran bienvenidas en mi correo [email protected]

1.0 Introduccion a los vectores

Los vectores son una parte muy importante de cualquier motor 3D. Representan una magnitud y una direccion. Se utilizan tanto para representar la geometria de un modelo 3D, como la posicion de una camara en el espacio. Aprender a operar con vectores es fundamental para el programador 3D.

Un vector V de dimesion n podriamos representarlo asi:

V = < V1, V2, …, Vn >

Si es de dos dimensiones:

A cada V1, V2, …, Vn, las llamaremos componentes del vector V, y representan su magnitud en cada eje. Para el tema que nos interesa solo trataremos con vectores de 2, 3 y 4 dimensiones. Los definiremos asi:

2 dimensiones

union
{
struct { real x, y; };
struct { real width, height; };
real v[2];
};
3 dimensiones

union
{
struct { real x, y, z; };
real v[3];
};
4 dimensiones

union
{
struct { real x, y, z, rhw; };
real v[4];
};

Definiremos un vector por sus coordenadas cartesianas, otra forma seria hacerlo por sus coordenadas polares, magnitud y angulo, pero no nos seria nada util. Usamos uniones para poder acceder a las componentes de varias maneras, segun nos venga mejor. Un ejemplo de esto es el vector de 2 dimensiones, que ademas de como vector, lo usaremos como point y size.

1.1 Operaciones basicas

Las primeras operaciones que realizaremos con un vector, seran las aritmeticas basicas, osea : opuesto, suma, resta, multiplicacion por escalar y division por escalar. Tambien tendremos la version aditiva de las anteriores (operadores +=, -=, *=, /=).

En general, dados 2 vectores P y Q de n dimensiones:

P + Q = (P1 + Q1, P2 + Q2, …, Pn + Qn)

Dado un vector, P de n dimensiones, y un escalar a:

aP = (aP1, aP2, …, aPn)

La resta y division, serian las opuestas a la suma y multiplicacion respectivamente. El modulo de un vector, o longitud, es un escalar que notaremos como |V| que representa la longitud de ese vector (>= 0).

Para averiguar el modulo, calcularemos el valor de la hipotenusa del triangulo rectangulo formado por ABC usando el teorema de pitagoras. En el caso de nuestra clase vec2, seria asi:

  real len() const { return sqrt(x*x + y*y); }
real len2() const { return (x*x + y*y); }

Usaremos vec2::len2() (el cuadrado del modulo) cuando queramos comparar 2 vectores y saber cual es mas grande, sin importanos cuanto. Asi nos ahorraremos hacer una costosa raiz cuadrada. Un ejemplo de esto serian las tipicas operaciones logicas, igualdad, mayor, menor, mayor igual, menor igual y desigualdad.

Del modulo de un vector podemos obtener las siguientes propiedades:

Teorema 1[1.1]
Dado cualquier escalar, a, y dos vectores cualesquiera, P y Q, se cumple lo siguiente:

(a) |P| >= 0

(b) |P| = 0, si y solo si P = <0, 0, …, 0>, P seria el vector nulo

(c) |aP| = |a| |P|

(d) |P + Q| <= |P| + |Q|

A un vector que tenga exactamente uno como modulo, lo llamaremos vector unitario. En el caso de vec2, tenemos los vectores estaticos
vec2::X y vec2::Y que serian 2 vectores unitarios que representarian los ejes de un sistema de 2 dimensiones. Tambien tenemos vec2::ZERO que seria un vector sin longitud, osea con modulo cero, que llamaremos vector nulo (todas sus componentes son cero).

Podemos cojer cualquier vector que no sea nulo, y transformalo en un vector unitario, conservando su orientacion y direccion originales. A este proceso se le llama normalizar un vector (NO confundir con vector normal, no es lo mismo). Para normalizar un vector no nulo, tenemos que multiplicarlo por la inversa de su modulo (o lo que es lo mismo: dividirlo por su modulo).

El vector azul seria el resultado de normalizar el vector rojo. Todos los posibles vectores unitarios en un sistema de 2 dimensiones estarian contenidos en la circunferencia negra. El codigo de vec2 seria:

 const vec2& vec2::normalize()
{
real a = len();
real b = 1.0f / a;

x *= b;
y *= b;

return (*this);
}

1.2 Producto escalar

El producto escalar, o dot product o inner product en ingles, escrito como P·Q, es una de las operaciones mas usadas en los motores 3D,
ya que nos da una medida de la diferencia entre las direcciones de 2 vectores:

Viendo este dibujo podemos apreciar la importancia del productor escalar, por ejemplo para conocer si dos vectores son perpendiculares o calcular la proyeccion de un vector sobre otro, como veremos mas abajo. El producto escalar en nuestro ejemplo de vec2 seria asi:

 real vec2::dot(const vec2& v) const
{
return (x * v.x) + (y * v.y);
}

El siguiente teorema revelara el secreto que se esconde tras el producto escalar :

Teorema 2[1.2]
Dados tres vectores cualesquiera, P, Q y R, el producto escalar de ambos, P·Q, cumple:

P·Q = |P| |Q| cos( ß ) (|P|cos(ß) seria la zona verde del dibujo de arriba)

P·Q = Q·P

(aPQ = a(P·Q)

P·(Q + R) = P·Q + P·R

donde ß es el angulo entre las lineas que conectan el origen de los puntos P y Q.

Lo verdaderamente importatente de esto, es que P y Q son perpendiculares, si y solo si P·Q = 0. Si su producto escalar es 0, diremos que ambos vectores son ortogonales.

Otra informacion valiosa que podemos sacar del producto escalar es de su signo. Si desde el origen del vector P, trazamos una linea perpendicular a este, dividiremos el espacio en 2 mitades. Si el producto escalar entre P y Q es mayor de 0, entonces Q esta en la mitad en la que esta P. Si es menor que cero, Q se encuentra en la otra mitad (si fuese igual a cero, Q se encontraria en esa linea que divide el espacio).

Gracias a todas estas propiedades, y a la trigonometria, podemos calcular cosas como la proyeccion de un vector sobre otro, o el angulo que forman 2 vectores:

 const vec2& vec2::projectOnto(const vec2 &v)
{
return (dot(v) / (v.dot(v))) * v;
}

real vec2::angle(const vec2 &v)
{
real mag = len() * v.len();

if (mag == 0.0)
return 0;

return (real)acos(dot(v) / mag);
}

1.3 Producto vectorial

El producto vectorial, o cross product en ingles, escrito como P x Q, de dos vectores de tres dimensiones nos devuelve otro vector perpendicular a estos dos. Esta operacion se usa en muchos de los calculos de los motores 3D:

Lo calcularemos asi:

 const vec3& vec3::cross(const vec3 &v)
{
vec3 res;

res.x = (z * v.y) - (y * v.z);
res.y = (x * v.z) - (z * v.x);
res.z = (y * v.x) - (x * v.y);

return res;
}

El resultado de esta operacion, aparte del vector nulo, es un vector perpendicular, pero ¿ en que direccion ? Lo podemos saber gracias a la regla de la mano derecha. Si calculamos P x Q, el vector resultante tendra la direccion que marque el dedo pulgar de la mano derecha, si los dedos de esta oscilan de P a Q por el camino mas corto.

Al igual que el producto escalar, el productor vectorial tambien esconde un secreto, que es:

Teorema 3[1.3]
Dados dos vectores cualesquiera, P y Q, el producto vectorial de ambos, P x Q, cumple:

|P x Q| = |P| |Q| sin( ß )

donde ß es el angulo entre las lineas que conectan el origen de los puntos P y Q.

Gracias al teorema anterior, podemos decir que el modulo del vector resultante de P x Q es igual al area de paralelogramo formado por los vectores P y Q. Asi, podemos decir tambien, que el arena a de un triangulo formado por los vertices V1, V2 y V2 es:

a = 1/2 ||(V2V1) x (V3V1)||

1.4 Transformaciones

Las tipicas transformaciones que podemos realizar sobre un vector son tres: translacion, rotacion y escalado. Aqui veremos como hacerlas de una manera muy simple. En el siguiente capitulo, veremos como hacer esto mismo, y mas cosas, gracias a las matrices, de una manera mucha mas comoda y elegante.

La translacion de un vector, tambien podriamos llamarla movimiento de un vector. Para ello usaremos la suma y resta que hemos
visto :

Vnuevo = Vviejo + D

Donde Vviejo seria el vector original, D seria el vector desplazamiento que queremos aplicarle y Vnuevo seria el resultado. El escalado de un vector es tambien muy sencillo y logico, basta con multiplicar por un escalar sus componentes:

Vnuevo = aVviejo

Por ultimo, podemos rotar un vector en los tres ejes x, y, y z. No lo conseguiremos de una forma tan simple como en las anteriores, ya que recurriremos a la trigonometria, las rotaciones en el eje x:

ynuevo = yviejocos(ß) – zviejosin(ß)

znuevo = yviejosin(ß) + zviejocos(ß)

en el eje y:

xnuevo = xviejocos(ß) – zviejosin(ß)

znuevo = xviejosin(ß) + zviejocos(ß)

y en el eje z:

xnuevo = xviejocos(ß) – yviejosin(ß)

ynuevo = xviejosin(ß) + yviejocos(ß)

Con esto se acaba el tema de los vectores. Podeis bajaros el comienzo de GML, que contienen las clases vec2, vec3 y vec4.
En la siguiente entrega, matrices, hasta pronto!!

  1. #1 por angel adrian el 30/09/2009 - 10:09 pm

    ta chido

  2. #2 por StrongCod3r el 29/12/2009 - 2:24 am

    Buen tute, espero que lo continues.thx

  3. #3 por Roberto Glez el 29/07/2010 - 8:03 am

    gracias por el tutorial. Saludos ;)

  4. #4 por liz el 31/08/2010 - 11:23 pm

    hola y ps esta chido

  5. #5 por simon gonzaleez el 01/03/2011 - 10:59 pm

    liz :hola y ps esta chido

  6. #6 por mario el 29/11/2011 - 2:45 pm

    hola esta muy interesante el tema gracias por esta ayuda.

  7. #7 por mario el 29/11/2011 - 2:46 pm

    QUE TEMA MUY INTERESANTE GRACIAS POR PUBLICARLO ATENTAMENTE MARIO.

  8. #8 por Paco47 el 13/07/2012 - 1:11 pm

    muy buen artículo, si señor

(no sera publicado)
*