Hogar Indice Busqueda Enlaces Sobre Nosotros
[LinuxFocus Image]
[Navegation Bar]
  Noticias   Archivos   Compañias   Consejos y Trucos  

Programando GLUT: Ventanas y Animaciones

por Miguel Angel Sepúlveda


Introducción

Inicializationes

Procesado de Eventos

Ejemplo de Animación



GLUT Homepage
GLUT Author
GLUT-3.6 sources

Introducción

Por razones de diseño, la especificación de OpenGL se aisló de cualquier dependencia de un sistema de ventanas concreto. El interface resultante es una librería de trazado en 2D y 3D portable y eficiente. Es trabajo del sistema de ventanas nativo el abrir y trazar ventanas. La librería de OpenGL se comunica con el sistema nativo a través de librerías adicionales auxiliares. Por ejemplo, la librería auxiliar GLX describe la interacción entre OpenGL y el sistema X Window System.

El kit de utilidades de OpenGL (OpenGL Utility Toolkit - GLUT) es un interface de programación con bindings para ANSI C y FORTRAN para escribir programas OpenGL independientes del sistema de ventanas. Lo ha escrito Mark J. Kilgard y cubre un gran agujero dejado por la especificación de OpenGL. Gracias a GLUT, los desarrolladores pueden usar un inteface común para el sistema de ventanas independientemente de la plataforma empleada. Las aplicaciones de OpenGL que usan GLUT se pueden portar fácilmente entre plataformas sin tener que introducir muchos cambios en el código fuente. En fin, GLUT simplifica la producción de código OpenGL y complementa la librería de OpenGL.

GLUT es relativamente pequeño y fácil de aprender. Está bien diseñado y su autor ha escrito una buena documentación. Por eso, parece redundante empezar una serie de artículos aquí en LinuxFocus. Recomendamos a cualquier desarrollador serio que lea la documentación de Mark. Nuestro propósito escribiendo estas columnas de GLUT es introducir la librería GLUT y su uso paso por paso con ejemplos, como complemento a la lectura de la serie de OpenGL de este magazine. Esperamos hacer una contribución útil y como mínimo motivar a alguno de vosostros a subiros al vagón de Linux y OpenGL. De cualquier modo, obtener vuestra copia de la documentación de Mark como referencia.

La API de GLUT es una máquina de estados, como OpenGL. Esto significa que GLUT tiene una serie de variables de estado que duran toda la ejecución de la aplicación. El estado inicial de la máquina de GLUT se ha elegido razonablemente para ajustarse a la mayor parte de aplicaciones. El programa puede modificar los valores de las varibles de estado para ajustarlas a su gusto. Cuando se llama a una función de GLUT, su acción se modifica de acuerdo a los valores de las variables de estado. Las funciones de GLUT son simples, tienen pocos parámetros. Nunca devuelven punteros, y los únicos punteros pasados a las funciones de GLUT son punteros a cadenas de caracteres y manejadores de fuentes.

Las funciones de GLUT se pueden clasificar en varias subAPIs según su funcionalidad:

  • Inicialización
  • Inicio del procesado de eventos
  • Control de ventanas
  • Control de overlay
  • Control de menús
  • Registro de funciones Callback
  • Control del mapa de colores
  • Obtención del estado
  • Trazado de fuentes
  • Trazado de formas geométricas

Es este artículo exploraremos algunas de las funciones de inicialización, procesado de eventos y control de ventanas necesarias para empezar un sencillo programa en OpenGL.

Initializations

Todo programa de OpenGL que utilice GLUT debe empezar inicializando el estado de la máquina de estados de GLUT. Las funciones de inicialización de GLUT tienen el prefijo glutInit-. La rutina principal de inicialización es glutInit:

Uso:
  glutInit(int **argcp, char **argv);
  argcp
es un puntero a la variable argc de la función main (sin modificar). Al acabar la función, el valor apuntado por argcp se actualiza, ya que glutInit extrae todas las opciones de la línea de comandos relevantes para la librería GLUT. Por ejemplo: bajo X Window System toda opción relevante para la ventana X asociada a la ventana GLUT..
  argv es la variable argv de la función main (sin modificar).

glutInit se encarga de modificar las variables de estado de GLUT y negociar una sesión con el sistema de ventanas. Hay muy pocas funciones que pueden aparecer antes de glutInit; solo aquellas precedidas por glutInit-. Estas rutinas se pueden usar para poner los estados de inicialización por defecto, por ejemplo:

Uso:
  glutInitWindowPosition(int x, int **y);
  glutInitWindowSize(int width, int **height);
  x , y
posición en la pantalla en píxels de la ventana (esquina superior izquierda)
  width, height ancho y alto en píxels de la ventana.

Existe otra rutina de inicialización ominpresente en toda aplicación en OpenGL, glutInitDisplayMode():

Uso:
  glutInitDisplayMode(unsigned int mode);
  mode
es el modo de display, un OR de los posibles valores de display, que son:
GLUT_RGBA Selecciona una ventana en modo RGBA. Es el valor por defecto si no se indican ni GLUT_RGBA ni GLUT_INDEX.
GLUT_RGB Lo mismo que GLUT_RGBA.
GLUT_INDEX Seleciona una ventana en modo de índice de colores. Se impone sobre GLUT_RGBA.
GLUT_SINGLE Selecciona una ventana en modo buffer simple. Es el valor por defecto.
GLUT_DOUBLE Selecciona una ventana en modo buffer doble. Se impone sobre GLUT_SINGLE.
GLUT_ACCUM Selecciona una ventana con un buffer acumulativo.
GLUT_ALPHA Selecciona una ventana con una componente alpha del buffer de color.
GLUT_DEPTH Selecciona una ventana con un buffer de profundidad.
GLUT_STENCIL Selecciona una ventana con un buffer de estarcido.
GLUT_MULTISAMPLE Selecciona una ventana con soporte multimuestra.
GLUT_STEREO Selecciona una ventana estéreo.
GLUT_LUMINANCE Selecciona una ventana con un modelo de color de "luminancia".

Si alguna de estas propiedades no te es familiar, no te preocupes, más tarde o más pronto escribiremos sobre ellas. Veamos un par de ejemplos, primero una inicialización simple para una aplicación de trazado en un paso:

#include <GL/glut.h>

void main(int argcp, char **argv){

/* Poner el tamaño y posición de la ventana */
glutInitWindowSize(640, 480);
glutInitWindowPosition(0, 0);

/* Seleccionar el tipo de modo de display:
   Buffer simple y color RGBA */
glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE);

/* Inicializar el estado de GLUT */
glutInit(&argcp, argv);

.....más código


};

Ahora un ejemplo de un programa de animación:

#include <GL/glut.h>

void main(int argcp, char **argv){

/* Poner el tamaño y posición de la ventana */
glutInitWindowSize(640, 480);
glutInitWindowPosition(0, 0);

/* Seleccionar el tipo de modo de display:
   Buffer doble y color RGBA */
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);

/* Inicializar el estado de GLUT */
glutInit(&argcp, argv);

.....más código


};

Volveremos a estos dos ejemplos a medida que aprendamos más sobre GLUT. La principal diferencia es que en el segundo caso el display se inicializa en modo de doble buffer, ideal para animaciones porque elimina el parpadeo cuando cambia la imagen en la secuencia de animación.

Procesado de Eventos

Como hemos dicho antes, GLUT es una máquina de estados. Ahora veremos que también está diseñada como un motor dirigido por eventos. Esto significa que hay un "timer" o bucle continuo que comienza después de la inicialización correspondiente y que procesa uno por uno todos los eventos declarados a GLUT durante la inicialización. Los eventos son: un botón del ratón que se ha pulsado, una ventana que se cierra, una ventana que se redimensiona, un cursor que se mueve, unas teclas del teclado que se han pulsado, un curioso evento "idle", esto es, no pasa nada. Cada uno de los posibles eventos se debe registrar en una de las variables de estado de GLUT para que el "timer" o bucle de proceso de eventos de GLUT mire periódicamente si este evento ha sido activado por el usuario.

Por ejemplo, podemos registrar "pulsar botón del ratón" como un evento para GLUT. Los eventos se registran mediante rutinas de registro callback. Todas tienen la sintaxis glut[algunEvento]Func, en el caso del click del ratón será glutMouseFunc. Un registro de callback le dice a la máquina de GLUT que función definida por el usuario se debe llamar si el correspondiente evento es activado. Así pues, si escribo mi rutina MyMouse que especifica qué hacer cuando se pulsa el botón izquierdo del ratón, o el botón derecho, etc.. entonces registraré mi función callback después de glutInit() en main() usando la sentencia "glutMouseFunc(MyMouse);" .

Dejemos para más tarde qué funciones callback y qué eventos están permitidos en GLUT. Lo importante ahora es señalar que después de registrar todos los eventos importantes de nuestra aplicación debemos invocar la rutina de procesado de eventos de GLUT, que es glutMainLoop(). Esta función nunca vuelve, nuestro programa básicamente comienza un bucle infinito. Irá llamando, cuando sea necesario, las funciones callback que hayan sido previamente registradas. Toda función main() de una aplicación OpenGL debe pues terminar en una sentencia glutMainLoop(). Así pues, en el caso de nuestra plantilla de una animación:

#include <GL/glut.h>

void main(int argcp, char **argv){

/* Inicializar el estado de GLUT */
glutInit(&argcp, argv);
glutInitWindowSize(640, 480);
glutInitWindowPosition(0, 0);

/* Abrir una ventana */
glutCreateWindow("My OpenGL Application");

/* Seleccionar el tipo de modo de display:
   Buffer doble y color RGBA */
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);


/* Registrar funciones Callback */
.....

/* Iniciar el procesado de eventos */
glutMainLoop();
};

Notad que he añadido un código que no he mencionado antes. Es una de las funciones de control de ventanas de GLUT, glutCreateWindow(char **name). Esto es lo que me gusta de la filosofía de diseño de OpenGL y GLUT, está muy claro lo que hace la rutina solo mirando el nombre!. Sí, se encarga de pasar la orden al sistema de ventanas subyacente de abrir una ventana para nuestra aplicación de OpenGL. La ventana tendrá el nombre "name", pasado como una cadena de caracteres. En el entorno de X Window este nombre se escribe en la esquina superior izquierda de la ventana. La sección de control de ventanas de GLUT tiene muchas otras funciones que iremos estudiando poco a poco. Por hoy, esta es suficiente. También he reordenado las funciones de inicialización para mostrar que se pueden poner después de glutInit()

Volvamos a los eventos... En el presente artículo quiero introducir dos funciones de registro de callback que son fundamentales en cualquier programa de animación. La función glutDisplayFunc, que registra la función de display para la ventana actual y la función glutIdleFunc, que registra la función "idle". Ambas rutinas de registro esperan una función del tipo void *(void). Supongamos que escribimos dos funciones callback adicionales en nuestra plantilla de animación, void MyDisplay(void) que se encarga de invocar las instrucciones OpenGL que dibujan nuestra escena en la ventana, y void MyIdle(void) que es una función que es llamada cuando no hay entradas del usuario, esto es, cada vez que el procesador de eventos de GLUT da una vuelta al bucle infinito (glutMainLoop()) y no encuentra ningún nuevo evento activado, procesa MyIdle. ¿Por qué necesito registrar una función callback "idle" en un programa de animación? Porque si quiero modificar cada una de las imágenes mostradas durante la animación independientemente de la entrada del usuario, debe existir una función (la función callback "idle") que se llame muy a menudo durante la ejecución del programa y cambie las imágenes antes de que se dibujen en pantalla por Mydisplay().

Animation Example

Finalmente aquí tenemos una simple plantilla para un programa de animación:

#include <GL/glut.h>

void MyIdle(void){
/* Código para modificar las variables que definen la próxima imagen */
....
};

void MyDisplay(void){
/* Código OpenGL que dibuja una imagen */
....
/* Después de dibujar la imagen, intercambiar los buffers */
glutSwapBuffers();
};

void main(int argcp, char **argv){

/* Inicializar el estado de GLUT */
glutInit(&argcp, argv);
glutInitWindowSize(640, 480);
glutInitWindowPosition(0, 0);

/* Abrir una ventana */
glutCreateWindow("My OpenGL Application");

/* Seleccionar el tipo de modo de display:
   Buffer doble y color RGBA */
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);


/* Registrar funciones Callback */
glutDisplayFunc(MyDisplay)
glutIdleFunc(MyIdle)

/* Iniciar el procesado de eventos */
glutMainLoop();
};

Nota que al final de MyDisplay he añadido una nueva rutina GLUT, glutSwapBuffers(). Es muy útil en animaciones. Estamos usando una ventana en modo de DOBLE buffer, uno que se muestra y otro oculto. Las instrucciones de dibujo de OpenGL en este caso siempre trazan en el buffer oculto. La llamada a glutSwapBuffers intercambia los buffers, mostrando en la ventana de golpe todo lo que hemos dibujado. Esta técnica es común en animaciones por ordenador porque previene que el ojo humano vea como se construye la imagen línea por línea.

Ya tenemos suficiente material para empezar a escribir aplicaciones OpenGL, lo único que falta son las instrucciones OpenGL en MyDisplay que realizan el dibujo... pero esto es otra historia ;-).

En el próximo artículo de programación en GLUT exploraremos más profundamente las funcionalidades disponibles en la sección de control de ventanas de GLUT como abrir multiples escenas dentro de la misma ventana. También aprenderemos cómo usar menús y sus pros y contras en cuanto a portabilidad.


Traducido por Hugo Lastras

Para más información:
© 1998 Miguel Angel Sepúlveda
Esta página está mantenida por Miguel A Sepulveda.