viernes, 2 de octubre de 2009

Introducción a Motores 3D (parte 2)

Representación en pantalla

En el post anterior mencionaba que para crear nuestro mundo 3D encontraríamos dos grandes desafíos: la modelación de los objetos en estructuras de datos y la representación de estos en nuestra pantalla 2D.

Mostré un poco, de manera general, la modelación de los objetos; ahora hablaremos de la representación de estos.

La representación de objetos 3D no es un tema fácil, requiere que el programador, no solamente sea un buen programador y conocedor del lenguaje, sino que además debe tener bastantes conocimientos de vectores, albegra lineal, física, geometría analítica, trigonometría, análisis numérico, etc.

A pesar de que nuestro modelo almacena la información en tres dimensiones, debemos transformar esta información a dos dimensiones para representarlo en el monitor. Este cálculo matemático es demasiado complejo, y aunque es posible realizarlo, nos tomaría demasiado tiempo y además ya existen herramientas que lo hacen por nosotros. Una de estas herramientas es OpenGL, éste nos brinda las funciones necesarias para poder realizar estta transformación de nuestro modelo 3D al monitor 2D.

Los objetos pasan por una serie de transformaciones para poder llegar a la representación final, estas transformaciones son:
  • Modelview Transformation
  • Projection Transformation
  • Perspective Division
  • Viewport Transformation

Modelview Transformation

En este paso, las coordenadas locales de cada objeto son tomadas y transformadas para darles una posición en el mundo 3D. Como pudimos ver anteriormente, las coordenadas que tenemos almacenadas en nuestro modelo, son coordenadas relativas al objeto y no a su posición en el mundo. En este paso, el objeto es movido a la posición que debe tener en el mundo 3D, ya sea a través de rotaciones, traslaciones o escalamientos. Es importante apuntar que la apariencia final del objeto depende del orden en que se apliquen estas transformaciones. Ejemplo:




En el inciso a) aplicamos primero una rotación al objeto y luego una traslación sobre el eje x, dado que su eje x está rotado se mueve diagonalmente. En el inciso b) aplicamos las mismas transformaciones pero en orden inverso, obteniendo así un resultado diferente.

Además de estas operaciones, se debe tomar en cuenta también la posición de la cámara, lo que transformará entonces nuestras coordenadas anteriores a unas nuevas que dependen del punto de vista del observador.

Para lograr este tipo de transformaciones, OpenGL utiliza una matriz llamada la GL_MODELVIEW matrix. Cada vértice es multiplicado por los valores de esta matriz dándole así su posición real en el mundo. Es decir, que para cada objeto, debo almacenar información que me indique cómo ubicarlo en la posición q deseo. Aplico primero las transformaciones para modificar la matriz y luego voy calculando el valor de cada vértice. Para el ejemplo del inciso a) tendríamos un código similar a este:

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(10, 0.0f, 0.0f, 1.0f);
glTranslatef(10.0f, 0.0f, 0.0f);

La primera instrucción le indica a OpenGL con qué matriz vamos a trabajar. Luego cargamos en ella la matriz identidad y luego aplicamos las operaciones de rotación y traslación. Una vez que hemos realizado todas las transformaciones que deseamos para el objeto, le decimos a OpenGL cuáles son los vértices que queremos representar:

glBegin(GL_TRIANGLES);
for (j=0; j<numPolygons;j++) {
//----------------- FIRST VERTEX -----------------
glColor3f(polygon[j].red,polygon[j].green,polygon[j].blue);
glVertex3f( vertex[ polygon[j].vertex[0] ].x, vertex[ polygon[j].vertex[0] ].y,vertex[ polygon[j].vertex[0] ].z);

//----------------- SECOND VERTEX -----------------
glColor3f(polygon[j].red,polygon[j].green,polygon[j].blue);
glVertex3f( vertex[ polygon[j].vertex[1] ].x, vertex[ polygon[j].vertex[1] ].y,vertex[ polygon[j].vertex[1] ].z);

//----------------- THIRD VERTEX -----------------
glColor3f(polygon[j].red,polygon[j].green,polygon[j].blue);
glVertex3f( vertex[ polygon[j].vertex[2] ].x, vertex[ polygon[j].vertex[2] ].y,vertex[ polygon[j].vertex[2] ].z);
}
glEnd();

glBegin le dice a OpenGL el tipo de polígono que vamos a utilizar para representar nuestro objeto. gl Color3f es para indicar el color y glVertex3f es para indicar las coordenadas de cada vértice.

Como podemos ver, se requiere entonces almacenar por cada objeto una matriz para ubicar el objeto en la posición correcta, por lo que en nuestra definición de la clase Object3D debemos añadir esta estructura de datos. La matriz es una matriz 4x4, la explicación del funcionamiento de esta matriz es un tema que esta fuera del objetivo de este post, pero que puede ser deducido a través de un breve análisis matemático. Puede consultarse el siguiente link: http://www.geocities.com/valcoey/intro3d.html