Contenido
Requisitos
Introducción
El Kit de Herramientas de Utilidad OpenGL (KHUGL) u OpenGL Utility Toolkit (GLUT) es una librería en C que proporciona funciones sencillas y útiles para el manejo de ventanas, en el sistema operativo, en las que luego se podrán ejecutar aplicaciones construidas con la librería de OpenGL.
GLUT es básicamente un creador de "hojas blancas" en las cuales se puede luego "dibujar" usando OpenGL. Se puede usar en Windows, Linux y MacOS pero lo que escribo solo contenpla su uso en Linux (con el compilador GCC).
En este escrito se van a usar las funciones de GLUT y, para nuevas funciones, la implementación "Libre" de GLUT (ya que la versión original era de código cerrado) llamada FreeGLUT. FreeGLUT es compatible con programas hechos para GLUT por lo que se puede usar directamente para todos los ejemplos aqui descritos. La página web oficial del proyecto de FreeGLUT se puede conseguir en las referencias. Dicha página proporciona el código fuente de FreeGLUT para ser compilado y da información sobre como instalar paquetes ya compilados de FreeGLUT para cada sistema operativo.
Configuración
Como se mencionó anteriormente, en esta página se considera el uso de FreeGLUT bajo Linux (basado en Debian) dada la simplicidad en la configuración. Para cualquier distribución de Linux basada en Debian lo más probable es que las librerías ya esten instaladas en tu sistema, si no, puedes instalarlas a través de APT con el siguiente comando:
sudo apt install libglut-dev freeglut3-dev
En caso de que lo anterior no funcione, tienes que investigar cuales son los paquetes a instalar o puedes compilar FreeGLUT tu mismo y usarlo para seguir este escrito. Para Windows se menciona que se puede usar Cygwin o MSYS para obtener/usar la librería, pero no estoy seguro de como funciona el asunto (puede que escriba luego acerca de ello).
Definiciones
GLUT posee varios objetos, estos objetos pueden ser creados, inicializados, editados y eliminados a través de funciones asociadas a ellos. El objeto principal de GLUT es la ventana y es el elemento principal con el cual se va a trabajar.
En un programa que usa GLUT se pueden crear multiples objetos de un mismo tipo, pero sólo uno de ellos estará disponible para ser editado a la vez. A este objeto se le denomina el objeto actual y representa símbolicamente el objeto actualmente seleccionado. La selección puede ocurrir implícitamente (cuando se crea dicho objeto por ejemplo) o explícitamente (a través de la ejecución manual de la función de selección asociada). Cuando un objeto está seleccionado, todas las funciones que se ejecuten relacionadas al tipo de objeto del objeto seleccionado van a operar sobre este objeto seleccionado y no sobre otros objetos. Solamente se actuará sobre otro objeto (del mismo tipo) cuando se seleccione, mediante la funcion de selección necesaria, al siguiente objeto con el que se desee trabajar a través de la referencia que se tenga del mismo.
El primer programa
Primero que todo, hay que hacer un primer programa en el que se encuentre el código mínimo necesario para saber si GLUT (o la implementación de GLUT de FreeGLUT) esta instalado correctamente.
En un archivo de C (test.c
por ejemplo) copia y pega el siguiente código:
#include <GL/glut.h> int main(void) { return 0; }
Luego, compila el programa con GCC usando el siguiente comando:
gcc test.c -lglut
NOTA: -lglut
le indica a GCC que enlace la librería GLUT para conseguir las implementaciones de las funciones de GLUT que uno esté usando en su código (-lglut
equivale a decir link GLUT)
Si el compilador no muestra errores/advertencias entonces la libreria de GLUT es accesible y se puede empezar a desarrolar programas con ella.
Ahora, el primer programa real de GLUT será el siguiente:
#include <GL/glut.h> void some_func(void) { return; } int main(int argc, char ** argv) { glutInit(&argc, argv); int window1 = glutCreateWindow(NULL); glutDisplayFunc(some_func); glutMainLoop(); return 0; }
Al compilar este programa y ejecutarlo (si todo sale bien) deberías ver algo como lo mostrado en la siguiente imagen.
Fig. 1: Ventana creada por GLUT con "lo que sea" de contenido.
Vamos a ir paso por paso indicado lo que se necesita saber de este programa:
- Todo lo que se muestra en este programa es lo mínimo necesario para crear la única ventana que se muestra en la imagen.
- Como no nos importa el contenido de la ventana, el mismo se compone de lo que sea que se encuentre en la región asociada de la pantalla al momento en que aparezca la ventana. Arrastré una ventana del explorador de archivos encima de la ventana de GLUT y, en mi caso, queda ese "efecto de barrido" que dejan los programas cuando se atascan/funcionan lento.
-
El código fuente del programa tiene varias funciones desconocidas:
-
void glutInit(int * argcp, char ** argv)
: función que se tiene que llamar siempre al inicio de un programa GLUT. Se encarga de inicializar dicha librería y necesita de los parámetros de la funciónmain
cuando se quieren pasar datos de entrada de la terminal. Estos parametros son pasados a la funcion para leer las posibles entradas por terminal que puedan modificar el funcionamiento inicial del programa GLUT. -
int glutCreateWindow(char * name)
: crea un objeto tipo ventana que tendrá de nombre en el borde superior la cadena de caracteres ASCII apuntada porname
. La función devuelve un número entero (que empieza en 1) que representa a la ventana (es la identificación de la ventana creada). Esta función implícitamente establece la ventana creada como la ventana actual. -
void glutDisplayFunc(void (* func)(void))
: esta función establece la función, para la ventana actual, que se va a llamar cuando GLUT considere que la ventana necesita ser redibujada/refrescada. Es obligatorio el uso de esta función despues de haber creado una ventana. En otras palabras, es una función que permite establecer la función de "actualización" de una ventana. -
void glutMainLoop(void)
: esta función se encarga de evitar que llegue el fin del programa de C. Básicamente es una función que se ejecuta eternamente hasta que el programa de GLUT finaliza (se cierra la ventana en el boton de cerrado usual de las ventana) y se encarga de ejecutar todas las funciones en segundo plano que se requieren para cambiar los estados de los objetos creados en GLUT. Tiene que aparecer como máximo 1 vez.
-
Con todo lo anterior dicho, falta tambien información acerca de ¿Donde van a empezar a salir las ventanas en la pantalla? y ¿Qué dimensiones iniciales tendrán las ventanas? Bueno, esas son condiciones iniciales que pueden establecerse antes de crear alguna ventana con glutCreateWindow
. Si las funciones no se llaman, GLUT pondrá el valor que le parezca. Adicionalmente, las funciones pueden llamarse más de una vez para "sobreescribir" estos valores iniciales.
-
void glutInitWindowPosition(int x, int y)
: establece la posición inicial de todas las ventanas creadas conglutCreateWindow
en pixeles. La posición(x, y)
es con respecto a la esquina superior izquierda de la pantalla. +X es hacia la derecha +Y es hacia abajo. -
void glutInitWindowSize(int width, int height)
: establece las dimensiones iniciales de todas las ventanas creadas conglutCreateWindow
en pixeles.
Fig. 2: ejemplos del uso de glutInitWindowPosition
y glutInitWindowSize
.
En la implementación de FreeGLUT se mencionan ciertas diferencias respecto a GLUT en relación a la interpretación de estos valores. Lo que sucede se puede explicar a través de la siguiente imagen y es explicado en este enlace.
Fig. 3: Ventana real en la pantalla.
En FreeGLUT:
- Cuando se crea una ventana con coordenadas
(x, y)
y tamaño(ancho, alto)
las coordenadas(x, y)
corresponden a las coordenadas de la ventana real (punto azul) y las dimensiones(ancho, alto)
corresponden a las dimensiones del área de dibujo (que se localiza en el punto rojo). - Cuando se pide la posición
(x, y)
y dimensión(ancho, alto)
de una ventana (a través de la funciónglutGet
cuando ya se han creado las ventanas, a explicar luego), la posición y dimensiones devueltas por FreeGLUT son únicamente del área de dibujo.
Con eso claro, vamos a ver a la función glutGet
de forma superficial.
-
int glutGet(GLenum state)
:state
es un valor de enumeración que puede representar distintas propiedades de los objetos de GLUT, al pasarselo a la función, esta devuelve el número entero que representa a esta variable. Algunos posibles valores a pasar aglutGet
son los siguientes:GLUT_WINDOW_X
: posición X de la ventana en pixeles.GLUT_WINDOW_Y
: posición Y de la ventana en pixeles.GLUT_WINDOW_WIDTH
: ancho de la ventana en pixeles.GLUT_WINDOW_HEIGHT
: alto de la ventana en pixeles.
El resto de los posibles valores de enumeración que pueden pasarse a la función se muestran aquí (GLUT) y aquí (FreeGLUT).
Fig. 4: Ejemplo del uso de glutGet
para obtener la posición y tamaño de una ventana.
El área de dibujo
La computación gráfica es un área cuya naturaleza es la de llevar a cabo muchas operaciones ocurriendo en paralelo, ya que paa generar una imagen hay que considerar muchas variables (color, iluminación, perspectiva, etc.). Al final, todas las operaciones devuelven sus resultados, y, al apilarlos unos sobre otros, se genera la imagen final que se muestra en la pantalla del computador. Existe entonces la necesidad de guardar la información de cada proceso en algun sitio y ese sitio, en general, es un buffer.
En OpenGL existen varios tipos de bufferes y desde FreeGLUT uno puede decidir que tipo de buffer habilitar en una ventana. Esta información se especifica a través de la función glutInitDisplayMode
.
void glutInitDisplayMode(unsigned int mode)
:
Los bufferes que usará serán o GLUT_DOUBLE o GLUT_SINGLE
Como se dijo en la sección anterior, el área de dibujo es donde se reflejan visualmente los comandos de OpenGL. En este escrito no voy a tocar el como dibujar en OpenGL (principalmente porque no sé todavia como usarlo >:]) pero para poder continuar con buenos ejemplos en GLUT necesito usar 4 funciones muy elementales que son parte de la librería de OpenGL 1.1:
- glClearColor()
- glClear()
- glFlush()
- glFinish()
La ventana
La ventana, como se dijo, es el objeto principal de GLUT.