Principal | Gráficos 3D | Gráficos 2D | Fractales | Math | Códigos | Tutoriales | Links
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 :
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>
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;
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.
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.
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() |
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();
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; } }
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