Descargar el PDF
Descargar el PDF
OpenGL es una herramienta de programación en tres dimensiones muy versátil que sirve para dibujar escenas complejas en 3D por medio de rutinas sencillas. El presente artículo te enseñará a dibujar un cubo que podrás girar para verlo ¡en tres dimensiones!
Para este proyecto necesitarás un editor de código y un poco de conocimientos sobre programación con el lenguaje C.
Pasos
1) Instalar OpenGL
- Para empezar, sigue estos pasos para instalar OpenGL en tu sistema. Si ya lo tienes además del compilador C, podrás omitir este paso e ir al siguiente.
2) Crear el documento
- Crea un archivo nuevo en tu editor de código favorito y guárdalo como: micubo.c
3) Librerías (#includes)
- Estas son las librerías básicas que necesitarás para tu programa. Será importante tener presente que dependiendo del sistema operativo podría haber otras librerías necesarias. Asegúrate de añadir todas estas para que tu programa sea versátil y pueda ejecutarse por cualquier usuario.
// Includes #include <stdio.h> #include <stdarg.h> #include <math.h> #define GL_GLEXT_PROTOTYPES #ifdef __APPLE__ #include <GLUT/glut.h> #else #include <GL/glut.h> #endif
4) Prototipos de función y variables globales
- El siguiente paso será declarar algunos prototipos de función.
// Prototipos de función void display (); void specialKeys (); // Variables globales double rotate_y = 0 ; double rotate_x = 0 ;
- Se explicarán detalladamente cada una de estas funciones y las variables globales a medida que vayas implementándolas después según el presente tutorial. Por ahora, lo importante será declararlos.
5) Configurar la función main()
-
int main ( int argc , char * argv []){ // Inicializar los parámetros GLUT y de usuario proceso glutInit ( & argc , argv ); // Solicitar ventana con color real y doble buffer con Z-buffer glutInitDisplayMode ( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
- Esta declaración establecerá el ambiente. No deberás olvidar que cuando programas en OpenGL, deberás preguntar por todo. Esto implica que deberás conocer muy bien cómo es que el programa trabaja y qué necesitas declarar para hacerlo funcional. En este punto ajustarás la visualización con un doble buffer, el color RGB y el Z-buffer.
- El doble buffer
es una técnica usada en los programas gráficos para eliminar el problema que surge debido a las imágenes dibujadas en la pantalla. Cada vez que dibujes, la pantalla deberá borrarse y luego dibujar con la nueva información. Sin el doble buffer, verás el efecto parpadeante en donde la pantalla se borra y se dibuja a la vez
Este problema se resuelve al usar un segundo buffer para dibujar. Con este método, se dibuja una imagen en el primer buffer y se muestra. El próximo cuadro se dibujará en el segundo buffer y cuando esté completo, los dos buffers se intercambiarán. Inmediatamente verás el segundo buffer, pero oculto, el primer buffer se borrará y se volverá a dibujar con un tercer cuadro y será intercambiado por el segundo buffer cuando esté completo. - También tendrás que habilitar el sistema color RGB'
en la ventana. Más adelante explicaremos cómo usar el color en OpenGL cuando trabajes con la función de visualización.
- Con el Z-buffer
es como se accede al efecto 3D que quieres lograr. El OpenGL usa un sistema de coordenadas tridimensional con ejes en X, Y, Z. La variable Z es la que hace el efecto zoom, acercando o alejando el objeto. Hablaremos un poco más de ello cuando dibujes los vértices en el cubo.
6) Crear la ventana
- El siguiente paso será crear la ventana
dentro de la cual dibujaremos el cubo. En el presente tutorial, la llamaremos: “Cubo asombroso”.
// Create window glutCreateWindow ( "Cubo asombroso" );
7) Habilitar la prueba de profundidad
- El OpenGL es un lenguaje estricto por el hecho de que no asume nada. Para que tu programa se muestre en 3D con el Z-buffer que vimos anteriormente, necesitarás habilitar la prueba de profundidad
. A medida que sigas explorando OpenGL, descubrirás muchas características que necesitarás habilitar, tales como iluminación, textura, eliminación selectiva de caras y muchas más.
// Habilitar la prueba de profundidad de Z-buffer glEnable ( GL_DEPTH_TEST );
8) Funciones de retrollamadas
- Aquí estarán las funciones de retrollamadas que programaste para los prototipos anteriormente. Cada vez que ejecutes el programa tendrás que llamar estas funciones. La función “display” (visualización) volverá a dibujar el cubo basado en cualquier cambio hecho a las variables desde su llamada previa. La función specialKeys
te permitirá interactuar con el usuario.
// Funciones de retrollamadas glutDisplayFunc ( display ); glutSpecialFunc ( specialKeys );
9) Funciones de retrollamadas
- El paso final de la configuración inicial será hacer el lazo principal
. Esta volverá a llamar la función principal hasta que cierres el programa para permitir las animaciones y la interacción con el usuario.
// Pasar el control de eventos a GLUT glutMainLoop (); // Regresar al sistema operativo return 0 ; }
- Todo el trabajo para dibujar el cubo se hará con esta función. La idea principal será dibujar los 6 lados y acomodarlos en la posición adecuada.
- Conceptualmente, tendrás que definir las 4 esquinas de un lado y dejar que OpenGL conecte los puntos con líneas y las coloree como las hayas definido. A continuación te presentaremos los pasos para hacerlo:
1) Función glClear()
- El primer paso que deberás tomar en esta función será limpiar el color y el Z-buffer
, de lo contrario, los dibujos antiguos todavía serán visibles debajo de los dibujos nuevos y los objetos dibujados no estarán en la posición correcta de la pantalla.
void display (){ // Borrar la pantalla y el Z-buffer glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
2) Funciones glBegin() y glEnd()
- OpenGL define los objetos como combinaciones de diferentes polígonos. Con el comando glBegin()
, podrás dibujar cualquier forma, como si tuvieras un lápiz. Para levantar el lápiz y dibujar una forma nueva, necesitarás usar el comando glEnd()
. Para el presente tutorial tendrás que usar GL_POLYGON para dibujar cada lado del cubo, sin embargo, podrías usar otros parámetros como GL_LINE, GL_QUAD o GL_TRIANGLE para crear otras formas.
- Empezaremos con el lado frontal del cubo. Después añadiremos color a todos los 6 lados.
// LADO FRONTAL: lado multicolor glBegin ( GL_POLYGON ); // Los vértices se incluirán en el siguiente paso. glEnd ();
3) Función glVertex3f()
- Cuando hayas decidido iniciar un polígono, tendrás que definir los vértices
. La función glVertex tiene múltiples formas para hacerlo dependiendo de qué deseas hacer con el objeto.
- Lo primero será definir con cuántas dimensiones trabajarás. El “3” anteriormente mencionado en glVertex3f indica que trabajarás en tres dimensiones, pero será posible trabajar con 2 o 4 dimensiones. La letra “f” significa que serán números reales, números de punto flotante, aunque también podrás trabajar con números cortos, enteros o dobles.
- Ten presente que estos puntos se definen en contra de las manecillas del reloj
. Ahora es irrelevante, pero será importante cuando trabajes con luminosidad, texturas y la eliminación selectiva de caras. Ello será sumamente importante, así que acostúmbrate a definir los puntos en contra de las manecillas del reloj ahora.
- Agrega los vértices entre las líneas glBegin() y glEnd().
// LADO FRONTAL: lado multicolor glBegin ( GL_POLYGON ); glVertex3f ( - 0.5 , - 0.5 , - 0.5 ); // P1 glVertex3f ( - 0.5 , 0.5 , - 0.5 ); // P2 glVertex3f ( 0.5 , 0.5 , - 0.5 ); // P3 glVertex3f ( 0.5 , - 0.5 , - 0.5 ); // P4 glEnd ();
4) Función glColor3f()
- glColor
funciona de un modo similar al glVertex. Podrás definir los puntos como cortos, enteros, dobles o flotantes. Cada color tiene un valor entre 0 y 1. Todos los 0 hacen el punto negro y todos los 1 hacen el punto blanco. El “3” en glColor3f() hace referencia al sistema de color RGB sin canal alfa. El nivel alfa de un color define su transparencia. Para cambiar ese nivel, entonces tendrás que usar glColor4f(), cuyo último parámetro tendrá un valor de 0 (opaco) a 1 (transparente).
- Cuando llames glColor3f(), cada vértice dibujado desde ese punto tendrá ese color. Por lo tanto, si quieres que los cuatro vértices sean rojos, solo tendrás que configurar el color una vez antes de los comandos glVertex3f() y todos los vértices serán de ese color.
- El lado frontal definido a continuación muestra cómo definir un color nuevo para cada vértice. Cuando lo hagas, verás una propiedad interesante de los colores OpenGL. Gracias a que cada vértice del polígono tiene su propio color, ¡OpenGL mezclará los colores automáticamente! El siguiente paso te mostrará cómo asignar cuatro vértices con el mismo color.
// LADO FRONTAL: lado multicolor glBegin ( GL_POLYGON ); glColor3f ( 1.0 , 0.0 , 0.0 ); glVertex3f ( 0.5 , - 0.5 , - 0.5 ); // P1 es rojo glColor3f ( 0.0 , 1.0 , 0.0 ); glVertex3f ( 0.5 , 0.5 , - 0.5 ); // P2 es verde glColor3f ( 0.0 , 0.0 , 1.0 ); glVertex3f ( - 0.5 , 0.5 , - 0.5 ); // P3 es azul glColor3f ( 1.0 , 0.0 , 1.0 ); glVertex3f ( - 0.5 , - 0.5 , - 0.5 ); // P4 es morado glEnd ();
5) Los otros lados
- Te animamos a
Te proponemos encontrar los otros vértices para los otros 5 lados del cubo, pero para simplificar las cosas ya lo hemos resuelto y se incluyen en la función display() final
a continuación:
// LADO TRASERO: lado blanco glBegin ( GL_POLYGON ); glColor3f ( 1.0 , 1.0 , 1.0 ); glVertex3f ( 0.5 , - 0.5 , 0.5 ); glVertex3f ( 0.5 , 0.5 , 0.5 ); glVertex3f ( - 0.5 , 0.5 , 0.5 ); glVertex3f ( - 0.5 , - 0.5 , 0.5 ); glEnd (); // LADO DERECHO: lado morado glBegin ( GL_POLYGON ); glColor3f ( 1.0 , 0.0 , 1.0 ); glVertex3f ( 0.5 , - 0.5 , - 0.5 ); glVertex3f ( 0.5 , 0.5 , - 0.5 ); glVertex3f ( 0.5 , 0.5 , 0.5 ); glVertex3f ( 0.5 , - 0.5 , 0.5 ); glEnd (); // LADO IZQUIERDO: lado verde glBegin ( GL_POLYGON ); glColor3f ( 0.0 , 1.0 , 0.0 ); glVertex3f ( - 0.5 , - 0.5 , 0.5 ); glVertex3f ( - 0.5 , 0.5 , 0.5 ); glVertex3f ( - 0.5 , 0.5 , - 0.5 ); glVertex3f ( - 0.5 , - 0.5 , - 0.5 ); glEnd (); // LADO SUPERIOR: lado azul glBegin ( GL_POLYGON ); glColor3f ( 0.0 , 0.0 , 1.0 ); glVertex3f ( 0.5 , 0.5 , 0.5 ); glVertex3f ( 0.5 , 0.5 , - 0.5 ); glVertex3f ( - 0.5 , 0.5 , - 0.5 ); glVertex3f ( - 0.5 , 0.5 , 0.5 ); glEnd (); // LADO INFERIOR: lado rojo glBegin ( GL_POLYGON ); glColor3f ( 1.0 , 0.0 , 0.0 ); glVertex3f ( 0.5 , - 0.5 , - 0.5 ); glVertex3f ( 0.5 , - 0.5 , 0.5 ); glVertex3f ( - 0.5 , - 0.5 , 0.5 ); glVertex3f ( - 0.5 , - 0.5 , - 0.5 ); glEnd (); glFlush (); glutSwapBuffers (); }
- También deberás incluir dos últimas líneas de código para esta función: glFlush(); y glutSwapBuffers(); , las cuales te darán el efecto de doble-buffer.
1) Función specialKeys()
- Todo está casi listo: ya tienes el cubo, pero no hay manera de rotarlo. Para hacerlo, crea una función specialKeys()
para permitirte presionar las teclas de dirección con flechas y ¡rotar el cubo!
- Por esta función tenías que declarar las variables globales “rotate_x” y “rotate_y”. Cuando presiones las teclas de flecha hacia la derecha e izquierda, entonces la variable “rotate_y” se incrementará o se disminuirá en 5 grados. Del mismo modo, cuando presiones las teclas de flecha hacia arriba y hacia abajo, la variable “rotate_x” se incrementará o se disminuirá en 5 grados.
void specialKeys ( int key , int x , int y ) { // La flecha derecha: incrementa su rotación en 5 grados if ( key == GLUT_KEY_RIGHT ) rotate_y += 5 ; // La flecha izquierda: disminuye su rotación en 5 grados else if ( key == GLUT_KEY_LEFT ) rotate_y -= 5 ; else if ( key == GLUT_KEY_UP ) rotate_x += 5 ; else if ( key == GLUT_KEY_DOWN ) rotate_x -= 5 ; // Solicitud para actualizar la pantalla glutPostRedisplay (); }
2) glRotate()
- La última instrucción será agregar las líneas para que el objeto rote. Regresa a la función display()
y antes de LADO FRONTAL, agrega las líneas a continuación:
// Resetea las transformaciones glLoadIdentity (); // Rotar cuando el usuario cambie los valores de “rotate_x” y “rotate_y”. glRotatef ( rotate_x , 1.0 , 0.0 , 0.0 ); glRotatef ( rotate_y , 0.0 , 1.0 , 0.0 ); // LADO FRONTAL: lado multicolor ....
- Observa que la sintaxis de glRotatef()
es similar a la de glColor3f() y glVertex3f() pero requiere de 4 parámetros siempre. El primer parámetro es el grado de rotación que será aplicado y los otros tres parámetros son los ejes de rotación X, Y y Z. Por ahora solo será necesario que rote sobre X e Y.
- Todas las transformaciones que escribas en el programa necesitan una línea similar a esta. Conceptualmente, se considera que es como rotar el objeto en el eje X por la cantidad definida por “rotate_x” y luego gira en el eje Y por “rotate_y”. Sin embargo, OpenGL combina todas estas declaraciones en una transformación de matriz. Cada vez que llames la función “display”, construirás una matriz de transformación y glLoadIdentity()
garantizará que empieces con una nueva matriz en cada pasada.
- Las otras funciones de transformación que podrías aplicar son glTranslatef() y glScalef(). Estas funciones son similares a glRotatef(), excepto que solo piden tres parámetros, las cantidades de X, Y, Z para trasladar o dibujar el objeto a escala.
- Para lograr el efecto deseado cuando apliques las tres transformaciones a un objeto, será necesario que las apliques en el orden correcto. Escríbelas siempre en orden: glTranslate, glRotate, luego glScale
. Lo que hará esencialmente OpenGL será aplicar las transformaciones de abajo hacia arriba. Para poder asimilarlo, imagina la apariencia de un cubo simple de 1x1x1 con las transformaciones si OpenGL las aplicara de arriba hacia abajo y si lo hiciera de abajo hacia arriba.
- Añade los siguientes comandos para dibujar a escala el cubo por 2 en los ejes X e Y, rota el cubo 180 grados en el eje Y, luego traslada el cubo 0,1 en el eje X. Al igual que con los comandos glRotate(), asegúrate de ponerlos en el orden correcto descrito anteriormente (si no estás seguro, lo verás hecho en el código final ubicado al final del presente tutorial).
// Otras transformaciones glTranslatef ( 0.1 , 0.0 , 0.0 ); glRotatef ( 180 , 0.0 , 1.0 , 0.0 ); glScalef ( 2.0 , 2.0 , 0.0 );
Compilación
- El último paso para terminar tu primer proyecto con OpenGL será compilar y ejecutar el código
. Asumiendo que tienes a tu disposición un compilador gcc, ejecuta los siguientes comandos en una terminal para compilar y probar el programa.
En Linux: gcc cube.c -o cube -lglut -lGL ./ micubo En Mac: gcc -o foo foo.c -framework GLUT -framework OpenGL ./ micubo En Windows: gcc -Wall -ofoo foo.c -lglut32cu -lglu32 -lopengl32 ./ micubo
- ¡Listo, ahí tienes tu primer programa OpenGL! A continuación se presenta el código fuente para que lo tengas como referencia:
// // Archivo: micubo.c // Autor: Mateo Díaz // Creado: 4/25/2012 // Proyecto: código fuente para hacer un cubo en OpenGL // Descripción: crear una ventana OpenGL y dibujar un cubo 3D // que el usuario pueda rotar con las teclas de flecha // // Controles: Flecha izquierda: rotar a la izquierda // Flecha derecha: rotar a la derecha // Flecha hacia arriba: rotar hacia arriba // Flecha hacia abajo: rotar hacia abajo // ---------------------------------------------------------- // Librerías // ---------------------------------------------------------- #include <stdio.h> #include <stdarg.h> #include <math.h> #define GL_GLEXT_PROTOTYPES #ifdef __APPLE__ #include <GLUT/glut.h> #else #include <GL/glut.h> #endif // ---------------------------------------------------------- // Prototipos de función // ---------------------------------------------------------- void display (); void specialKeys (); // ---------------------------------------------------------- // Variables globales // ---------------------------------------------------------- double rotate_y = 0 ; double rotate_x = 0 ; // ---------------------------------------------------------- // Función de retrollamada “display()” // ---------------------------------------------------------- void display (){ // Borrar pantalla y Z-buffer glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // Resetear transformaciones glLoadIdentity (); // Otras transformaciones // glTranslatef( 0.1, 0.0, 0.0 ); // No incluido // glRotatef( 180, 0.0, 1.0, 0.0 ); // No incluido // Rotar cuando el usuario cambie “rotate_x” y “rotate_y” glRotatef ( rotate_x , 1.0 , 0.0 , 0.0 ); glRotatef ( rotate_y , 0.0 , 1.0 , 0.0 ); // Otras transformaciones // glScalef( 2.0, 2.0, 0.0 ); // No incluido //LADO FRONTAL: lado multicolor glBegin ( GL_POLYGON ); glColor3f ( 1.0 , 0.0 , 0.0 ); glVertex3f ( 0.5 , - 0.5 , - 0.5 ); // P1 es rojo glColor3f ( 0.0 , 1.0 , 0.0 ); glVertex3f ( 0.5 , 0.5 , - 0.5 ); // P2 es verde glColor3f ( 0.0 , 0.0 , 1.0 ); glVertex3f ( - 0.5 , 0.5 , - 0.5 ); // P3 es azul glColor3f ( 1.0 , 0.0 , 1.0 ); glVertex3f ( - 0.5 , - 0.5 , - 0.5 ); // P4 es morado glEnd (); // LADO TRASERO: lado blanco glBegin ( GL_POLYGON ); glColor3f ( 1.0 , 1.0 , 1.0 ); glVertex3f ( 0.5 , - 0.5 , 0.5 ); glVertex3f ( 0.5 , 0.5 , 0.5 ); glVertex3f ( - 0.5 , 0.5 , 0.5 ); glVertex3f ( - 0.5 , - 0.5 , 0.5 ); glEnd (); // LADO DERECHO: lado morado glBegin ( GL_POLYGON ); glColor3f ( 1.0 , 0.0 , 1.0 ); glVertex3f ( 0.5 , - 0.5 , - 0.5 ); glVertex3f ( 0.5 , 0.5 , - 0.5 ); glVertex3f ( 0.5 , 0.5 , 0.5 ); glVertex3f ( 0.5 , - 0.5 , 0.5 ); glEnd (); // LADO IZQUIERDO: lado verde glBegin ( GL_POLYGON ); glColor3f ( 0.0 , 1.0 , 0.0 ); glVertex3f ( - 0.5 , - 0.5 , 0.5 ); glVertex3f ( - 0.5 , 0.5 , 0.5 ); glVertex3f ( - 0.5 , 0.5 , - 0.5 ); glVertex3f ( - 0.5 , - 0.5 , - 0.5 ); glEnd (); // LADO SUPERIOR: lado azul glBegin ( GL_POLYGON ); glColor3f ( 0.0 , 0.0 , 1.0 ); glVertex3f ( 0.5 , 0.5 , 0.5 ); glVertex3f ( 0.5 , 0.5 , - 0.5 ); glVertex3f ( - 0.5 , 0.5 , - 0.5 ); glVertex3f ( - 0.5 , 0.5 , 0.5 ); glEnd (); // LADO INFERIOR: lado rojo glBegin ( GL_POLYGON ); glColor3f ( 1.0 , 0.0 , 0.0 ); glVertex3f ( 0.5 , - 0.5 , - 0.5 ); glVertex3f ( 0.5 , - 0.5 , 0.5 ); glVertex3f ( - 0.5 , - 0.5 , 0.5 ); glVertex3f ( - 0.5 , - 0.5 , - 0.5 ); glEnd (); glFlush (); glutSwapBuffers (); } // ---------------------------------------------------------- // Función de retrollamada “specialKeys()” // ---------------------------------------------------------- void specialKeys ( int key , int x , int y ) { // Flecha derecha: aumentar rotación 5 grados if ( key == GLUT_KEY_RIGHT ) rotate_y += 5 ; // Flecha izquierda: disminuir rotación 5 grados else if ( key == GLUT_KEY_LEFT ) rotate_y -= 5 ; else if ( key == GLUT_KEY_UP ) rotate_x += 5 ; else if ( key == GLUT_KEY_DOWN ) rotate_x -= 5 ; // Solicitar actualización de visualización glutPostRedisplay (); } // ---------------------------------------------------------- // Función “main()” // ---------------------------------------------------------- int main ( int argc , char * argv []){ // Inicializar los parámetros GLUT y de usuario proceso glutInit ( & argc , argv ); // Solicitar ventana con color real y doble buffer con Z-buffer glutInitDisplayMode ( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH ); // Crear ventana glutCreateWindow ( "Cubo asombroso" ); // Habilitar la prueba de profundidad de Z-buffer glEnable ( GL_DEPTH_TEST ); // Funciones de retrollamada glutDisplayFunc ( display ); glutSpecialFunc ( specialKeys ); // Pasar el control de eventos a GLUT glutMainLoop (); // Regresar al sistema operativo return 0 ; }
Referencias
- Instalar OpenGL
- Tutoriales sobre OpenGL por Nehe Productions
- OpenGL: A Primer, 3/E
- CSCI 4229 Introducción a los gráficos por computadora. Profesor Schreüder, Universidad de Colorado, Boulder
Anuncio