3.- Vista Tridimensional. 3.1.- Proceso de vista 3D 3.2.- Comandos de transformaciones de propósito general 3.3.- Transformaciones de modelo y vista 3.4.- Transformaciones de proyección 3.5.- Transformaciones de ventana 3.6.- Manejar la pila de matrices En este tema veremos como ubicar los objetos y el punto de vista del observador dentro de un espacio tridimensional, para obtener la vista de la escena que deseemos. Para crear una imagen plana es decir bidimensional sobre la pantalla del ordenador, a partir de una descripción tridimensional de la escena, tenemos que realizar una serie de operaciones, que incluyen la proyección y el recorte de los objetos sobre un volumen de vista específico. Basándose en OpenGL estas operaciones son las siguientes: Transformaciones, que se utilizan para operaciones de modelado, vista y proyección. Recorte de objetos o porciones de objetos no visibles. Correspondencia entre coordenadas 2D y pixeles de la pantalla. En este capítulo veremos las funciones de OpenGL que nos facilitan estas operaciones.
Página: 22 3.1.- PROCESO DE VISTA. El proceso de vista de una escena lleva asociados una serie de pasos. En primer lugar tenemos que realizar una transformaciones, que son las siguientes: Transformaciones de vista, para ubicar al observador en el lugar preciso desde el que se quiere observar la escena. Transformaciones de modelo para componer la escena a partir de los objetos definidos en sus propios sistemas de coordenadas, por medio de traslaciones, rotaciones y escalados de los mismos. Transformaciones de proyección, para ajustar el zoom, es decir el ángulo de vista del observador. Se define la forma y orientación del volumen de vista. Transformaciones de viewport para determinar el tamaño final de la ventana en la que se visualiza la imagen. Para las tres primeras operaciones se generan matrices de transformación 4x4 y se multiplican por todos los vértices de los objetos. Cuando se han definido todas las transformaciones necesarias, se puede dibujar la escena. Para eso OpenGL transforma cada vértice de todos los objetos en la escena, por medio de las matrices de transformación tanto de modelo como de vista. De esta forma cada vértice se proyecta y se recorta si queda fuera del volumen de vista. Finalmente los vértices pertenecientes a la escena se transforman a coordenadas de ventana. 3.2.- COMANDOS DE TRANSFORMACIÓN DE PROPÓSITO GENERAL. En este apartado introduciremos los comandos de transformación más generales, basados todos en multiplicaciones de matrices.
Página: 23 void glmatrixmode (GLenum mode) Especifica cual de las matrices se va a modificar según el argumento mode. Las matrices que se pueden modificar son la matriz de modelo y vista combinadas, la matriz de proyección y la matriz de textura, que no utilizaremos en este curso. Los parámetros que se utilizan para cada caso se reflejan en la tabla 3.1. Tabla 3.1. Argumento GL_MODELVIEW GL_PROJECTION GL_TEXTURE Matriz Matriz de modelo y vista. Matriz de proyección Matriz de textura. Los siguientes comandos de transformación afectan a la matriz especificada por esta función. Sólo puede modificarse una matriz en cada operación, y estará activa la última indicada por la función anterior. Por defecto se modifica la matriz de modelo y vista (ModelView). Al comienzo todas las matrices están inicializadas a la matriz identidad. void glloadidentity (void) Inicializa la matriz actual, con los valores de la matriz identidad de 4x4. Se utiliza para limpiar la matriz actual, al comienzo o bien para volver a la posición inicial del objeto. void glloadmatrix{fd} (const TYPE *m) Especifica una matriz a partir del puntero m para cargarla en la matriz actual. El argumento m es un vector de 16 (4x4) componentes. Si definimos una matriz en lenguaje C como m[4][4], entonces en OpenGL el elemento m[i][j] es la columna i y la fila j. Esto es la notación contraria a la que normalmente se utiliza, por lo tanto para evitar problemas es más fácil definir siempre vectores de 16 componentes es decir m[16]. void glmultmatrix{fd} (const TYPE *m) Multiplica la matriz actual por la matriz de 4x4 especificada por el parámetro m. El orden de la multiplicación es matriz actual C por matriz especificada M, es decir el resultado es C * M.
Página: 24 3.3.- TRANSFORMACIONES DE MODELO Y VISTA. Como ya vimos estos dos tipos de transformaciones hacen referencia a la ubicación de los objetos dentro de la escena, y a la ubicación del observador en la posición deseada para observar la escena. En OpenGL estas dos matrices se combinan en una única matriz llamada ModelView. Para todas las transformaciones que veremos a continuación se ha de activar como matriz actual la matriz de Modelo/Vista, ejecutando primero la función: glmatrixmode (GL_MODELVEW); 3.3.1.- Transformaciones de Modelo. Estas operaciones sirven para transformar la posición, orientación y tamaño del modelo o modelos que forman la escena. Las operaciones básicas que tenemos son las ya vistas en clase como: Trasladar. Rotar. Escalar. Las funciones de OpenGL para realizar estas operaciones son las siguientes. void gltraslate{fd} (TYPE x, TYPE y, TYPE z) Multiplica la matriz actual por una matriz de traslación, que desplaza un objeto por las distancias de traslación x, y, z. Este desplazamiento se realiza desde la posición actual del objeto hasta alcanzar la posición final, es decir utiliza desplazamientos relativos. void glrotate{fd} (TYPE angle, TYPE x, TYPE y, TYPE z) Multiplica la matriz actual por una matriz de rotación que gira el objeto en el sentido contrario al del giro de las agujas del reloj, sobre el eje que va desde el origen de coordenadas al punto (x, y, z) especificado como parámetro, un ángulo igual al especificado como argumento. void glscale{fd} (TYPE x, TYPE y, TYPE z) Multiplica la matriz actual por un matriz de escalado, que escala el objeto en los tres ejes de coordenadas X, Y, Z, por los factores de escala x, y, z. Cada coordenada de un punto (x, y, z) se multiplica por los factores de escala especificados como parámetros.
Página: 25 Escalar por valores mayores que 1.0 estira un objeto y con valores menores de 1.0 encoge el objeto. 3.3.2.- Transformaciones de Vista. Las transformaciones de vista cambian la posición y orientación del punto de vista del observador. En general se componen de operaciones básicas de transformación, sobre todo traslaciones y rotaciones. Las transformaciones de vista deben realizarse antes de cualquier transformación de modelo. La ubicación por defecto del observador es el origen de coordenadas, y orientado hacia el eje Z negativo, es decir mira hacia el interior de la pantalla. Existen varias formas de ubicar la posición final del observador en un programa realizado con OpenGL. A continuación señalamos algunas de ellas: Utilizando las rutinas de traslación y rotación. De esta forma a partir de la posición inicial ya descrita podemos mover la cámara a la posición adecuada por medio de estas rutinas. Utilizar la rutina glulookat () que pertenece a las librerías auxiliares de OpenGL, para indicar hacia donde mira la cámara. Crear rutinas de utilidad propias, que calculen todos los movimientos necesarios, según los parámetros que queramos darles. A continuación veremos las dos primeras de estas formas, pues la última queda para aplicaciones avanzadas. 3.3.2.1.- Utilización de las Rutinas gltraslate() y glrotate(). Para utilizar este tipo de funciones tenemos que tener en mente que mover el punto de vista del observador, equivale a desplazar todos los objetos de la escena en el sentido contrario. Como ya hemos mencionado la cámara está inicialmente colocada en el origen de coordenadas, mirando hacia el eje Z-negativo. El caso más simple es el de moverla hacia atrás, es decir alejarla, de los objetos que componen la escena. Para esto se utiliza la llamada a la función gltraslate (). Así por ejemplo, si como en el caso del cubo, queremos mover al observador cinco unidades hacia atrás, para alejarse de los objetos, esto es lo mismo que mover todos los objetos -5.0 unidades en el eje Z. Así pues, la función: gltraslate (0.0, 0.0, -5.0)
Página: 26 aplicada al principio de todo tiene el mismo efecto que desplazar el punto de vista a la posición (0.0, 0.0, 5.0): Figura 3.1: Desplazamiento de los objetos que componen la escena. Por otro lado, para ver la escena desde un lado se pueden rotar estos, o bien la posición del observador. Así pues, con la secuencia de pasos: gltraslate (0.0, 0.0, -5.0) glrotate (90.0, 0.0, 1.0, 0.0) obtenemos una vista lateral del objeto, ya que primero me alejo y después lo roto 90, en el sentido positivo del eje Y. 3.3.2.2.- Utilización de la Rutina glulookat (). void glulookat (GLdouble eyex, GLdouble eyey, GLdouble eyez, GLdouble centrox, GLdouble centroy, GLdouble centroz, GLdouble upx, GLdouble upy, GLdouble upz) Define una matriz de vista y la postmultiplica por la matriz actual. La ubicación viene indicada por los puntos eyex, eyey, eyez. Los parámetros centrox, centroy, centroz, determinan el punto al cual mira la cámara, que en general es el centro de la escena. Los último los argumentos upx, upy, upz indican la dirección de abajo arriba del volumen de vista. Esta función engloba varias rutinas básicas de OpenGL, sobre todo rutinas de traslación y rotación. 3.4.- TRANSFORMACIONES DE PROYECCIÓN.
Página: 27 Especificar los parámetros de proyección es lo mismo que seleccionar el objetivo de una cámara. Es decir los factores que se ven afectados por estos parámetros son al ángulo de vista y la forma de proyectar. Antes de especificar ninguno de estos parámetros tenemos que activar la matriz de proyección como matriz actual e inicializarla con la matriz identidad. Para esto se utiliza la secuencia de funciones siguiente: glmatrixmode (GL_PROJECTION); glloadidentity (); El propósito de estas transformaciones es seleccionar el volumen de vista. Este volumen se usa para: Determinar cómo se proyectan los objetos en la pantalla, es decir si se utiliza proyección perspectiva o paralela. Definir qué objetos o porciones de los objetos serán visibles en la imagen final. Si bien OpenGL permite los dos tipos de proyecciones, tanto perspectiva como paralela sólo se presentan aquí las funciones para la proyección perspectiva, pues es la que se utilizará en estas prácticas. 3.4.1.- Proyección Perspectiva. Como ya hemos visto en teoría el volumen de vista generado por una proyección perspectiva es una pirámide truncada como puede verse en la figura 3.2. Este método de proyección se utiliza en aplicaciones de simulación y en cualquier otra aplicación que requiera realismo, ya que la imagen es similar a la que capta el ojo humano. Figura 3.2: Volumen de vista en perspectiva especificado por glfrustum().
Página: 28 La función que define la pirámide es la siguiente: void glfrustum (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far) Crea una matriz para una vista en perspectiva y la multiplica por la matriz actual. No requiere que el volumen de vista sea regular. Este volumen se define por los parámetros: (left, bottom, near) - (right, top, near) que definen las coordenadas (x, y, z) de las esquinas inferior-izquierda, y superiorderecha del plano de recorte de cerca, near y far definen las distancias desde el punto de vista del observador a los planos de recorte de cerca y lejos, como se puede ver en la figura anterior. Todos estos valores deben ser positivos. La pirámide tiene una orientación por defecto en el espacio tridimensional. Pero se puede trasladar y rotar la matriz de proyección para alterar esta situación, y hacer trucos con la imagen. En lugar de esto se puede utilizar la función auxiliar siguiente: void gluperspective (GLdouble fovy, GLdouble aspect, GLdouble znear, GLdouble zfar) Esta función crea una matriz de proyección que define una vista simétrica y la multiplica por la matriz actual. El argumento fovy determina el ángulo del campo de vista, en el plano X-Z y su rango es [0.0, 180.0]. El parámetro aspect es el cociente entre el ancho de la pirámide dividido por el alto de la base de la pirámide. Los valores znear y zfar son las distancias desde el punto de vista a los planos de recorte. Deben ser siempre positivos. Se debe dar un valor adecuado para el campo de vista, pues sino la imagen aparecerá distorsionada. Hay que hacer algunos cálculos entre el ancho de la pantalla, el ángulo de visión y la distancia del observador a la pantalla, como se ha visto en la parte de teoría. 3.5.- TRANSFORMACIONES DE VENTANA. La ventana es la región rectangular sobre la que se proyecta la imagen. Puede coincidir con la pantalla del ordenador completa o no. Estas transformaciones sirven para adaptar la escena al tamaño de la ventana. Las coordenadas de la ventana se miden, en función del número de pixeles, relativas en principio, a la esquina inferior izquierda de la misma. Sea cual sea el tamaño de la ventana final ya no se realizan más recortes, sino que se representan todos los puntos que están dentro del volumen de vista definido antes, y lo que puede variar es el tamaño de la representación.
Página: 29 3.5.1.- Definición de la Ventana. Dado que OpenGL no proporciona rutinas de gestión de ventanas es el propio gestor de ventanas (window manager) que se selecciona el encargado de abrir la ventana. En nuestro caso utilizaremos las librerías auxiliares que se encargan de eso. Una vez abierta la ventana tenemos que definir su marco, para lo que se utiliza la siguiente función. void glviewport (GLint x, GLint y, GLsizei width, GLsizei height) Define un rectángulo de pixeles dentro de una ventana en el cual se dibuja la imagen final. Los parámetros x, y definen la esquina inferior izquierda de la ventana. Los parámetros width, height definen el ancho y el alto, es decir el tamaño de la ventana. Por defecto la ventana ocupa toda la pantalla. El ratio, es decir la relación ancho/alto de la ventana debe ser igual a la del volumen de vista, pues sino la imagen se desvirtúa. 3.6.- MANEJAR LA PILA DE MATRICES. Las matrices que hemos estudiado en este tema son sólo la parte visible de una pila de matrices, una por cada tipo de operación, que OpenGL nos permite gestionar de forma eficiente. Este mecanismo nos ayuda a construir modelos complejos a partir de otros más sencillos, a posicionar los objetos definidos en un sistema de coordenadas local en una posición concreta de la pantalla, etc..., todo ello de forma rápida y eficiente. Con OpenGL se puede controlar qué matriz es la primera de la pila y de esta forma componer como queramos las transformaciones, haciendo y deshaciendo en cada momento la transformación. void glpushmatrix (void) Añade una matriz a la pila actual poniéndola en la cabeza de esa pila, desplazando las anteriores un puesto hacia abajo. La pila actual se define con la función glmatrixmode (). Si se añaden demasiadas matrices se produce un error. Copiando la matriz actual en la cabeza de la pila sirve para recordar donde estoy en este momento, y por lo tanto se puede hacer una transformación para colocar otro objeto en su posición y volver al punto actual sólo con recuperar la matriz. void glpopmatrix (void)
Página: 30 Descarta la primera matriz de la pila, dejando la segunda como cabeza de la misma. La matriz que se extrae de la pila se destruye. La matriz actual sobre la que se va a trabajar se activa con la función glmatrixmode (). Esta función sirve para deshacer un paso de transformación, es decir volver un paso atrás y situarme en el punto donde estaba antes de hacer la última transformación. 3.6.1.- Pila de Matrices ModelView. Esta pila almacena al menos 32 matrices 4x4 que corresponde a las 32 ultimas transformaciones de modelo y vista que se han realizado. Inicialmente la cabeza de la pila contiene la matriz identidad. 3.6.2.- Pila de Matrices de Proyección. Contiene una matriz en la que se almacenan las transformaciones de proyección que definen el volumen de vista. En cada ventana sólo existirá una matriz de proyección y por lo tanto esta pila sólo tendrá dos niveles. Así pues, antes de modificar la matriz de proyección se debe cargar la matriz identidad.