Matrices
Las matemáticas que
hay tras las transformaciones se simplifican gracias a las matrices. Cada una
de las transformaciones puede conseguirse multiplicando una matriz que contenga
los vértices por una matriz que describa la transformación. Por tanto todas las
transformaciones ejecutables con ogl pueden describirse como la multiplicación
de dos o más matrices.
El canal de transformaciones
Para poder llevar a
cabo todas las transformaciones de, deben modificarse dos matrices: la matriz
del Modelador y la matriz de Proyección. OpenGL proporciona muchas funciones de
alto nivel que hacen muy sencillo la construcción de matrices para
transformaciones. Éstas se aplican sobre la matriz que
este activa en ese instante. Para activar una de las dos matrices utilizamos la
función glMatrixMode. Hay dos parámetros posibles:
glMatrixMode(GL_PROJECTION);
Activa la matriz de proyección, y
glMatrixMode(GL_MODELVIEW);
Activa la del modelador. Es necesario especificar con que
matriz se trabaja, para poder aplicar las transformaciones necesarias en función
de lo que se desee hacer.
El camino que va desde los datos “en bruto”
de los vértices hasta las coordenadas en pantalla sigue el siguiente
camino: Primero, el vértice se convierte en una matriz 1x4 en la que los tres
primeros valores son las coordenadas x, y, z. El cuarto número (llamado parámetro
w) es un factor de escala, que no se va a usar de momento, usando el valor 1.0.
Entonces se multiplica el vértice por la matriz del modelador, para obtener las
coordenadas oculares. Éstas se multiplican por la matriz de proyección
para conseguir las coordenadas de trabajo. Con esto se eliminan todos los datos
que están fuera del volumen de proyección. Estas coordenadas de trabajo
se dividen por el parámetro w del vértice, para hacer el escalado relativo.
Finalmente, las coordenadas resultantes se mapean en un plano 2D mediante la transformación
de la vista.
La matriz del modelador
La matriz del modelador es una matriz 4x4 que representa el
sistema de coordenadas transformado que se está usando para colocar y orientar
los objetos. Si se multiplica la matriz del vértice (de tamaño 1x4) por ésta se
obtiene otra matriz 1x4 con los vértices transformados sobre ese sistema de
coordenadas.
OpenGL proporciona funciones de alto nivel para conseguir
matrices de translación, rotación y escalado, y además la multiplican por la
matriz activa en ese instante, de manera que no hay que preocuparse por ello en
absoluto.
Traslación
Imaginemos que se quiere dibujar un cubo con la función de
la librería GLUT glutSolidCube, que lleva como parámetro el lado del cubo. Si
se escribe el siguiente código:
glutSolidCube(5);
Se obtiene un cubo centrado en el origen (0,0,0) y con el
lado de la arista 5. Ahora se quiere mover 10 unidades hacia la derecha (es
decir, 10 unidades en el sentido positivo del eje de las x). Para ello se tendría
que construir una matriz de transformación y multiplicarla por la matriz del
modelador. Ogl nos ofrece la función glTranslate, que crea la matriz de transformación
y la multiplica por la matriz que está activa en ese instante (en este caso debería
ser la del modelador, GL_MODELVIEW). Entonces el código quedaría de la
siguiente manera:
glTranslatef(10.0f, 0.0f, 0.0f);
glutSolidCube(5);
La “f” añadida a la función indica que se usarán
flotantes. Los parámetros de glTranslate son las unidades a desplazar en el eje
x, y y z, respectivamente. Pueden ser valores negativos, para trasladar
en el sentido contrario.
Rotación
Para rotar, tenemos también una función de alto nivel que
construye la matriz de transformación y la multiplica por la matriz activa, glRotate.
Lleva como parámetros el ángulo a rotar (en grados, sentido horario), y después
x, y y z del vector sobre el cual se quiere rotar el objeto. Una rotación
simple, sobre el eje y, de 10° sería:
glRotatef(10, 0.0f, 1.0f, 0.0f);
Escalado
Una transformación de escala incrementa el tamaño de nuestro
objeto expandiendo todos los vértices a lo largo de los tres ejes por los factores
especificados. La función glScale lleva como parámetros la escala en x, y y z,
respectivamente. El valor 1.0f es la referencia de la escala, de tal forma que:
glScalef(1.0f, 1.0f, 1.0f);
No modificaría el objeto en absoluto. Un valor de 2.0f sería
el doble, y 0.5f sería la mitad. Por ejemplo, para ensanchar un objeto a lo
largo de su eje z, de tal forma que quedase cuatro veces más “alargado” en este eje, sería:
glScalef(1.0f, 1.0f, 4.0f);
La matriz identidad
El “problema” del uso de estas funciones
surge cuando se tiene más de un objeto en la escena. Estas funciones
tienen efectos acumulativos. Es decir, si se quiere preparar una escena como la
de la ilustración:
Con una esfera (de radio 3) centrada en (0, 10, 0) y otra
centrada en (10, 0, 0), y se escribe el siguiente código (que es incorrecto):
glTranslatef(0.0f,
10.0f, 0.0f);
glutSolidSphere(3.0f,
8, 8);
glTranslate(10.0f,
0.0f, 0.0f);
glutSolidsphere(3.0f,
8, 8);
En este código, se dibuja primero una esfera en (0, 10, 0)
como se quiere. Pero después, se está multiplicando la matriz del modelador
(que ya estaba transformada para dibujar la primera esfera) por otra matriz de transformación
que desplaza 10 unidades hacia la derecha. Por ello la segunda matriz se dibujaría,
en (10, 10 ,0), y no en (10, 0, 0), como se pretendía.
Para solventar este problema se debe reiniciar la matriz del
modelador a un estado conocido, en este caso, centrada en el origen de nuestro
sistema de coordenadas oculares. Para ello se carga en la matriz del modelador
la matriz identidad (una matriz 4x4 llena de ceros excepto en la diagonal, que
contiene unos). Esto se consigue gracias a la función glLoadIdentity, que no
lleva parámetros. Simplemente carga la matriz identidad en la matriz activa en
ese instante. El código correcto para el ejemplo anterior quedaría de la
siguiente manera:
glTranslatef(0.0f,
10.0f, 0.0f);
glutSolidSphere(3.0f,
8, 8);
glLoadIdentity();
glTranslate(10.0f,
0.0f, 0.0f);
glutSolidsphere(3.0f, 8, 8);
La matriz de proyección
La matriz de proyección especifica el tamaño y la forma del
volumen de visualización. El volumen de visualización es aquel cuyo contenido
es el que se representa en pantalla. Está delimitado por una serie de planos de
trabajo. De estos planos, los más importantes son los planos de corte, que son
los que nos acotan el volumen de visualización por delante y por detrás. En el
plano más cercano a la cámara (znear) es donde se proyecta la escena para luego
pasarla a la pantalla. Todo lo que está más adelante del plano de corte más
alejado de la cámara (zfar) no se representa.
Proyecciones ortográficas
Una proyección ortográfica es cuadrada en todas sus caras.
Esto produce una proyección paralela, útil para aplicaciones de tipo CAD o
dibujos arquitectónicos, o también para tomar medidas, ya que las dimensiones
de lo que representan no se ven alteradas por la proyección.
Una aproximación menos técnica pero más comprensible de esta
proyección es imaginar que se tiene un objeto fabricado con un material
deformable, y se aplasta literalmente como una pared. Se obtendría el mismo
objeto, pero plano, liso. Pues eso es lo que se vería por pantalla.
Para definir la matriz de proyección ortográfica y
multiplicarla por la matriz activa (que debería ser en ese momento la de proyección,
GL_PROJECTION), se utiliza la función glOrtho, que se define de la siguiente
forma:
glOrtho(limiteIzquierdo, limiteDerecho, limiteAbajo,
limiteArriba, znear, zfar)
Siendo todos flotantes. Los valores de znear y zfar no son
las posiciones de esos planos en el espacio 3D. Representan la distancia desde
el centro de proyección, con valor positivo hacia delante y negativo hacia atrás.
Con esto simplemente se acota lo que será¡
el volumen de visualización (un cubo).
Por ejemplo, la ilustración es un render de un coche con proyección
ortográfica, visto desde delante.
El código utilizado
para esta proyección ha sido:
glOrtho(-0.5f,
0.5f, -0.5f, 0.5f, 0.01f, 20.0f);
Proyecciones perspectivas
Una proyección en perspectiva reduce y estira los objetos más
alejados del observador. Es importante saber que las medidas de la proyección
de un objeto no tienen por qué coincidir con las del objeto real, ya que han
sido deformadas.
El volumen de visualización creado por una perspectiva se
llama frustum. Un frustum es una sección piramidal, vista desde la parte
afilada hasta la base.
Se puede definir esta proyección utilizando la función glFrustum.
Pero existe otra función de la librería GLU llamada gluPerspective que hace el
proceso más sencillo. Se define de la siguiente forma:
Void gluPerspective(angulo, aspecto, znear, zfar);
Los parámetros de gluPerspective son flotantes y definen las
características mostradas en la ilustración, el ángulo para el campo de visión
en sentido vertical, el aspecto que es la relación entre la altura (h) y la
anchura (w) y las distancias znear y zfar de los planos que acotan el fustrum
al observador. Los valores de znear y zfar no son las posiciones de esos planos
en el espacio 3D, representan la distancia desde el centro de proyección, con
valor positivo hacia delante y negativo hacia atrás.
La ilustración muestra la escena del coche de la sección
anterior, esta vez con una proyección en perspectiva:
El código utilizado para definir la proyección ha sido:
gluPerspective(45.0f,(GLfloat)(width/height),0.01f,100.0f);
Se usan 45° de ángulo, la relación entre el ancho y alto de
la pantalla (width y height son el ancho y alto actual de la ventana) y las
distancias a los planos de corte znear y zfar son 0.01 y 100 respectivamente.
No hay comentarios:
Publicar un comentario