Mini Manual OpenGL

Anuncio
Mini Manual de OpenGL
Por Daniel Barrero ([email protected]) - 11/6/2002
Introducción
OpenGL es una interface de software para el hardware grafico, esta interface consiste de una larga serie de
comandos para manipulacion de objetos y operaciones sobre estos los cuales permiten controlar la
implentacion realizada en la forma de una maquina de estados finitos, donde cada una de las variables que
determinan el estado se aplican a partir de ese punto hasta que se indique explicitmanete el cambio, asi las
variables de estado de OpenGL que vamos a utilizar mas comunmente son:
• Color (de dibujo y de borrado).
• Matrices de Transformacion (GL_MODELVIEW, GL_PROYECTION).
• Patrones (de lineas y de relleno).
• Modo de dibujo de poligonos.
• Buffers de dibujo.
• Buffer de Stencil.
• Buffer de profundidad (z-Buffer).
• Buffer de acumulacion.
Funcionamiento de OpenGL:
Para poder trabajar con OpenGL, primero se debe crear un contexto de trabajo, este contexto contiene el
estado actual de maquina finita, asi como las referencias a los diferentes buffers de trabajo, estos buffers se
pueden ver como zonas de memoria correspondiendo a la pantalla en las cuales OpenGL va a dibujar, en
general a parte del buffer de color (GL_COLOR_BUFFER) que es el buffer en el cual se van a dibujar las
primitivas, existen otro tipo de buffers mas especializados.
La configuracion en memoria de estos buffers (cuantos bits representan un pixel, etc) depende de la
manera como fue creado el contexto OpenGL y de las limitaciones del hardware, por esto no se puede
acceder directamente sino solo a traves de las primitivas OpenGL.
OpenGL puede funcionar adicionalmente de dos maneras, de modo directo o indirecto:
• Modo directo: las primitivas se van dibujando a medida que se van definiendo.
Instruccion -> Buffer de Color = Pantalla
• Modo indirecto: las primitivas se guardan en una lista y solo se dibujan cuando el usuario decida o la
lista este llena, esto permite optimizar la fase de dibujo.
Instruccion-> Pila de instrucciones-> flush -> Buffer de Color = Pantalla
En este modo cuando se desea que OpenGL pinte lo que esta en la lista se utiliza la instruccion
glFlush(): esta instruccion obliga a pintar y no espera a que el hardawre termine para continuar con
el programa, analogamente la glFinish() obliga a pintar pero espera a que el hw termine antes de
continuar con el programa.
En el modo indirecto, OpenGL permite definir dos buffers de colores (doublebuffer), asi un buffer
corresponde a lo que se ve en pantalla y otro a el buffer donde se esta pintando, de esta manera una vez que
se ha pintado todo lo deseado y se quiere que esto aparezca en pantalla se intercambian los buffers, esta
instruccion depende del sistema operativo para esto se utilizara la instruccion de la libreria portable glut:
glutSwapBuffers() (esta ejecuta implicitamente glFlush o glFinish), en este modo glFlush y glFinish
obligan a pintar en el buffer de dibujo pero esto NO sera visible hasta intercambiar buffers.
Primitivas de dibujo :
En OpenGL solo se pueden dibujar primitivas muy simples, tales como puntos lineas, cuadrados, triangulos
y polygonos, a partir de estas primitivas es posible construir primitivas mas complejas como arcos y
circulos aproximandolos por poligonos.
Toda primitiva de dibujo se construye con un par: glBegin(tipo_de_primitiva); glVertex2f(); ... glEnd();
donde tipo_de_primitiva puede ser cualquiera de las siguientes:
GL_POINTS: Cada vertice es un punto
GL_LINES: Cada par de vertices sucesivos es
una linea
GL_LINE_STRIP: lineas conectadas.
GL_LINE_LOOP: lineas conectadas donde el
ultimo y el primer vertice indican una linea
cerrando el poligono.
GL_POLYGON: poligono (relleno o no) donde
los vertices sucesivos componiendolo se dan el
sentido contrario de las manecillas del reloj.
GL_QUADS: cuadrilateros separados, cada 4
vertices hacen un quad.
GL_QUAD_STRIP: tira de cuadrados unidos,
cada par de vertices sucesivos forman un
cuadrado con el par anterior.
GL_TRIANGLES: Triangulos separados, cada
3 vertices hacen un triangulo.
GL_TRIANGLE_STRIP: tira de triangulos
unidos (similara quad_strip).
GL_TRIANGLE_FAN: Grupo de triangulos
con un unico vertice comun a todos.
Dentro del par glBegin, glEnd solo pueden ir instrucciones OpenGL para definir objetos tales como
vertices, y colores (existen otras mas complejas como normales y materiales) y no transformaciones ni
cambios de estado (diferentes a los especificados), adicionalmente dentro del par pueden ir instrucciones de
programacion del lenguaje tales que ciclos, condicionales, llamados a funciones, etc. Siempre y cuando no
usen alguna de las funciones OpenGL no permitidas, i.e.:
glBegin(GL_POLYGON)’;
glColor3f(1.0, 0.0, 0.0); // rojo
for(int i=0; i<10; i++){
glVertex3f(1.0/i, i*i, 0.0);
}
glColor3f(0.0, 1.0, 0.0); // verde
glVertex3f(1.0, 0.0, 0.0);
glColor3f(0.0, 0.0, 1.0); // azul
glVertex3f(1.0, 1.0, 0.0);
glEnd();
muchas primitivas basicas tiene sufijos indicando el tipo del valor o variable que se le pasan como
parametro, asi como el numero de parametros, asi una instruccion como p.ej. glVertex puede tener 2,3 o 4
parametros, asi comor recibir tipos enteros, flotantes, etc. O un vector conteniendo esos parametros
Entonces en los manuales se escribe como: glVertex[234][sifd][v] quiere decir que se le pueden pasar 2,3 o
4 parametros mas el tipo o un vector, como minimo debe tener el numero de parametros y el tipo i.e.:
glVertex2f(1.0,0.0). o si se usa un vector v Glfloat v[3]={1.0,0.5} entonces seria glVertex2fv(v);
los tipos de datos tambien se pueden usar para definir variables, estos son:
b
entero 8 bits
GLbyte
s
entero 16 bits
GLshort
i
entero 32 bits
Glint
f
float 32 bits
GLfloat
d
flotante 64 bits
GLdouble
ub entero sin signo 8 bits GLubyte
us entero sin signo 16 bits GLushort
ui entero sin signo 32 bits GLuint
Las primitivas mas basicas de dibujo son entonces:
glRect[sid][v]: dibuja un rectangulo, NO puede estar en bloque glBegin/glEnd
glColor3[f][v]: para cambiar el color actual de dibujo, puede estar en bloque glBegin/glEnd
glVertex[234][sifd][v]: vertice de un punto,linea o poligono, SOLO puede estar entre glBegin/glEnd
Variables de estado que afectan las primitivas anteriores (NO puede estar en bloque glBegin/glEnd):
glPointSize(size): size= flotante indicando el tamaño de dibujo de un punto > 0.0, 1.0 por defecto.
glLineWidth(size): size= flotante indicando el ancho de dibujo de una linea > 0.0, 1.0 por defecto.
glLineStipple(factor,patron): factor = entero indicando un factor de escalamiento, patron= short sin signo
conteniendo el patron de bits par pintar la linea (punteado,etc..)..
glPolygonMode(cara,modo): cara corresponde al lado del poligono, si el que esta hacia el usuario
(pantalla) (GL_FRONT) o hacia el otro lado (GL_BACK) o los dos (GL_FRONT_AND_BACK), el modo
puede ser: relleno (GL_FILL), solo los vertices (GL_POINT), o solo el borde (GL_LINE).
Otras funciones y variables de estado:
glClearColor(r,g,b): r,g,b son flotantes y definen el color de borrado de la pantalla (fondo).
glClear(mask): mask = GL_COLOR_BUFFER_BIT borra el buffer de dibujo con el color de fondo
definido.
Transformaciones:
Las tranformaciones se realizan multiplicando por las matrices y se aplican en sentido inverso al que se
escriben, esto es si quiero rotar y luego trasladar un objeto en el codigo, primero traslado, luego roto y luego
si pinto mi objeto (es tambien en sentido inverso al que se lee el codigo), OpenGL trabaja con dos matrices
diferentes:
• GL_MODELVIEW: es la matriz donde se trabajan con las transformaciones.
• GL_PROJECTION: es donde se define la relacion mundo viewport.
Para cambiar de matriz a utilizar se usa la instruccion:
glMatrixMode(modo): es uno de los dos modos de matriz.
Adicionalmente, la matriz en uso se puede guardar y eliminar de una pila:
glPushMatrix(): coloca la matriz en la pila.
glPopMatrix(): quita la matriz de la pila.
La pila de la matriz de projeccion tiene un limite maximo de 2 en la mayoria de las implementaciones.
Para trabajar con la matriz directamente se tiene:
glLoadIdentity(): inicializa la matriz actual con una matriz identidad.
glMultMatrix(matriz): matriz = vector de 16 flotantes con el que multiplica la matriz actual.
glLoadMatrix(matriz): matriz = vector de 16 flotantes con el que reemplaza la matriz actual.
glGetFloatv(modo,m): modo = uno de los modos de matriz.
matriz = vector de 16 flotantes en el que se recupera la matriz actual de OpenGL.
Para no tener que generar sus propias matrices, se proveen instrucciones de tranformacion:
glRotate[fd](a,x,y,z): rota un angulo a alrededor del vector x,y,z
glTranslate[fd](x,y,z): traslada una cantidad x,y,z.
glScale[fd](sx,sy,sz): multiplica cada coordenada por el valor de escalamiento correspondiente.
Estructura de un programa OpenGL
Para la creacion de programas OpenGL se va a utilizar la libreria GLUT, esta libreria permite olvidarse de
los problemas especificos de cada plataforma, La estructura general de un programa utilizando la libreria
GLUT es la siguiente (en C o C++ es las funciones de opengl son las mismas):
#include <GL/gl.h>
#include <GL/glut.h>
void init(void)
{
glClearColor(0,0,0,0); // iniciar estado global (color de borrado
}
void reshape ( int w, int h)
{
glViewport(0,0,w,h);
// coloque el viewport al tamano de la ventana
glMatrixMode(GL_PROJECTION); // modo matriz de proyeccion
glLoadIdentity();
// inicialize la matriz
glOrtho(0, w, 0, h, -1, 1);
// tranformacion directa a pantalla
glScalef(1, -1, 1);
// invierta Y para que vaya para abajo
glTranslatef(0, -h, 0);
// corra el origen a la parte superior izq.
glMatrixMode(GL_MODELVIEW);
// modo matriz de transformacion
glLoadIdentity();
// inicialize la matriz
}
void display(void)
// pinte....
{
glClear(GL_COLOR_BUFFER_BIT); // borre la pantalla
glBegin(GL_TRIANGLES);
// pinte
glColor3f(0.0, 0.0, 1.0);
glVertex3f(0, 1,0);
glVertex3f(0, 0,0);
glVertex3f(0, -1,0);
glEnd();
glutSwapBuffers();
// cambie buffers...
}
void keyboard(unsigned char c, int x, int y)
{
switch(c) {
// aqui adicionar los casos para las diferentes teclas.
case 27: exit(0);
break;
}
glutPostRedisplay();
//pinte si se cambiaron variables que afectan el dibujo
}
void idle(void)
{
// modifique variables
glutPostRedisplay(); //pinte
}
void main( int argc, char **argv)
{
glutInit(&argc,argv);
// inicie la libreria GLUT
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); // inicie el modo de video
glutInitWindowSize(w,h); // inicie tamano de la ventana
glutInitWindowPos(x,y); // inicie posicion de la ventana
glutCreateWindow("nombre ventana ");
init();
// iniciar variables y estado global
glutDisplayFunc(display);
// instale la funcion de display
glutReshapeFunc(reshape);
// instale la funcion de tamaño.
glutKeyboardFunc(keyboard);
// instale la funcion de teclado
glutIdleFunc(idle);
// funcion de animacion.
glutMainLoop(); // ciclo infinito
}
Manejo de Buffers
Para utilizar los diferentes tipos de buffers de OpenGL estos se deben crear al crear el contexto opengl, en
el caso de la utilizacion de la libreria glut, al llamar la funcion: glutInitDisplayMode (buffers), buffers es
una combinacion de valores indicando que buffers se deben crear i.e. para crear un contexto con doble
buffer rgba y z-buffer buffers seria = GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH, los valores
para crear otro tipos de buffers son:
GLUT_STENCIL = buffer de stencil.
GLUT_ACCUM = buffer de acumulacion.
Todos los buffers deben ser activados o desactivados dependiendo de la utilizacion con el comando
glEnable(param) o glDisable(param), donde param corresponde al tipo de test que se activa, i.e param=
GL_STENCIL_TEST para el buffer de stencil, o param=GL_DEPTH_TEST para el buffer de
profundidad (activado por defecto).
Los buffers de stencil, acumulacion y profundidad funcionan aplicando una serie de operaciones y
funciones sobre los valores que se van a escribir en el buffer, asi para manejar la funcion del buffer de
profundidad se utiliza la funcion glDepthFunc(funcion), donde funcion indica cuando pasa el test de
profundidad (GL_EQUAL, GL_LESS, GL_LEQUAL (por defecto), GL_NOTEQUAL, GL_GREATER,
GL_GEQUAL). En el caso de la funcion de test, se define glStencilFunc(funcion, mascara), donde funcion
son las mismas que para la profundidad, y mascara indica que bits del stencil se utilizan. Adicionalmente a
la funcion de stencil, se utiliza la operacion de stencil glStencilOp(fallo,zfallo,zpaso) que especifica como
se modifica el buffer de stencil dependiendo de si la funcion del buffer stencil y z fallaron o pasaron, pueden
ser GL_KEEP,GL_ZERO, GL_REPLACE, GL_INCR, GL_DECR, GL_INVERT.
Manejo de Animacion y Timers
Opengl no maneja animacion ni timers directamente ya que esto depende del sistema operativo, OpenGL
solo se preocupa de colocar poligonos en la pantalla, y solo seria cuestion de ordenarle de manera ciclica el
repintar la pantalla con paramentros nuevos, el problema se encuentra cuando se desea que la animacion se
no bloqueante, la libreria GLUT nos facilita el manejo de la animacion de dos maners independientemente
del Sistema operativo de manera no bloqueante:
1. Funcion de animacion Idle, GLUT nos permite manejar la animacion definiendo una funcion de
animacion con la funcion glutIdleFunc(funcname), donde funcname es el nombre de la funcion que se va a
llamar para realizar la animacion, esta funcion no recibe ni retorna parametro alguno, esta funcion se debe
encargar de calcular los parametros de la animacion y luego llamar la funcion de dibujo, esta no se debe
llamar directamente sino con la funcion glutPostRedisplay() que quiere decir que solo se llama una vez se
acabe de calcular la funcion idle, esta funcion idle no se ejecuta de manera regular, solo se ejecuta una vez
se hallan procesado todos los eventos que recibe la aplicacion tales como teclado, repintar, etc. Por lo tanto
la velocidad de la animacion resultante puede no ser constante.
2. Funciones de Timers, para evitar el problema de la velocidad de animacion irregular y para tener mas
control sobre el tiempo de ejecucion de la funcion de animacion, glut nos permite definir timers de a los
cuales le asignamos una funcion que se debe ejecutar cada cierto tiempo definido por el timer, para definir
estos timers se utiliza la funcion: glutTimerFunc(tiempo,funciontimer,valor), donde tiempo es el tiempo
en milisegundos del timer, funciontimer es la funcion a llamar la cual no devuelve nada pero recibe un
parametro entero (valor), valor es el parametro que se le va a pasar a la funcion. Esta funcion se ejecuta
dentro del tiempo determinado y termina, si se desea que se vuelva a llamar, al final de la funcion se debe
instalar de nuevo el timer. El valor sirve para seleccionar diferentes acciones la proxima vez que se llame el
timer, como estas funciones no reciben parametros, cualquier resultado debe ser devuelto por una variable
global.
Compilacion de aplicaciones OpenGL en Windows
Para compilar un programa OpenGL suponiendo que se esta en un sistema operativo MS-Windows, con el
compilador VisualC++, se debe recuperar la libreria glut para windows (glut.h glut32.lib y glut32.dll), los
archivs .h y . lib se deben colocar en el directorio respectivo para includes y librerias del VisualC, el archivo
glut32.dll se debe colocar en el directorio system32 de windows idealmente o en su defecto en el directorio
donde va a quedar el ejecutable.
En el VisualC se debe crear un proyecto nuevo de tipo win32 o win32 consola (si se va a utilizar GLUT
NO! se debe crear proyecto de tipo MFC) En las opciones de compilacion del proyecto, en el tabulador de
encadenamiento, se debe adicionar las siguientes librerias en este orden especifico: glut32.lib glu32.lib
opengl32.lib.
Compilacion de aplicaciones OpenGL en Linux
Para compilar un programa OpenGL suponiendo que se esta en sistema operativo UNIX o Linux con las
librerias instaladas en lugares estandar se debe dar el siguiente comando suponiendo que todo el programa
esta en un solo archivo:
Programa esta en C:
gcc -o nombre_ejecutable
nombre_programa.c –lglut –lGLU –lGL lm
Programa esta en C++:
g++ -o nombre_ejecutable
nombre_programa.c –lglut –lGLU –lGL lm
Si el programa esta compuesto por varios modulos C o C++ se debe realizar un archivo llamado: Makefile,
este archivo debe ser de la manera siguiente:
CC = gcc
CXX = g++
INCDIR = -I./ -I/usr/local/include
# XLIBS = -lX11 -lXt -lXext -lXm -lXmu -lXi
GL_LIBS= -lglut -lGLU -lGL -lm
LIBDIR = -L/usr/X11/lib -L/usr/X11R6/lib -L/usr/local/lib
CFLAGS = -O
DOCDIR = doc
OBJECTS= modulo1.o modulo2.o
PROGS = nombreprograma
.SUFFIXES: .c .cc .cpp
.c:
$(CC) -o $@ $(CFLAGS) $(INCDIR) $< $(LIBDIR) $(GL_LIBS) $(XLIBS)
.cc:
$(CXX) -o $@ $(CFLAGS) $(INCDIR) $< $(LIBDIR) $(GL_LIBS) $(XLIBS)
.cpp:
$(CXX) -o $@ $(CFLAGS) $(INCDIR) $< $(LIBDIR) $(GL_LIBS) $(XLIBS)
all: $(OBJECTS) $(PROGS)
nombreprograma: modulo1.c modulo2.cpp
$(CC) -o $@ $(CFLAGS) $(INCDIR) $(OBJECTS) $(LIBDIR) $(GL_LIBS) $(XLIBS)
Ojo: Las identaciones en el archivo son tabulador no espacios en blanco!.
Solo se debe cambiar nombreprograma por el nombre del archivo que contiene la funcion main, y modulo1,
modulo2, etc por los nombres de los otros archivos conteniendo otras funciones del programa, para compilar
solo basta dar en la linea de comandos el comando:
make
Para correr el programa ya sea que se haya compilado con la ayuda de make o directamente a mano con
gcc/g++ solo basta dar el comando:
./nombredeprograma
Para mas informacion sobre el comando make dar el comando:
man make
,o en caso que este falle:
info make
Para mas informacion sobre opciones gcc/g++ dar el comando: man gcc ,o: man g++
Informacion Adicional
Para mas informacion sobre instalar OpenGL y glut en caso que estos no esten instalados ir a:
http://www.opengl.org y http://www.mesa3d.org
Tutoriales basicos y avanzados de opengl se pueden encontrar en los siguientes sitios:
• http://nehe.gamedev.net/
• http://romka.demonews.com/index_eng.htm
Descargar