Programación en OpenGL y Glut

Principal | Gráficos 3D | Gráficos 2D | Fractales | Math | Códigos | Tutoriales | Links

Programando en OpenGL y GLUT

Este primer programa de ejemplo dibuja una "Tetera", una fuente de luz puntual, se define ademas un material el que se aplica a la "Tetera". Utilizo GLUT (OpenGL Utility Toolkit) que es una interface de programación para C y FORTRAN para escribir programas OpenGL independientes del sistema de ventanas.

El código esta compuesto por las siguientes partes :

#includes

void main(void)

void init(void)

void reshape(int w, int h)

void display(void)

void keyboard()

Código Fuente

includes

En la primer parte del código estan los #includes que indican al compilador que debe incluir otros archivos fuentes.

//Incluimos las librerias
#include <GL/glut.h>
#include <stdlib.h>

main()

Cualquier programa en C se caracteriza por su función main o principal, función que se llama automáticamente al iniciar la ejecución del programa. Para empezar definimos main como int ya que debe retornar un entero :

int main (int argc, char **argv);

La llamada que sigue es a GLUT, esta funcion me inicializa OpenGL.

glutInit (&argc, argv);

Ahora hay que decirle a OpenGL como queremos "Dibujar", le indicamos que vamos a usar un buffer unico con la constante GLUT_SINGLE y ademas le tenemos que decir comos se van a aplicar los colores, los cuales van a estar definidos por tres valores numericos, uno para el Rojo, otro para el Verde y otro para el azul, para esto usamos la constante GLUT_RGB.

glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);

GLUT nos permite definir las dimensiones de nuestra ventana de visualización.

glutInitWindowSize (300, 300);

También hay que "colocar" la ventana en algún punto determinado de la pantalla.

glutInitWindowPosition (0, 0);

Una vez ya definido como "Dibujar", con que medidas de ventana y en que posición física de la pantalla creamos la ventana, le damos un nombre cualquiera que sera el título que aparecerá en esta.

glutCreateWindow ("Teapot");

Creamos una función llamada Init() que se encargará de la Inicialización de las variables de OpenGL.

init();

Tras inicializar el sistema le decimos a OpenGL cual es la función que debe llamarse cada vez que se requiera dibujar en la pantalla. Esta función la tendremos que crear nosotros por supuesto y será dónde le diremos a OpenGL que es lo que debe dibujarse en la ventana que hemos creado para tal fin.

glutDisplayFunc(display);

Esta funcion no retorna ningun valor, y tampoco recibe argumento alguno.

void display(void)
{
.................................
//aqui va el codigo de la funcion
.................................
}


En caso de que se modifique el tamaño de nuestra ventana, lo indicamos en la funcion que llamaremos reshape().

glutReshapeFunc(reshape);

esta funcion la definimos de la siguiente manera

void reshape(int w, int h )
{
................................
//código que deseemos se ejecute
................................
}


Los parámetros que nos llegan a la función se refieren al nuevo ANCHO (w) y al nuevo ALTO (h) de la ventana tras ser redimensionada por el usuario. En esta función deberemos asegurarnos de que la imagen no se distorsione.

El control del teclado se realiza mediante

glutKeyboardFunc(keyboard);

esto lo añadimos a nuestra función de main y entonces definimos a parte la función de control propiamente dicha para el teclado :

void keyboard( unsigned char key, int x, int y )
{
................................
//código que deseemos se ejecute
................................
}


Bueno ya sabemos dónde dibujar, en la ventana, y qué dibujar. Sólo falta entrar en el bucle infinito que domina cualquier aplicación OpenGL. Con la función que sigue, que siempre se pone al final del main, le decimos a la librería que espere eternamente a que se produzcan "eventos".

glutMainLoop ();

Por ultimo se requiere que la funcion main retorne un entero aunque no sirva literalmente para nada :

return 0;

Init()

Bueno todo programador que se precie de no hacerlo muy mal sabrá que hay que ahorrarse el máximo de sentencias en el propio main e incluirlas en una rutina de inicialización que se llama desde éste, para eso esta Init() como que no se le pasa ningún parámetro ni retorna ningún valor la declaramos tal como :

void inicio (void)

En primer lugar le indico la ubicacion de la fuente de luz, en este caso se trata de una fuente de luz puntual situada en el infinito, que pasa por el origen de coordenadas y por el punto (1.0, 1.0, 1.0) :

GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };

si hubiera sido :

GLfloat light_position[] = { 1.0, 1.0, 1.0, 1.0 };

la fuente de luz estaria en el en la posicion (1.0, 1.0, 1.0).

Activamos todo el calculo de ilumincaion :

glEnable(GL_LIGHTING);

Activamos la fuente de luz :

glEnable(GL_LIGHT0);

Elijo el modo de comparacion para el test del Depth Buffe.

glDepthFunc(GL_LESS);

Activo el Depth Buffer, es usado generalmente para la eliminacion de superficies ocultas.

glEnable(GL_DEPTH_TEST);

Aqui le decimos que dibuje sólo las caras frontales GL_FRONT y rellenadas, es decir con color en su interior además de en los bordes GL_FILL. Para dibujar sólo las caras traseras utilizaríamos GL_BACK y para dibujar ambas GL_FRONT_AND_BACK. En el caso del color de relleno, si deseamos evitarlo coloreando solo los bordes de cada polígono usaremos la constante GL_LINE :

glPolygonMode (GL_FRONT, GL_FILL);

Eso es todo lo que tengo en mi rutina de inicializacion por ahora.

display()

Defino las caracteristicas del material que voy a usar :

GLfloat mat_ambient[] = { 0.7f, 0.7f, 0.7f, 1.0f };
GLfloat mat_diffuse[] = { 0.1f, 0.5f, 0.8f, 1.0f };
GLfloat mat_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
GLfloat mat_shininess[] = { 100.0f };

La función glClear nos permite borrar tanto buffer de color, como el de profundidad.

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

Selecciono la matriz de modelado :

glMatrixMode (GL_MODELVIEW);

y la "limpio" con la matriz identidad :

glLoadIdentity ();

Roto el objeto 25 grados en torno al eje x.

glRotated(25.0, 1.0, 0.0, 0.0);

Lo vuelvo a rotar pero esta ves -30 grados en torno al eje y.

glRotated(-30.0, 0.0, 1.0, 0.0);

Aplico el material al objeto

glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
Dibujo una "Tetera" de dimensiones 125.0, este objeto esta definido en GLUT.

glutSolidTeapot(125.0);

Fuerzo a dibujar el objeto.

glFlush();

reshape()

Definimos primero un viewport es un área rectangular de la ventana de visualización. Por defecto es la ventana entera pero podemos variarlo a gusto. Se utiliza la funció :

void glViewport(GLint x, GLint y, GLsizei w, GLsizei h)

dónde (x,y) es la esquina inferior izquierda del rectangulo o viewport. Esta coordenada debe especificarse con relación a la esquina inferior izquierda de la ventana. Claro está que w, h son la anchura y altura de nuestro viewport dentro de la ventana. Todos los valores son enteros ya que se tratan de pixels.

glViewport(0, 0, (GLsizei) w, (GLsizei) h);

En primer lugar tenemos decirle a OpenGL como debe proyectar nuestros gráficos en pantalla. Por ello y para empezar le decimos que active la matriz de proyección :

glMatrixMode (GL_PROJECTION);

Procedemos a "limpiarla" para que no contenga ningún valor que pueda falsear los cálculos y por tanto nos haga obtener resultados inexactos. Para ello cargamos a la matriz activa (proyeccion) con la matriz identidad :

glLoadIdentity ();

Tengo la matriz de proyección limpia, pero ahora tengo que decirle a OpenGL de que manera quiero que proyecte mis gráficos en la ventana que he creado. Usaremos la función glOrtho(...). Así creo el volumen de visualización. Todo lo que esté dentro de este volumen será proyectado de la forma más simple, eliminando su coordenada Z, o de profundidad. Es la llamada proyección ortográfica y no permite que se distinga la distancia de los objetos a la cámara :

glOrtho (-200, 200, -200, 200, -200, 200);

glOrtho()
Se utiliza para especificar una proyección ortográfica. Este tipo de proyección define un volumen de vista rectangular, concretamente define un paralelepípedo de tamaño infinito, este hecho nos lleva a definir una serie de planos de corte para detallar con exactitud el volumen de vista. OpenGL define la sentencia como:

glOrtho(xmin, xmax, ymin, ymax, cerca, lejos);

Estos seis argumentos definen la ventana de visualización y los planos de corte tanto cercano como lejano. Para definir la ventana de visualización es suficiente definir las coordenadas de dos esquinas de la ventana, con estos dos valores queda totalmente definida. Los valores de cerca y lejos representan el plano cercano y el plano lejano. Hay que tener en cuenta que el objeto a visualizar debe encontrarse dentro de ambos planos, si sobrepasan estos dos planos el objeto se recortará automáticamente.

Ya hemos terminado con la matriz de proyección. El sistema ya sabe como debe proyectar en pantalla. Ahora pasemos a la matriz de modelado/visionado, o sea, la matriz que rota/escala/traslada....Dado que queremos operar sobre ella la seleccionamos con :

glMatrixMode (GL_MODELVIEW);

Procedemos a "limpiarla" con la matriz identidad.

glLoadIdentity();

keyboard()

Esta funcion termina la aplicacion si es presionada la tecla Esc


void keyboard(unsigned char key, int x, int y)
{
 switch (key) 
   {
   case 27: exit(0);
             break;
   }
}

Código Fuente del ejemplo

Codigo fuente (ejemplo1.zip 4kb)

/* Este programa utiliza proyeccion ortografica para
 * visualizar una "Tetera", este objeto esta definido
 * en GLUT, se crea una fuente de luz, y un material */

//Incluimos las librerias
#include <GL/glut.h>
#include <stdlib.h>

void init(void)
{
 // Ubicamos la fuente de luz en el punto (1.0, 1.0, 1.0)
 GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };

 // Activamos la fuente de luz
 glEnable(GL_LIGHTING);
 glEnable(GL_LIGHT0);
 glDepthFunc(GL_LESS);
 glEnable(GL_DEPTH_TEST);
 // Queremos que se dibujen las caras frontales
 // y con un color solido de relleno. 
 glPolygonMode(GL_FRONT, GL_FILL); 
}

void reshape(int w, int h)
{
 if (!h)
	return;
 glViewport(0, 0,  (GLsizei) w, (GLsizei) h);
 // Activamos la matriz de proyeccion.
 glMatrixMode(GL_PROJECTION);
 // "limpiamos" esta con la matriz identidad.
 glLoadIdentity();
 // Usamos proyeccion ortogonal
  glOrtho(-200, 200, -200, 200, -200, 200);
 // Activamos la matriz de modelado/visionado. 
 glMatrixMode(GL_MODELVIEW);
 // "Limpiamos" la matriz
 glLoadIdentity();
}

// Aqui ponemos lo que queremos dibujar.
void display(void)
{ 
 // Propiedades del material
 GLfloat mat_ambient[] = { 0.7f, 0.7f, 0.7f, 1.0f };
 GLfloat mat_diffuse[] = { 0.1f, 0.5f, 0.8f, 1.0f };
 GLfloat mat_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
 GLfloat mat_shininess[] = { 100.0f };

 // "Limpiamos" el frame buffer con el color de "Clear", en este 
 // caso negro. 
 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

 glMatrixMode( GL_MODELVIEW_MATRIX );
 glLoadIdentity();
 
 // Rotacion de 30 grados en torno al eje x
 glRotated(25.0, 1.0, 0.0, 0.0);
 // Rotacion de -30 grados en torno al eje y
 glRotated(-30.0, 0.0, 1.0, 0.0);
 // Dibujamos una "Tetera" y le aplico el material

 glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
 glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
 glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
 glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
 glutSolidTeapot(125.0);

 glFlush();
} 

// Termina la ejecucion del programa cuando se presiona ESC
void keyboard(unsigned char key, int x, int y)
{
 switch (key) 
   {
   case 27: exit(0);
             break;
   }
}    

// Main del programa.
int main(int argc, char **argv)
{ 
 // Inicializo OpenGL
 glutInit(&argc, argv);
 
 // Activamos buffer simple y colores del tipo RGB  
 glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);

 // Definimos una ventana de medidas 300 x 300 como ventana 
 // de visualizacion en pixels
 glutInitWindowSize (300, 300);
 
 // Posicionamos la ventana en la esquina superior izquierda de 
 // la pantalla.
 glutInitWindowPosition (0, 0);

 // Creamos literalmente la ventana y le adjudicamos el nombre que se
 // observara en su barra de titulo.
 glutCreateWindow ("Teapot");

 // Inicializamos el sistema 
 init();

 glutDisplayFunc(display); 
 glutReshapeFunc(reshape);
 glutKeyboardFunc(keyboard);
 glutMainLoop();
 
 // ANSI C requiere que main retorne un valor entero.
 return 0;
}


valcoey@hotmail.com

Ramiro Alcocer, 2001

Principal | Gráficos 3D | Gráficos 2D | Fractales | Math | Códigos | Tutoriales | Links

1