Documentos de Académico
Documentos de Profesional
Documentos de Cultura
openGLTutorialSpanish PDF
openGLTutorialSpanish PDF
con OpenGL
ndice
NDICE 1
INTRODUCCIN 5
1. CONCEPTOS PREVIOS. 6
3. TRANSFORMACIONES. 18
4. PROYECCIONES. 30
5. LA LIBRERA GLUT 35
6.1. EL RATN 46
6.2. EL TECLADO 48
6.3. CAMBIO DE TAMAO 50
6.5. MENS 51
7. ILUMINACIN 54
8. TEXTURIZACIN 62
9. BUFFERS DE OPENGL 68
10.1. NIEBLA 73
10.2. TRANSPARENCIAS 74
10.3. ANTIALIASING 76
10.4. MOTION BLUR 77
Introduccin
OpenGL es una librera grfica escrita originalmente en C que permite la
manipulacin de grficos 3D a todos los niveles. Esta librera se concibi para
programar en maquinas nativas Silicon Graphics bajo el nombre de GL (Graphics
Library). Posteriormente se considero la posibilidad de extenderla a cualquier tipo de
plataforma y asegurar as su portabilidad y extensibilidad de uso con lo que se llego al
termino Open Graphics Library, es decir, OpenGL.
As esta librera puede usarse bajo todo tipo de sistemas operativos e incluso
usando una gran variedad de lenguajes de programacin. Podemos encontrar variantes
de OpenGL para Windows 95/NT, Unix, Linux, Iris, Solaris, Delphi, Java e incluso
Visual Basic. No obstante, su uso mas extenso suele ser el lenguaje C o C++.
Algo de lgebra lineal tambin har falta para entender que es lo que ocurre
internamente y como podemos operar con nuestra geometra para lograr los efectos
deseados.
1. Conceptos previos.
1.2. Sistemas grficos. Dispositivos y elementos.
La resolucin viene dada por el producto ancho x alto, es decir, el nmero de filas
multiplicado por el nmero de columnas anlogamente a las resoluciones que nuestro
monitor puede tener (640 x 480, 800 x 600 ...).
Dado que color real implica 256 posibles valores de rojo, 256 de verde y 256 de
azul por pxel y esto implica un byte/pxel para cada una de estas componentes, es decir,
3 bytes por pxel.
OpenGL utiliza este modelo semntico para interpretar una escena que debe ser
dibujada. Bsicamente se trata de imaginar un objeto situado en un determinado lugar y
filmado por una cmara, tan sencillo como esto. Veamos el siguiente diagrama:
Contamos con un "mundo 3D" que estamos observando desde una determinada
posicin. Podis pensar en el observador como vosotros mismos mirando hacia vuestro
mundo virtual o bien como una cmara que lo esta filmando. Ese punto es el centro de
proyeccin tal y como se observa en la figura. Evidentemente el mundo es
tridimensional pero su proyeccin en un plano (plano de proyeccin) es bidimensional.
Este plano es nuestro frame buffer antes de dibujar o bien la pantalla del monitor
despus de haberlo hecho.
Necesitamos luces que iluminen nuestro mundo 3D. Para ello ser necesario que
especifiquemos sus localizaciones, sus intensidades y sus colores.
Necesitamos una cmara que "filme" nuestro mundo virtual y lo muestre por
pantalla. Esta cmara es nuestro "punto de vista" del mundo a cada momento. Se
caracteriza por su posicin en el mundo, su orientacin y su apertura o
"campo visual". El campo visual es la "cantidad" de mundo que la cmara
alcanza a ver. Mirad la figura anterior e imaginad que acercamos el plano de
Es muy importante que notis la independencia que todos estos elementos tienen
entre s. La cmara es independiente de los objetos puesto que estos estn en una
posicin y ella en otra. Manejaremos los objetos (Geometra) por un lado y la cmara
por otro de manera que los primeros "actuaran" en nuestro mundo y la segunda filmar
desde una determinada posicin con un determinado ngulo.
Usaremos:
o:
glVertexfv( vrtice );
glBegin(GL_TRIANGLES);
glVertex3f( -1.0, 0.0, 0.0 );
glVertex3f( 1.0, 0.0, 0.0 );
glVertex3f( 0.0, 1.0, 0.0 );
glEnd( );
Este cdigo crea un tringulo situado en el plano XY ya que observareis que los
valores de Z son todos 0.0 para los tres vrtices que lo forman. Sus tres vrtices se
encuentran en las posiciones ( -1.0, 0.0 ), ( 1.0, 0.0 ) y (0.0, 1.0 ) segn la forma ( X, Y
).
donde los tres valores (floats) que se le pasan a la funcin glColor son por orden,
la cantidad de rojo (Red) que deseamos, la cantidad de verde (Green) y la cantidad de
azul (Blue). Es el llamado sistema RGB que muchos conoceris sobradamente.
Aplicando una cantidad de cada color conseguimos el tono deseado (Teora aditiva del
color). Los valores de cada color deben estar entre 0.0 (No aplicar ese color) y 1.0
(Aplicar ese color en su mxima intensidad). Por tanto:
mientras que...
Algo muy importante que debis tener en cuenta. Cuando definis un polgono a
base de sus vrtices deberis seguir un orden concreto. Si no lo hacis, OpenGL no
asegura que la representacin en pantalla sea la correcta. La convencin es crear los
vrtices siguiendo el polgono segn el sentido antihorario de las manecillas del reloj.
OpenGL supone la cara "a dibujar" del polgono como la que se define de esta manera.
Respecto a las constantes que podemos usar en la funcin glBegin tenemos entre
otras:
GL_POINTS : para que todos los vrtices indicados entre ambas funciones se
dibujen por separado a modo de puntos "libres".
GL_LINES : cada dos vrtices definidos, se traza automticamente una lnea
que los une.
GL_POLYGON : se unen todos los vrtices formando un polgono.
GL_QUADS : cada 4 vrtices se unen para formar un cuadriltero.
GL_TRIANGLES : cada 3 vrtices se unen para formar un tringulo.
GL_TRIANGLE_STRIP : crea un tringulo con los 3 primeros vrtices.
entonces sucesivamente crea un nuevo tringulo unido al anterior usando los dos
ltimos vrtices que se han definido y el actual.
GL_QUAD_STRIP : igual que el anterior pero con cuadrilteros.
De hecho los tipos mas usados son los tres primeros mientras que el resto pueden sernos
de ayuda en casos determinados. Veamos la figura:
creados segn el orden que veis en la figura ( primero p0, luego p1, etc.) para los 6
rectngulos de la primera fila. Es decir:
glBegin( TIPO );
glVertex3f( p0x, p0y, p0z );
glVertex3f( p1x, p1y, p1z );
glVertex3f( p2x, p2y, p2z );
glVertex3f( p7x, p7y, p7z );
glEnd();
Los polgonos especificados por una serie de puntos deben ser convexos para su
correcto trazado, y coloreado, por parte de OpenGL. Que quiere decir esto?, pues bien
se define a un polgono como convexo si cogiendo dos puntos cualesquiera de su
interior y trazando la lnea que los une, todos los puntos que pertenecen a la lnea se
encuentran dentro del polgono. En la anterior figura tenis un ejemplo de esto.
Por tanto asegurad siempre que defins polgonos convexos, de lo contrario los
resultados son simplemente impredecibles y pocas veces acertados. Entonces, como
dibujamos polgonos que no sean convexos? Lo que normalmente se hace es dibujar un
polgono complejo, convexo o no, a base de muchos tringulos. Es lo que se denomina
"Tesselation". Todo polgono puede descomponerse en tringulos y, por tanto, a partir
de estos somos capaces de dibujar cualquier cosa. Tambin lo tenis en la figura
explicitado grficamente.
Usando GLU o GLUT podemos crear con una sola llamada objetos mas complejos
como es el caso de una esfera:
En cuanto programemos lo veris mas claro pero de hecho no dejan de ser funciones
en C de las de siempre. Con radius definimos el radio que queremos para la esfera, con
slices y stacks la "partimos" en porciones como si de un globo terrqueo se tratara (
paralelos y meridianos) de manera que a mas particiones, mas calidad tendr ya que
OpenGL la aproximara por mas polgonos. En cuanto a *qobj en la primera funcin, se
trata de definir primero un objeto de tipo quadric con otra funcin de GLU (
gluNewQuadric ) para despus asociar la esfera al susodicho objeto. No os preocupis
porque no usaremos demasiado esta filosofa. De momento quedaros con la idea que es
lo que me interesa.
glTranslatef( posX, posY, posZ ): funcin que nos traslada posX unidades en el eje
X, posY en el eje Y ... y si tras esto llamamos a glutSolidSphere, ya tendremos a nuestra
esfera dibujada en el punto que deseamos.
glClearColor( 0.0, 0.0, 0.0, 0.0 ): esta es algo genrica y se refiere al color con
el cual debe de "resetearse" el frame buffer cada vez que redibujemos toda la
escena de nuevo. En este caso el "fondo" de nuestra ventana ser como el fijado
por esta funcin en el frame buffer, de color negro. El cuarto parmetro de la
funcin se refiere al valor de "alpha" en cuanto al color. Veremos mas adelante
que el valor de alpha permite variar el grado de transparencia de un objeto.
glColor3f( 1.0, 0.0, 0.0 ): En este caso definimos que todo lo que se dibuje
desde este momento ser de color rojo. Recordad que el orden de parmetros es
Red, Green, Blue ( RGB ).
glPointSize( 2.0 ): con esta llamada definimos que cada uno de nuestros puntos
deber tener un grosor de dos pixeles en el momento de ser trasladado a pantalla.
glNormal3f( 1.0, 0.0, 0.0 ); cuando operemos con luces veremos que para cada
cara de un polgono hay que asociar un vector o normal. Esta funcin define
como normal a partir de este momento a un vector definido desde el origen con
direccin positiva de las X. El orden de los parmetros es X, Y, Z.
glMaterialfv(GL_FRONT, GL_DIFFUSE, blanco); por ultimo y tambin
referido al tema de las luces. Cada objeto ser de un material diferente de
manera que los reflejos que en el se produzcan debidos a las luces de nuestra
escena variaran segn su rugosidad, transparencia, capacidad de reflexin ... en
este caso definimos que todas las caras principales ( FRONT, son las caras "que
se ven" del polgono ) de los objetos dibujados a partir de ahora tendrn una
componente difusa de color blanco ( asumiendo que el parmetro "blanco" es un
vector de reales que define este color ). La componente difusa de un material
define que color tiene la luz que este propaga en todas las direcciones cuando
sobre el incide un rayo luminoso. En este caso se vera luz blanca emanando del
objeto/s dibujados a partir de ahora. Por supuesto antes hay que definir luces en
En el caso del valor para alpha, 0 significa transparencia total y de aqu podemos
usar cualquier valor hasta 1, objeto totalmente opaco.
Que decir del texto.....no nos es de mucho inters ahora mismo que lo que estamos
deseando es empezar a dibujar polgonos 3D como locos pero comentare un par de
cosas. OpenGL permite dos modos de texto: Stroke Text y Raster Text.
En el primer caso cada letra del alfabeto es una nuevo polgono literalmente construido
a partir de primitivas de manera que puede tratarse tal y como si de un objeto cualquiera
se tratara. Es pesado pues tenemos que dibujar literalmente cada letra a base de
primitivas pero interesante en cuanto a posibilidades pues podremos escalar, rotar,
trasladar.....en fin todo lo que nos es posible aplicar a un polgono.
En cuanto al segundo caso tenemos un ejemplo de simplicidad y rapidez. Es el tpico
alfabeto creado a partir de bloques de bits ( BitMaps ), de manera que tendremos una
rejilla sobre la que un 1 significara lleno y un 0 vaco. De esta forma:
1 0 0 0 1
1 0 0 0 1
1 0 0 0 1
1 1 1 1 1
1 0 0 0 1
1 0 0 0 1
1 0 0 0 1
3. Transformaciones.
P = ( x, y, z)
Vector v = Punto1 - Punto2 = (x1, y1, z1) - (x2, y2, z2) = (a, b, c)
Punto P1 = (x1, y1, z1) en cartesianas es P1 = (x1, y1, z1, w1) en homogneas.
Veremos ms adelante en este captulo como este convenio nos permite operar
transformando un punto en otro punto, y un vector en otro vector, sin tener que efectuar
una operacin diferente en cada caso.
En el caso que W sea diferente que 1 0, tendremos que efectuar una sencilla
operacin para transformar un punto homogneo en uno cartesiano.
Si tenemos el punto:
Es decir, que normalizamos cada una de las componentes XYZ del punto por su
componente W. En el caso de W = 1 no hay que hacer nada pues la divisin es obvia,
pero puede pasar que nos interese variar W y entonces no podremos usar las XYZ hasta
haberlas normalizado segn os acabo de explicar.
3.2. Escalar
3.3. Trasladar
3.4. Rotar
Seguimos con el mismo criterio que antes. El cubo rojo sigue esttico en el
origen. El cubo verde tiene exactamente las mismas dimensiones pero se ha rotado 45
grados alrededor del eje vertical, que en este caso es el eje Y y no el Z al que tanto nos
hemos acostumbrado desde siempre. La matriz a emplear est en la figura 9.
Respecto al cambio del eje Y con el Z, hemos de pensar que en grficos siempre
se utiliza la Z como unidad de profundidad, y no de altura. Incluso ms adelante
hablaremos del Z buffer, o buffer de profundidad de unos polguonos respecto de los
otros para que no se solapen (para eliminar las superfcies ocultas)
Otro aspecto a considerar, es el signo del ngulo cuando vamos a efectuar a una
rotacin. Existe una convencin establecida para cuando hablemos de ello:
Es decir:
En ella podis ver como si miramos haca el origen a travs del eje de las X, una
rotacin contra-reloj es la indicada. Pues esa rotacin se considera positiva. As pues si
digo que voy a rotar 30 grados CCW alrededor del eje X me refiero a que rotar 30
grados siguiendo la direccin y el eje de la figura.
3.5. Deformar
Se entiende que los valores Sxy, Sxz, Syx, etc son escalares reales, es decir
nmeros, que vosotros mismos deberis escoger para conseguir el efecto deseado.
Lo cul equivale a decir que el orden de las matrices afecta al resultado final,
es decir, a la posicin y orientacin de nuestro objeto geomtrico 3D. Una prueba
grfica es la siguiente figura:
Se observa con toda claridad que el resultado de aplicar las mismas dos
transformaciones pero con el orden cambiado da como resultados dos bien distintos.
Como siempre el cubo rojo y centrado es el inicial y el verde el resultado final. En el
primer caso hemos aplicado una traslacin de 30 a lo largo de X y despus una rotacin
de 45 alrededor del Y. En el segundo caso primero rotamos los 45 alrededor del
mismo eje y despus trasladamos 30 unidades siguindo el eje X.
Pero ojo que en ambos casos tenemos que multiplicar las matrices como siempre
nos han enseado, es decir, de izquierda a derecha. Slo hay que fijarse en la
convencin que se usa porque eso define que forma tienen nuestros puntos, por que lado
los he de multiplicar y en que orden debo ir aadiendo las transformaciones.
3.8. Implementacin
Una vez hecho esto podemos acumular transformaciones sucesivas mediante las
funciones:
Rotar el ngulo segn el eje que define el vector (vx, vy, vz)
glRotatef(GLfloat angulo, GLfloat vx, GLFloat vy, GLfloat vz);
GlScalef()
GlRotatef()
GlTranslatef()
(...)
glRotatef... // afectar a toda la geometra que dibuje a partir de ahora
glTranslatef... // afectar a toda la geometra que dibuje a partir de ahora
glPushMatrix( ); // salvo el estado actual de la matriz, es decir, las 2
// transformaciones anteriores
glTranslatef... // afectar a slo a la geometra que dibuje antes del glPop
glScalef... //afectar a slo a la geometra que dibuje antes del
//glPop
dibujo_geometra_especfica( ); // Render de la geometra que pasar
// por 4 transformaciones
glPopMatrix( ); // recupero el estado de la matriz anterior
dibujo_el_resto( ); // Render de la geometra que pasar por 2 transformaciones
(...)
Por ltimo comentar que tambin podemos crearnos matrices "a mano" para
despus pasarlas a la matriz de transformacin de OpenGL. No disponemos tan slo de
las funciones de traslacin, rotacin... que os he comentado sin que tambin podemos
usar:
glLoadMatrixf(puntero_a_matriz);
glMultMatrixf(puntero_a_matriz);
GLfloat M[4][4];
Es importante saber que OpenGL asume que la matriz que se le pasar est
definida por columnas, es decir:
|a0 a4 a8 a12|
|a1 a5 a9 a13|
|a2 a6 a10 a14|
|a3 a7 a11 a15|
4. Proyecciones.
Tras las transformaciones homogneas para nuestra geometra ya somos capaces
de modelar y situar objetos a lo largo y ancho de nuestra escena virtual. Pero cmo
hacer que aparezcan en nuestra ventana del Sistema Operativo? La ventana es 2D y en
cambio est mostrando geometria 3D dando sensacin de que hay profundidad.
Proyecciones planares
Paralelas
Oblicua
Ortogrfica
Perspectiva
1 punto
2 puntos
3 puntos
Como proyeccin paralela que es, cuenta con proyectores paralelos entre ellos.
El centro de proyeccin (COP) se encuentra en el infinito. En el caso de la ortogrfica,
los proyectores son perpendiculares al plano de proyeccin. Lo observamos mejor en la
figura:
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(x_min, x_max, y_min, y_max, z_min, z_max);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective( FOV en grados, Relacin de Aspecto, z_near, z_far);
Las distancias NEAR y FAR son siempre positivas y medidas desde el COP
hasta esos planos, que sern obviamente paralelos al plano Z = 0. Dado que la cmara
apunta por defecto en la direccin negativa de Z, el plano de front (near) estar
realmente situado en z = -zmin mientras que el de back (far) estar en z = -zmax.
Hemos de pensar que no hay que proyectar toda la geometra existente sin tan
slo la que v la cmara desde su posicin y orientacin. Tenemos que acotar en este
sentido y es por eso que adems de definir un plano de proyeccin y una forma de
proyectar, creamos un volmen de visualizacin finito con unas fronteras bin
marcadas. Todo aquello que no se encuentre dentro del volmen ser rechazado y no
proyectado dado que no debe verse.
4.4. La Cmara
La cmara son nuestros ojos virtuales. Todo lo que ella vea ser proyectado,
discretizado y finalmente mostrado en nuestra ventana del sistema operativo. Podemos
imaginar que de la cmara emana el volmen de visualizacin de forma que se traslada
con ella. Los parmetros a definir en cuanto a la cmara son:
Orientacin. Una vez situada debe orientarse. Yo puedo estar quieto pero girar
la cabeza y mirar haca donde me venga en gana.
glMatrixMode(GL_MODELVIEW);
5. La librera GLUT
En este apartado veremos el funcionamiento de la librera GLUT. Esta librera,
que se sirve de funciones de OpengGL, aade funcionalidades tales como creacin de
ventanas, control de eventos de entrada, creacin de mens, etc. Su simplicidad de uso
la hace idnea para aplicaciones pequeas, donde tan solo queremos experimentar con
OpenGL, sin tener que complicar el cdigo para crear ventanas o controlar el teclado
con la API del Sistema Operativo.
caso negro. */
glClear(GL_COLOR_BUFFER_BIT);
/* Queremos que se dibujen las caras frontales de los polgonos y
con relleno de color. */
glPolygonMode(GL_FRONT, GL_FILL);
glBegin(GL_POLYGON);
/* Color azul para el primer vrtice */
glColor3f(0.0, 0.0, 1.0);
glVertex3i(-100, -100, 5);
/* Color verde para el segundo vrtice */
glColor3f(0.0, 1.0, 0.0);
glVertex3i(-100, 100, 5);
/* Color rojo para el tercer vrtice */
glColor3f(1.0, 0.0, 0.0);
glVertex3i(100, 100, 5);
/* Color amarillo para el cuarto vrtice */
glColor3f(1.0, 1.0, 0.0);
glVertex3i(100, -100, 5);
glEnd();
}
/*
* Main del programa.
*/
int main(int argc, char **argv)
{
/* Primera llamada siempre en OpenGL, por si usramos la lnea de
comandos */
glutInit(&argc, argv);
/* Activamos buffer simple y colores del tipo RGB */
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
/* Definimos una ventana de medidas ANCHO x ALTO como ventana de
visualizacin */
glutInitWindowSize (ANCHO, ALTO);
/* Posicionamos la esquina superior izquierda de la ventana en el
punto definido */
glutInitWindowPosition (ORIGENX, ORIGENY);
/* Creamos literalmente la ventana y le adjudicamos el nombre que
se observara en su barra de titulo */
glutCreateWindow("Cuadrado Multicolor");
/* Inicializamos el sistema */
inicio();
/* Hacemos saber a OpenGL que cada vez que sea necesario dibujar
de nuevo,
* por ejemplo la primera vez, o al redimensionar la ventana con
el ratn o
* en caso de provocar un "redibujado" por programa, debe llamar
a la
* funcin "dibujar".
*/
glutDisplayFunc(dibujar);
/* Aqu espera el programa mientras nada ocurra, es un Loop
* infinito que se vera turbado por las sucesivas veces que
* sea necesario redibujar.
*/
glutMainLoop();
/* ANSI C requiere que main retorne un entero. */
return 0;
}
Ahora hay que decirle al motor grfico como queremos renderizar, es decir, si
hay que refrescar la pantalla o no, que buffers hay que activar/desactivar y que
modalidad de colores queremos usar. En este caso no tenemos doble buffer
(GLUT_DOUBLE) y por tanto definimos un buffer de render nico con la constante
GLUT_SINGLE. Por otra parte, tenemos dos maneras de colorear, tenemos los colores
indexados, con una paleta de colores, o bien tenemos la convencin RGB. En nuestro
caso le decimos al subsistema grfico que cada color a aplicar ser definido por tres
valores numricos, uno para el rojo (Red), otro para el verde (Green) y otro para el azul
(Blue). Para esto usamos la constante GLUT_RGB. Sino usaramos GLUT_INDEX.
Con buffer simple contamos tan solo con un rea de memoria que se redibuja
constantemente. Esto no es factible para aplicaciones donde la velocidad de render es
muy alta o el contenido grfico es elevado. En nuestro caso slo definimos un polgono
y por lo tanto nos conformaremos con el buffer simple. De todas formas casi siempre
utilizaremos el buffer doble, dos zonas de memoria que se alternan de manera que se
dibuja primero una y en la siguiente iteracin se dibuja la otra. Ahora GLUT nos
permite definir las medidas de nuestra ventana de visualizacin. Estamos definiendo
literalmente el ANCHO y ALTO de nuestra ventana en pixels.
Una vez ya definido como "renderizar", con que medidas de ventana y en que
posicin fsica de la pantalla, crearemos la ventana. Para ello le damos un nombre
cualquiera que sera el ttulo que aparecera en esta. De hecho a la funcin se le pasa un
array de caracteres, ya sea explcito o sea el nombre entre comillas, o bien implcito, es
decir, una variable que contiene el nombre:
Hasta aqu ya tenemos una ventana en pantalla, con su ttulo y fondo negro por
defecto, ya que si os fijis no hemos especificado an otro. Ya podemos empezar a
"generar" grficos para colocarlos en ella. Creamos una funcin llamada Inicio() que se
encargar de inicializar todo lo necesario:
inicio ();
glutDisplayFunc (dibujar);
glutMainLoop ();
return 0;
}
GlMatrixMode (GL_PROJECTION);
glLoadIdentity ();
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();
Z = 5.0
Las constantes ANCHO y ALTO las hemos definido al principio del programa y
indican la altura y la anchura de la ventana.
Para acabar con las inicializaciones le diremos a OpenGL con que color
deseamos que se reinicialice el frame buffer cada vez que haya que volver a dibujar, o
lo que es lo mismo, que color debe aparecer en todas aquellas reas donde no dibujemos
nada, es decir, el color del fondo. Definimos que queremos el color de fondo negro para
nuestra ventana.
Sabemos que OpenGL llamara a la funcin DIBUJAR cada vez que necesite
"renderizar" de nuevo. En nuestro caso hacemos lo siguiente:
Una vez declarada la funcin, que como vis ni espera ni retorna nada, le
decimos a OpenGL que restaure el frame buffer. Esto es obligado para un correcto
proceso de render. De hecho estamos "reinicializando" la ventana de visualizacin con
el color definido anteriormente en glClearColor (0.0, 0.0, 0.0, 0.0);. De hecho nosotros
definimos el negro como valor para el fondo pero podramos no haberlo hecho ya que se
adopta este color por defecto.
glClear (GL_COLOR_BUFFER_BIT);
glBegin (GL_POLYGON);
el tercero es rojo....
y el cuarto es amarillo....
glEnd ();
}
Cada vez que OpenGL redibuje la escena vendr aqu y volver a ejecutar este
cdigo.
donde como ya he dicho (Xm, Ym) son las coordenadas 2D del punto en el
mundo y (X1,Y1), (X2, Y2) las observis en la figura como lmites de la escena real.
Entonces mapeamos de coordenadas unitarias a pantalla. Llamemos a las coordenadas
de pantalla (Xp, Yp):
Xp = W * Xu;
Yp = H * (1 - Yu);
5.4.2.Viewports
Un evento es "algo que el usuario puede hacer" como por ejemplo maximizar
una ventana, redimensionarla, pulsar el botn izquierdo del ratn, o usar una
determinada combinacin de teclas. En todos estos casos deberemos "ejecutar algo de
cdigo" dentro de nuestro programa, si es que estaba previsto as.
6.1. El ratn
glutMouseFunc( ControlRaton );
OpenGL entiende que cada vez que se pulse uno de los botones del ratn debe
llamar a una rutina llamada ControlRaton, que por supuesto tenemos que crear y
definir nosotros mismos. Lo haremos de esta forma:
...donde los parmetros que la funcin nos da (automticamente y sin tener que
hacer nada) son los siguientes:
estn predefinidas y podemos usarlas sin problema en nuestro cdigo pues GLUT las
interpretar correctamente.
state, puede tomar los valores GLUT_UP o GLUT_DOWN, segn si se ha
pulsado/soltado el correspondiente botn.
X e Y, son las coordenadas referidas a la ventana de visualizacin, no al mundo
virtual, en las que se puls/solt el susodicho botn.
Es importante aclarar que GLUT espera que los parmetros sean stos en el caso
de este callback, y no otros. De otra manera el programa no nos compilar.
Veamos un ejemplo:
En este caso, cuando el usuario pulse el botn izquierdo del ratn, sacaremos un
mensaje diciendo que se cierra la aplicacin y entonces la cerraremos. La funcin exit
(0) pertenece a ANSI C, no a OpenGL, y provoca el fin de la ejecucin.
En el caso de la funcin:
glutMotionFunc(ControlMovimientoRaton);
teniendo en cuenta que X e Y son las coordenadas de pantalla por las que el ratn est
pasando. As podramos usar esta funcin para indicar nuestra situacin en pantalla de la
siguiente forma:
void ControlMovimientoRaton( GLsizei x, GLsizei y )
{
printf( "Posicin del ratn en coordenadas de ventana es:\n");
printf( " X = %f\n", (GLfloat)GLsizei x);
printf( " Y = %f\n", (GLfloat)GLsizei y);
}
6.2. El teclado
glutKeyboardFunc( ControlTeclado );
Por ejemplo supongamos que podemos "movernos" por nuestro mundo virtual.
De una forma un tanto simple y primitiva, y asumiendo que el plano del suelo se
corresponde con Y=0, tendramos una situacin como sta:
glutReshapeFunc( ControlVentana );
que como siempre aadimos a nuestra funcin MAIN. Falta definir la funcin de
control:
6.4. MUI
Un trabajador de SGI ( Silicon Graphics ) llamado Tom Davis codific una
pequea librera a partir de GLUT llamada MUI (Micro User Interface). Lo hizo para
usarla l mismo en un proyecto interno de empresa pero dada su facilidad y versatilidad
de uso la incluyo de forma totalmente gratuita con GLUT.
Se trata de una serie de funciones que podemos usar fcilmente para crear
ventanas con botones, barras de desplazamiento, casillas de seleccin y
verificacin....todo al estilo Motif/Windows que tanto nos gusta y sabemos manejar.
Esta librera puede obtenerse juntamente con GLUT a partir de la versin 3.5 de ste.
6.5. Mens
Como veis tenemos dos niveles de men en la primera opcin mientras que el
resto son opciones de un slo nivel. Esto lo codificaramos as:
glutAddSubMenu("Ver", submenu);
que le dice al men de primer nivel que la primera de sus opciones se llama Ver
y debe llamar a un men asociado al entero submenu. Mirad que este entero lo hemos
asociado usando:
submenu = glutCreateMenu(menu_nivel_2);
y por supuesto este segundo nivel tambin dispone de sus propias opciones,
creadas de igual manera que antes. Por ltimo le decimos a GLUT que este men debe
asociarse a pulsar el botn derecho del ratn con:
glutAttachMenu(GLUT_RIGHT_BUTTON);
7. Iluminacin
Hasta ahora hemos trabajado con imgenes planas (FLAT). Con esto quiero
decir que cada una de las caras de nuestros objetos geomtricos ha contado con un nico
color. Pero, respecto al realismo, es evidente que puede mejorar y mucho. En eso vamos
a trabajar en este captulo.
Una vez calculada la normal tenemos que normalizar, es decir, dividir ese
vector por su propio mdulo para que sea unitario. De esta forma tenemos un vector
normal de mdulo igual a la unidad que es lo que OpenGL necesita.
OpenGL utilizar la normal asociada a cada vrtice para evaluar la luz que
incide sobre ste. Si un vrtice pertenece a ms de una cara, podemos o bien promediar
para obtener unos clculos correctos por parte de OpenGL, o bien repetir los vertices,
cada uno con la normal que le corresponde. Para definir normales con OpenGL ...
glBegin ( GL_POLYGON ) ;
glNormal3f ( CoordX, CoordY, CoordZ ) ;
glVertex3f ( ... ) ;
glVertex3f ( ... ) ;
glVertex3f ( ... ) ;
glVertex3f ( ... ) ;
...
glEnd( ) ;
glBegin ( GL_POLYGON ) ;
glNormal3f ( CoordX, CoordY, CoordZ ) ;
glVertex3f ( ... ) ;
glNormal3f ( CoordX, CoordY, CoordZ ) ;
glVertex3f ( ... ) ;
glNormal3f ( CoordX, CoordY, CoordZ ) ;
glVertex3f ( ... ) ;
glNormal3f ( CoordX, CoordY, CoordZ ) ;
glVertex3f ( ... ) ;
...
glEnd( ) ;
glShadeModel ( GL_FLAT ) ;
glShadeModel ( GL_SMOOTH ) ;
En este caso OpenGL si efectua clculos de color para cada uno de los puntos
del polgono. Se asocian las normales a los vrtices, OpenGL clcula los colores que
stos deben tener e implementa una interpolacin de colores para el resto de puntos. De
Antes de empezar a activar luces como unos cosacos tenemos que definir
nuestros materiales. Para cada polgono de la escena hay que definir un material de
forma que su respuesta a la incidencia de luz vare segn sea el caso.
Por tanto tenemos que decirle a OpenGL de que forma tendr que tratar a cada
trozo de geometra.
const GLfloat
GLenum face GLenum pname
*params
GL_FRONT GL_DIFFUSE ( R, G, B, 1.0 )
GL_BACK GL_AMBIENT ( R, G, B, 1.0 )
GL_FRONT_AND_BACK GL_AMBIENT_AND_DIFFUSE ( R, G, B, 1.0 )
GL_EMISSION ( R, G, B, 1.0 )
GL_SPECULAR ( R, G, B, 1.0 )
GL_SHININESS [ 0, 128 ]
Valores tpicos, son los usados por defecto, son de 0.8 para las tres componentes
en GL_DIFFUSE, de 0.2 para GL_AMBIENT y de 0.0 en GL_EMISSION y
GL_SPECULAR. Por supuesto tendremos que retocar estos valores hasta conseguir el
efecto deseado.
Mas adelante veremos el cuarto valor, el 1.0, que se refiere al valor del canal
alfa del color RGB, que veremos mas adelante quando hablemos de transparencias.
7.4. Luces
as sucesivamente. Para activar / desactivar una de ellas (en este caso la cuarta luz, es
decir, la nmero 3) :
glEnable ( GL_LIGHT3 ) ;
glDisable ( GL_LIGHT3 ) ;
7.4.1.Caracterstica ambiental
De esta forma:
Utilizaremos:
Se trata de la luz que viene de una direccin particular y rebota sobre un objeto
siguiendo una determinada direccin. Es la componente responsable de las zonas ms
brillantes en la geometra, de los "highlights".
Tenemos que especificar dnde queremos colocar cada una de las fuentes de luz.
Para ello utilizamos la funcin de siempre:
Por defecto la luz se encuentra en (0.0, 0.0, 1.0, 0.0) iluminando en la direccin
negativa de las Z's. Los rayos de la luz se asumen paralelos.
Podemos mover una luz a gusto por una escena. Incluso podemos movernos con
ella como si fuera una linterna. Para ello tan slo tenemos que considerarla como un
objeto 3D ms que se ve afectado por cambios en la matriz de transformacin
"MODEL-VIEW" de OpenGL. Podemos rotarla, trasladarla, escalar su posicin....como
si de un polgono se tratara.
7.4.5. Ms parmetros
En cuanto a 2 me estoy refiriendo al rea que abarca el haz de luz que surge de
la fuente. Podemos definir los siguientes parmetros:
8. Texturizacin
En este captulo veremos un aspecto bsico si queremos que una escena
contenga un mnimo de realismo: La texturizacin. Por texturizacin entendemos en el
proceso de asignar una imagen (bitmap) a un polgono, de manera que en lugar de ver
este de un color plano, o un gradiente de colores, veremos la imagen proyectada en l.
GLuint idTextura;
glGenTextures(1, &idTextura);
glBindTexture(GL_TEXTURE_2D, idTextura);
Pero veamos todos los parmetros, viendo los parmetros de esta funcin uno
por uno:
void glTexImage2D(
GLenum tipoTextura,
GLint nivelMipMap,
GLint formatoInterno,
GLsizei ancho,
GLsizei alto,
GLint borde,
GLenum formato,
GLenum tipo,
const GLvoid *pixels
);
free(textura);
Eso quiere decir que lo que veremos sern cuadrados de 16x12, representando
cada uno un texel de la textura. Visualmente queda muy poco realista ver una textura
pixelizada, de manera que le aplicamos filtros para evitarlo. El ms comn es el filtro
lineal, que se basa en hacer una interpolacin en cada pxel en pantalla que dibuja. A
pesar de que pueda parecer costoso a nivel computacional, esto se hace por hardware y
no afecta en absoluto al rendimiento. Veamos como lo podemos implementar:
Con esto estamos parametrizando dos filtros. Uno para cuando la textura se
representa ms grande de lo que es en realidad (el ejemplo que hemos comentado) y
otro para cuando la textura es mas pequea: GL_TEXTURE_MAG_FILTER y
GL_TEXTURE_MIN_FILTER, respectivamente. En los dos le decimos que haga un
filtro lineal. Tambien podriamos decirle que no aplicara ningn filtro (GL_NEAREST),
como podemos ver en la siguiente imagen:
Ahora que ya tenemos las texturas cargadas y ajustadas a nuestro gusto, veamos
ahora cmo podemos dibujar polgonos con texturas aplicadas. Supongamos que
queremos dibujar un simple cuadrado, con la textura que hemos cargado anteriormente.
Si lo dibujamos sin textura seria:
glBegin (GL_QUADS);
glVertex3i (-100, -100, 5);
glVertex3i (-100, 100, 5);
glVertex3i (100, 100, 5);
glVertex3i (100, -100, 5);
glEnd ();
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, idTextura);
glBegin (GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
glVertex3i (-100, -100, 5);
glTexCoord2f(1.0f, 0.0f);
glVertex3i (-100, 100, 5);
glTexCoord2f(1.0f, 1.0f);
glVertex3i (100, 100, 5);
glTexCoord2f(0.0f, 1.0f);
glVertex3i (100, -100, 5);
glEnd ();
glDisable(GL_TEXTURE_2D);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, idTextura);
glBegin (GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
glVertex3i (-100, -100, 5);
Aqu vemos algo que no estaba antes, nos referimos a glTexCoord2f(). Esta
funcion nos introduce un concepto nuevo: las coordenadas de textura, y nos referiremos
a ellas generalmente como s y t, siendo s el eje horizontal y t el vertical. Se usan
para referirnos a una posicin de la textura. Generalmente se mueven en el intervalo [0,
glDisable(GL_TEXTURE_2D);
Hemos estado hablando de texturizar una superficie, pero, que pasa con el color?
Realmente podemos seguir usndolos, es mas, OpenGL texturizar y colorear a la vez.
Podemos activar el color rojo, y todas las texturas que se dibujen a partir de entonces,
saldrn tintadas de ese mismo color. Del mismo modo con las luces. No hay ningn
problema en combinar colores, luces y texturas a la vez. Si no queremos iluminacin
alguna, basta con no activar las iluminacin, y si no queremos que las texturas salgan
tintadas de algn color, basta con definir el color activo como el blanco.
9. Buffers de OpenGL
En este captulo veremos la utilidad de los Buffers disponibles en OpenGL, su
utilidad y modo de hacerlos servir. En concreto veremos el buffer de color (el buffer que
usamos para dibujar) el de profundidad o zbuffer, el stencil buffer y el buffer de
acumulacin.
Cada buffer tiene propiedades especficas que van ms all que el simple doble
buffer para animacin y un buffer de ocultacin para eliminar caras ocultas. En OpenGL
un buffer es esencialmente una matriz bidimensional de valores que se corresponden
con un pxel en una ventana o en una imagen fuera de pantalla. Cada buffer tiene el
mismo nmero de filas y columnas que el rea cliente actual de la ventana, pero
mantiene un tipo y rango de valores distinto.
El buffer de color contiene informacin de color de los pxels. Cada pxel puede
contener un ndice de color o valores rojo/verde/azul/alfa que describen la apariencia de
cada pxel. Los pxels RGBA se visualizan directamente en pantalla utilizando el color
ms prximo disponible. La apariencia de los pixels de color con ndice viene
determinada por el valor asociado a este ndice en una tabla de color RGB.
Buffer Descripcin
GL_FRONT Dibujamos slo en el buffer de color frontal (visible).
GL_BACK Dibujamos slo en el buffer de color trasero (oculto).
GL_FRONT_AND_BACK Dibujamos en ambos buffers de color.
OpenGl soporta doble buffer, pero no hay ninguna funcin para intercambiar los
buffers frontal y oculto. Afortunadamente, cada sistema de ventanas con OpenGL
soporta una funcin para hacerlo. Bajo windows, esta llamada es:
SwapBuffers(hdc);
Nombre Funcin
GL_NEVER Siempre false.
GL_LESS True si Z de referencia<Z de profundidad.
GL_EQUAL True si Z de referencia=Z de profundidad.
GL_LEQUAL True si Z de referencia<=Z de profundidad.
GL_GREATER True si Z de referencia>Z de profundidad.
GL_NOTEQUAL True si Z de referencia!=Z de profundidad.
GL_GEQUAL True si Z de referencia=>Z de profundidad.
GL_ALWAYS Siempre true.
glDepthFunc(funcin);
glDepthRange(cerca, lejos);
Los parmetros cerca y lejos son valores de coma flotante entre 0.0 y 1.0, que
son los valores por defecto. Normalmente, cerca tiene un valor inferior a lejos, pero
podemos invertir sto para obtener efectos especiales ( o utilizar las funciones
GL_GREATER y GL_GEQUAL). Al reducir el rango de valores almacenados en el
buffer de profundidad, no modificamos el volumen de trabajo, pero reducir la precisin
del buffer de profundidad y puede llevar a errores en la eliminacin de caras ocultas.
glClearDepth(profundidad);
glEnable(GL_STENCIL_TEST);
Sin esta llamada, todas las operaciones del buffer de estarcido estn
desactivadas.
Nombre Funcin
GL_NEVER La verificacin de estarcido siempre falla (no se dibuja).
GL_LESS Pasa si el valor de referencia es menor que el valor de estarcido.
GL_EQUAL Pasa si el valor de referencia es igual que el valor de estarcido.
Pasa si el valor de referencia es menor o igual que el valor de
GL_LEQUAL
estarcido.
GL_GREATER Pasa si el valor de referencia es mayor que el valor de estarcido.
GL_NOTEQUAL Pasa si el valor de referencia no es igual que el valor de estarcido.
Pasa si el valor de referencia es mayor o igual que el valor de
GL_GEQUAL
estarcido.
Por defecto. La verificacin de estarcido siempre pasa (siempre se
GL_ALWAYS
realiza una operacin de dibujo).
Operacin Descripcin
GL_KEEP Mantiene los contenidos actuales del buffer de estarcido.
GL_ZERO Establece el valor cero en el buffer de estarcido.
GL_REPLACE Establece el valor de la funcin de referencia en el buffer de estarcido.
GL_INCR Incrementa el valor actual del buffer de estarcido.
GL_DECR Decrementa el valor actual del buffer de estarcido.
GL_INVERT Invierte el orden binario del valor actual del buffer de estarcido.
Normalmente se utiliza una mscara para perfilar el rea en la que tiene lugar el
dibujo. Aqu tenemos un ejemplo de cmo dibujar una mscara en el buffer de
estarcido:
glStencilFunc(GL_ALWAYS, 1, 1);
glStencilFunc(GL_EQUAL, 1, 1);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
Dado que tiene efecto con todas las funciones de dibujo de OpenGL, incluido
glBitMap, podemos usar el buffer de estarcido para crear efectos especiales de agujero
en animaciones.
Operacin Descripcin
GL_ACCUM Aade valores de color escalados al buffer de acumulacin.
Abre valores de color escalados en el buffer de acumulacin,
GL_LOAD
sustituyendo los que hubiera antes.
GL_ADD Aade un color constante a los valores del buffer de acumulacin.
Multiplica los valores de color en el buffer de acumulacin por un color
GL_MULT
constante (efectos de filtro).
GL_RETURN Copia el buffer de acumulacin en el buffer de color principal.
glAccum(GL_RETURN, 1.0);
10.1. Niebla
glFogi(GL_FOG_MODE, GL_LINEAR);
glFogi(GL_FOG_MODE, GL_EXP);
glFogi(GL_FOG_MODE, GL_EXP2);
Una vez que hemos elegido el tipo de niebla, debemos elegir un color para la
niebla que se mezclar con nuestra escena usando las funciones glFogfv o glFogfi:
GLfloat color_de_niebla[4]={r,g,b,a};
glFogfv(GL_FOG_COLOR, color_de_niebla);
GLint color_de_niebla[4]={r,g,b,a};
glFogfi(GL_FOG_COLOR, color_de_niebla);
glFogf(GL_FOG_DENSITY, densidad);
El parmetro densidad puede ser cualquier nmero mayor que 0.0, pero
normalmente se mantiene por debajo de 0.1. La siguiente figura muestra cmo afecta la
densidad de la niebla a la cantidad de color empleado.
Por defecto, la niebla se aplica a todas las distancias entre 0.0 y 1.0. Los
parmetros GL_FOG_START y GL_FOG_END restringen el rango de valores de
profundidad usados en los clculos de niebla. Esto se utiliza normalmente para definir
con precisin la densidad de la niebla cuando el rea inmediatamente cercana al
observador no est cubierta.
10.2. Transparencias
mezcla de color, que se especifican en las siguientes tablas. Por defecto, estos
argumentos son GL_ONE y GL_ZERO, respectivamente, lo que equivale a
glDisable(GL_BLEND).
Rd = Rs * As + Rd * (1 - As)
Gd = Gs * As + Gd * (1 - As)
Bd = Bs * As + Bd * (1 - As)
Dado que slo se emplea la componente alfa del color de origen, no necesitamos
una tarjeta grfica que soporte planos de color alfa en el buffer de color. Algo que
tenemos que recordar con la transpariencia por mezcla alfa es que la verificacin normal
de profundidad puede interferir con el efecto que tratamos de conseguir. Para
asegurarnos de que las lneas y polgonos transparentes se dibujan apropiadamente,
debemos dibujarlos siempre de atrs a adelante. Aqu tenemos un ejemplo del uso de
transpariencias que dibuja una tetera opaca que se ve a travs de otra transparente.
10.3. Antialiasing
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_LINE_SMOOTH);
glEnable(GL_POINT_SMOOTH);
glEnable(GL_POLYGON_SMOOTH);
glAccum(GL_ACCUM, 0.05);
};
/*Muestra la escena final*/
glAccum(GL_RETURN, 1.0);
Fijmonos en que no tenemos que usar glClear para inicializar los contenidos del
buffer de acumulacin, como hacemos con los buffers de color, profundidad y estarcido.
En su lugar, la mayora de las veces utilizaremos glAccum(GL_LOAD, s) en el primer
cuadro de la escena.
Hay dos cosas que debemos intentar hacer siempre en una aplicacin grfica:
Enviar el mnimo posible de informacin por el bus.
Enviar el mayor numero de informacin posible.
Una vez tengamos los arrays hechos debemos usar la siguiente estructura para el
pintado.
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3,GL_FLOAT, 0, VAVertex);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2,GL_FLOAT, 0, VACoordTex);
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(3,GL_FLOAT, 0, VAColor);
glDrawArrays(GL_TRIANGLES, 0, 3*numFaces);
79
Introduccin a la programacin grfica con OpenGL Optimizaciones del render
Una vez especificados los tres arrays podemos decir a OGL que pinte. Esto lo
hacemos con la ltima instruccion en la que indicamos:
Que es lo que queremos dibujar
Desde que posicion de los arrays vamos a empezar a dibujar
Hasta que posicin de los arrays vamos a dibujar.
Hasta ahora hemos hablado de cmo renderizar escenas usando todas las
primitivas de OpenGL. Hemos comentado el concepto de clipping, es decir, dada una
cmara y su ngulo de obertura, todo lo que la cmara que no ve se descarta, y no se
dibuja. No obstante, este proceso implica una serie de clculos. Si tenemos una escena
muy compleja al nivel de geometra, a cada fotograma estamos procesndola toda, para
luego dibujar solo una pequea parte. Esto realmente es muy poco ptimo, estamos
obligando a OpenGL a comprobar si todos y cada uno de los tringulos de la escena
estn dentro de la pirmide de visualizacin.
11.2.1.Quad-Trees
Una variacin de los Quad Trees serian los Octrees, basados en la misma idea,
pero en lugar de formar una matriz bidimensional, formaramos una matriz
tridimensional.
zonas que estn dentro del volumen de visualizacin de la cmara tendra un coste
logartmico.
11.2.3. Portales
Esta tcnica es una de las ms simples, y a la vez, una de las ms efectivas. Solo
es viable si nuestro escenario se puede dividir en habitaciones. A cada habitacin
asignaramos portales, que podra ser un rectngulo posicionado en cada una de las
puertas, ventanas, etc. que comunican una habitacin con otra. De este modo, al
renderizar, si sabemos que estamos en una determinada habitacin, y renderizamos uno
de los portales, sabemos que tendremos que dibujar la habitacin con la cul comunica.
Podemos ir mas all, y proyectar nuestra pirmide de visin a travs del portal, y
dibujar solo la geometra de la otra habitacin que est dentro de la pirmide
proyectada. No obstante, este proceso no vale la pena, ya que este simple clculo ya es
mas costoso que dibujar la otra habitacin entera.
11.2.4. PVS