Facultad de Ingeniería de Sistemas e Informática Escuela académico profesional de Ingeniería de Software UNMSM Ciclo 2015 – I Manual de OpenGL CURSO DE COMPUTACIÓN GRÁFICA 1 Facultad de Ingeniería de Sistemas e Informática Escuela académico profesional de Ingeniería de Software UNMSM Ciclo 2015 – I Indice Introducción ........................................................................................................................................ 3 Conceptos previos ............................................................................................................................... 4 Funcionamiento de OpenGL ............................................................................................................... 6 Primitivas de dibujo ............................................................................................................................ 9 Transformaciones.............................................................................................................................. 16 Estructura de un programa OpenGL ................................................................................................. 23 Compilación OpenGL ......................................................................................................................... 26 2 Facultad de Ingeniería de Sistemas e Informática Escuela académico profesional de Ingeniería de Software UNMSM Ciclo 2015 – I Introducción OpenGL es una interface de software para el hardware grafico, esta interface consiste de una larga serie de comandos para manipulación de objetos y operaciones sobre estos los cuales permiten controlar la implantación 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 explícitamente el cambio, así las variables de estado de OpenGL que vamos a utilizar mas comúnmente son: Color (de dibujo y de borrado). Matrices de Transformación (GL_MODELVIEW, GL_PROYECTION). Patrones (de líneas y de relleno). Modo de dibujo de polígonos. Buffers de dibujo. Buffer de Stencil. Buffer de profundidad (z-Buffer). Buffer de acumulacion. 3 Facultad de Ingeniería de Sistemas e Informática Escuela académico profesional de Ingeniería de Software UNMSM Ciclo 2015 – I Conceptos previos 4 Facultad de Ingeniería de Sistemas e Informática Escuela académico profesional de Ingeniería de Software UNMSM Ciclo 2015 – I 5 Facultad de Ingeniería de Sistemas e Informática Escuela académico profesional de Ingeniería de Software UNMSM Ciclo 2015 – I 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, así 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 configuración 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 través 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 está en la lista se utiliza la instrucción glFlush(): esta instrucción obliga a pintar y no espera a que el hardawre termine para continuar con el programa, análogamente 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 instrucción 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. 6 Facultad de Ingeniería de Sistemas e Informática Escuela académico profesional de Ingeniería de Software UNMSM Ciclo 2015 – I Arquitectura gráfica: 7 Facultad de Ingeniería de Sistemas e Informática Escuela académico profesional de Ingeniería de Software UNMSM Ciclo 2015 – I 8 Facultad de Ingeniería de Sistemas e Informática Escuela académico profesional de Ingeniería de Software UNMSM Ciclo 2015 – I 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: Triángulos separados, cada 3 vértices hacen un triangulo. GL_TRIANGLE_STRIP: tira de triángulos unidos (similara quad_strip). 9 Facultad de Ingeniería de Sistemas e Informática Escuela académico profesional de Ingeniería de Software UNMSM Ciclo 2015 – I GL_TRIANGLE_FAN: Grupo de triangulos con un unico vertice comun a todos. Puntos Un punto se define mediante la función glVertex, esta función especifica las coordenadas del punto dentro de la ventana de visualización. Con esta función podremos definir puntos en dos y tres dimensiones, dependiendo del número de coordenadas que se detallan. OpenGL trabaja normalmente en coordenadas homogéneas representadas por cuatro componentes, (x, y, z, h), por lo tanto cuando estamos definiendo puntos en dos dimensiones el valor z coge el valor cero y h el valor 1, en tres dimensiones h coge el valor 1. El tipo especificado de coordenadas viene determinado a partir según los sufijos que siguen a la función glvertex. Los sufijos que pueden seguir a la función serán, d (double), indica que las coordenadas deben especificarse en valores double, f (float), i (integer) y finalmente s (short), por lo tanto las coordenadas deberán indicarse con valores que correspondan al sufijo. Existe la posibilidad de definir un punto mediante un vector que contenga las coordenadas, para ello deberemos utilizar el sufijo v indicando que es un vector de coordenadas. Gldouble v[3]= {4.5, 6.7, 23.8} glVertex(v); Para definir un punto o conjuntos de puntos debemos especificar las siguientes cláusulas glBegin(GL_POINTS) y la cláusula glEnd(), detallando entre ambas las posiciones de cada punto, así tenemos por ejemplo: glBegin(GL_POINTS) glVertex2f(50.4,34.6); //Punto 2D,en punto flotante glVertex3i(10,20 34); //Punto 3D, en enteros glEnd(); Por lo tanto para definir un conjunto de punto sobre la ventana de visualización podemos emplear la estructura anterior. Una vez definido los puntos podemos modificar su tamaño empleando la sentencia glPointSize(valor), donde valor representa el tamaño con el que deseamos dibujar el punto. Líneas 10 Facultad de Ingeniería de Sistemas e Informática UNMSM Escuela académico profesional de Ingeniería de Software Ciclo 2015 – I De la misma forma que definimos puntos podemos definir líneas. Para definir una línea se precisan dos puntos, estos dos puntos se definen de la misma forma que al definir un punto de la pantalla, es decir con la función glVertex. Existen diversas formas de definir líneas dependiendo del modo en que se describa en la cláusula glBegin(modo), modo representará el tipo de línea que deseamos. Modo Descripción GL_LINES Genera una serie de líneas que no se conectan entre sí. Las líneas se definen mediante los pares de puntos sucesivos, por lo tanto el número de vértices debe ser par, en el caso de que fuera impar se ignoraría Genera una serie de líneas pero que se conectan entre sí, es decir el punto final de una línea es el punto inicial de la siguiente. Con este modo se pueden generar figuras cerradas si el punto inicial coincide con el final. GL_LINE_STRIP GL_LINE_LOOP Genera una serie de líneas conectadas entre sí, es parecido al modo anterior pero este modo conecta automáticamente el punto inicial con el punto final. Podemos ver el efecto de los diferentes modos en el siguiente código: glBegin (MODO); glVertex2f(0.0, glVertex2f(1.0, glVertex2f(1.0, glVertex2f(0.0, glEnd; 0.0); 0.0); 1.0); 1.0); Gráficamente quedará de la siguiente forma, según el valor de MODO. GL_LINES GL_LINE_STRIP GL_LINE_LOOP 11 Facultad de Ingeniería de Sistemas e Informática UNMSM Escuela académico profesional de Ingeniería de Software Ciclo 2015 – I (0,1) (1,1) (0,1) (1,1) (0,1) (1,1) (0,0) (1,0) (0,0) (1,0) (0,0) (1,0) El aspecto de las líneas también pueden modificarse, pudiendo crear líneas más gruesas y con formato punteado, para ello se utilizará los comandos: glEnable(GL_LINE_STIPPLE); glLineStipple( factor, mascara); glDisable(GL_LINE_STIPPLE); Con estos comandos podemos conseguir líneas punteadas, la primera instrucción activa el modo de línea punteada, mientras que el segundo define el estilo de la línea, donde factor es un valor que está comprendido entre 1 y 255, este valor define la separación entre los trozos de la línea, mientras que máscara, es un valor de 16 bits que se describe en hexadecimal, cada máscara define un formato de línea, los valores van comprendidos entre 0x0000 hasta 0xAAAA. Otra posibilidad que tenemos es modificar el grosor de la línea, para ello utilizaremos la siguiente instrucción: glLineWidth(tamaño); Por defecto OpenGL define las líneas con tamaño 1.0, pero podemos modificarlo mediante la sentencia anterior. Hay que tener en cuenta que una vez activado el tamaño hay que volver a establecer si deseamos líneas con el tamaño por defecto. Polígonos OpenGL define los polígonos como secuencia de aristas, por lo tanto sigue con el mismo formato especificado en los puntos anteriores. Para generar polígonos tenemos los siguientes modos para la sentencia glBegin(modo), estos son: Modo Descripción GL_POLYGON Genera un simple poligono relleno con los vertices especificados. Para generar un poligono debemos tener en cuenta que se debe 12 Facultad de Ingeniería de Sistemas e Informática UNMSM Escuela académico profesional de Ingeniería de Software Ciclo 2015 – I garantizar tres cosas: Como mínimo se precisan 3 vertices. Las líneas no deben cruzarse. GL_TRIANGLES Los vertices deben formar un poligono convexo, en caso contrario OpenGL ignorará el vertice. Genera una serie de triangulos rellenos que no se conectan entre sí. El número de vertices debe ser multiplo de 3, si el total de vertices es menor de tres, OpenGL ignora los vertices que no forma un triángulo. Genera una serie de triángulos rellenos conectados entre sí, es decir dos de los vertices de un triángulo son los vertices del siguiente GL_TRIANGLE_STRIP triángulo. Debemos saber que con N vertices se pueden crear N-2 triángulos. De igual forma que anteriormente el número de vertices debe ser multiplo de tres, si no lo es se ignora aquellos que sobran. GL_TRIANGLE_FAN Genera un conjunto de triángulos rellenos conectados entre sí, con la caracteristica de que todos los triángulos tiene un vertice en común. El primer triángulo defien el vertice comun a todos los triángulos. De igual forma que los anteriores el número de vertices debe ser múltiplo de 3, si no lo es se ignora aquellos vertices que sobran. GL_QUADS Genera un conjunto de cuadrilateros rellenos sin conectar entre ellos. El número de vertices que se requiere es multiplo de cuatro, si no se verifica esto entonces OpenGL ignora los vertices que sobran. Cada cuatro veretices se describe un cuadrilatero. GL_QUAD_STRIP Genera un conjunto de cuadrilateros rellenos que se conectan entre sí, es decir dos vertices de un cuadrado se utilizan para generar el siguiente cuadrilatero. Hay que tener en cuenta que si tenemos un total de N vertices podremos obtener un total de N/2-1 cuadrados. El número de vertices debe ser multiplo de cautro, si no se verifica entonces los vertices que sobran son ignorados. Gráficamente podemos ver las diferencias entre los modos para genera polígonos: 13 Facultad de Ingeniería de Sistemas e Informática UNMSM Escuela académico profesional de Ingeniería de Software v2 Ciclo 2015 – I v0 v1 v1 v1 v4 v2 v5 v3 v5 v0 vo v3 v4 v2 GL_TRIANGLE_STRIP GL_TRIANGLES GL_POLYGON v1 v1 v1 v0 v0 v2 v0 v2 v3 v4 v7 v3 v4 v2 v3 v4 v6 GL_QUADS v5 v5 GL_QUAD_STRIP GL_TRIANGLE_FAN Dentro del par glBegin, glEnd solo pueden ir instrucciones OpenGL para definir objetos tales como vértices, 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 programación del lenguaje tales que ciclos, condicionales, llamados a funciones, etc. Siempre y cuando no usen alguna de las funciones OpenGL no permitidas, i.e.: 14 Facultad de Ingeniería de Sistemas e Informática Escuela académico profesional de Ingeniería de Software UNMSM Ciclo 2015 – I Muchas primitivas básicas tienen sufijos indicando el tipo del valor o variable que se le pasan como parámetro, asi como el número de parámetros, asi una instrucción como p.ej. glVertex puede tener 2,3 o 4 parámetros, así comor recibir tipos enteros, flotantes, etc. O un vector conteniendo esos parámetros Entonces en los manuales se escribe como: glVertex[234][sifd][v] quiere decir que se le pueden pasar 2,3 ó 4 parametros mas el tipo o un vector, como minimo debe tener el numero de parámetros 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 también se pueden usar para definir variables, estos son: Las primitivas más básicas de dibujo son entonces: glRect[sid][v]: dibuja un rectángulo, 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]: vértice de un punto, línea o polígono, 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 línea > 0.0, 1.0 por defecto. glLineStipple(factor,patron): factor = entero indicando un factor de escalamiento, patrón= short sin signo conteniendo el patrón de bits par pintar la línea (punteado,etc..).. glPolygonMode(cara,modo): cara corresponde al lado del polígono, 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 vértices (GL_POINT), o solo el borde (GL_LINE). 15 Facultad de Ingeniería de Sistemas e Informática Escuela académico profesional de Ingeniería de Software UNMSM Ciclo 2015 – I 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 Una vez que tenemos definida la ventana donde realizaremos la escena debemos definir como va a ser la visualización de esta escena, es decir, deberemos definir aspectos tales como, posición de la cámara, a que punto mirara la cámara, que tipo de proyección utilizará,... etc. En este apartado se mostrará como podemos manipular la cámara virtual, es decir el punto desde donde vemos la imagen, junto con las transformaciones de visualización. Estos dos puntos nos permitirán definir una visión de los objetos más realista. Otro de los apartados de esta sección será manipular las transformaciones de proyección, y poder ver los efectos sobre la imagen. Matriz de visualización Para poder manipular los objetos de la escena debemos tener definida una matriz de visualización, esta matriz será de cuatro por cuatro, es decir coordenadas normalizadas. Esta matriz se utiliza tanto en dos como en tres dimensiones, la diferencia esta en la coordenada z, en el caso de dos dimensiones z siempre tomará el valor cero, mientras que en tres dimensiones podrá tomar diferentes valores. OpenGl, facilita maneras para poder manipular la matriz de visualización. Antes de manipular la matriz directamente debemos inicializar el modo para la matriz de operaciones, esto se consigue con la función: glMatrixMode(GL_MODELVIEW); Una vez definido el modo de la matriz podemos asignarle un valor con las siguientes funciones: glLoadIdentity() Inicializada la matriz de transformaciones con la identidad. glLoadMatrix(M) Esta sentencia nos permite inicializar la matriz con un valor dado, especificado por el valor de M. M puede ser definido como una matriz de cuatro por cuatro o bien por un vector de dieciséis elementos, (m1, m2, m3,...). Hay que tener en cuenta que los valores de la matriz se especifica por 16 Facultad de Ingeniería de Sistemas e Informática UNMSM Escuela académico profesional de Ingeniería de Software Ciclo 2015 – I columnas, es decir la primera columna corresponde a los valores del vector m1, m2, m3, m4, la segunda columna corresponderá a los valores m5, m6, m7, m8, y así sucesivamente. Como vemos estas dos sentencias nos permiten inicializar la matriz de transformaciones, para poder realizar operaciones precisamos la siguiente instrucción: glMultMatrix(M) Esta sentencia nos permitirá multiplicar matrices, teniendo en cuenta que multiplicará la matriz definida con una de las sentencias anteriores por la matriz M que definimos en este punto. Vistas OpenGl permite utilizar diferentes expresiones para definir la posición de la cámara y hacía donde mira dicha cámara. En OpenGl la representación de los ejes e coordenadas se describe en el siguiente gráfico. y x z Existe la posibilidad de manipular estos ejes cambiando la posición relativa de cada uno de ellos, para ello OpenGl utiliza una serie de sentencias que nos definen como vamos a ver los ejes de coordenadas y en consecuencia como se visualizará el objeto tridimensional. gluLookAt() Esta sentencia permite definir de forma especifica donde se situará la cámara, hacía donde mirara está y cual va a ser el orden de los ejes de coordenadas. gluLookAt(x0,y0,z0,xc,yc,zc,Vx,Vy,Vz); 17 Facultad de Ingeniería de Sistemas e Informática UNMSM Escuela académico profesional de Ingeniería de Software Ciclo 2015 – I Esta sentencia tiene nueve argumentos que describen tres puntos, los valores de x0, y0, z0, representa el punto hacia donde mira la cámara virtual, este punto normalmente se identifica en el origen de coordenadas (0,0,0), ahora bien podemos definir nosotros el punto que más propicio sea para nuestra escena. Los siguientes tres argumentos representan el punto donde se situará la cámara de visualización, estas coordenadas no deben coincidir con el punto al cual miramos. Las últimas tres coordenadas representa el vector de vista hacía arriba, es decir indica cual será el vector cuya dirección será hacia arriba, “apuntará hacia la parte superior del monitor”. Para entender este caso podemos ver una serie de ejemplos donde se especifica cual es el vector y como que da definido los ejes de coordenadas: y y x x z z (Vx,Vy,Vz) = (0,1,0) (Vx,Vy,Vz) = (1,1,0) Como podemos ver según sea el valor del vector de vista hacia arriba el objeto que visualizaremos tendrá un aspecto u otro. Normalmente para simplificar la visualización el vector de vista hacia arriba se hace coincidir con uno de los ejes de coordenadas, como es el caso del primer gráfico. glOrtho() Se utiliza para especificar una proyección ortográfica. Este tipo de proyección define un volumen de vista rectangular, concretamente define un paralelepípedo de tamaño infinito, este hecho nos lleva a definir una serie de planos de corte para detallar con exactitud el volumen de vista. OpenGl define la sentencia como: glOrtho(xwmin, xwmax, ywmin, ywmax, pcerca, plejos); 18 Facultad de Ingeniería de Sistemas e Informática UNMSM Escuela académico profesional de Ingeniería de Software Ciclo 2015 – I Estos seis argumentos definen la ventana de visualización y los planos de corte tanto cercano como lejano. Para definir la ventana de visualización es suficiente definir las coordenadas de dos esquinas de la ventana, con estos dos valores queda totalmente definida. Los valores de pcerca y plejos representan el plano cercano y el plano lejano. Hay que tener en cuenta que el objeto a visualizar debe encontrarse dentro de ambos planos, si sobrepasan estos dos planos el objeto se recortará automáticamente. Plano de corte lejano plejos pcerca Plano de corte cercano (xwmax, ywmax) (xwmin, ywmin) El objeto se visualizará entre los dos planos de recorte, en el caso que sobrepase estos planos se recortará, y si el objeto es tan grande que la ventana de visualización esta dentro de él, no se visualizará nada quedando la pantalla en negro. Hay que tener en cuenta que si el plano de corte es negativo esté se encontrará detrás de la ventana de visualización. glFrustum() Este comando se utiliza para definir una proyección perspectiva, se define de forma similar a la proyección ortográfica pero con la diferencia que la proyección perspectiva define como volumen de vista una pirámide, en consecuencia el objeto a visualizar tiene un aspecto mucho más realista. La sentencia que utiliza OpenGl es: glFrustum(xwmin, xwmax, ywmin, ywmax, pcerca,plejos); Como vemos esta sentencia se define de forma similar a la utilizada para definir proyecciones paralelas, de igual forma que anteriormente definimos planos de corte para limitar el volumen de vista, que en este caso al ser una proyección perspectiva definirá un tronco piramidal. 19 Facultad de Ingeniería de Sistemas e Informática UNMSM Escuela académico profesional de Ingeniería de Software Ciclo 2015 – I plejos Plano de corte lejano (xwmax, ywmax) Plano de corte cercano (xwmin, ywmin) pcerca Punto de vista Como vemos ambas sentencias se define de forma similar pero determina vistas muy diferentes entre sí. gluPerpespective() Esta sentencia es una alternativa a la función glFrustum, la diferencia entre ambas está en la forma de definir la ventana de visualización. Si en la sentencia glFrustum definimos los dos vértices necesarios de la ventana, en la sentencia glPerpestive solamente definiremos el ángulo de apertura de la cámara y la relación entre el largo y ancho del plano cercano de corte. La sentencia en OpenGl será pues de la forma: gluPerpective(apertura, aspect, pcerca, plejos); Apertura corresponde al ángulo de apertura de la cámara virtual, este ángulo puede tomar valores comprendidos entre 0º y 180º. El valor de aspect, vendrá dado por la relación entre el alto y ancho del plano de corte, por lo tanto aspect toma el valor de alto plano dividido entre largo plano. Los valores de pcerca y plejos corresponden a los plano de corte cercano y lejano, de forma similar que en los casos anteriores. 20 Facultad de Ingeniería de Sistemas e Informática UNMSM Escuela académico profesional de Ingeniería de Software Ciclo 2015 – I plejos Plano de corte lejano Plano de corte cercano pcerca Apertura Punto de vista Las transformaciones 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 código, primero traslado, luego roto y luego si pinto mi objeto (es también en sentido inverso al que se lee el código), OpenGL trabaja con dos matrices diferentes: GL_MODELVIEW: es la matriz donde se trabajan con las transformaciones. GL_PROJECTION: es donde se define la relación mundo viewport. Para cambiar de matriz a utilizar se usa la instrucción: 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 proyección tiene un límite máximo de 2 en la mayoria de las implementaciones. Para trabajar con la matriz directamente se tiene: 21 Facultad de Ingeniería de Sistemas e Informática Escuela académico profesional de Ingeniería de Software UNMSM Ciclo 2015 – I 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. 22 Facultad de Ingeniería de Sistemas e Informática Escuela académico profesional de Ingeniería de Software UNMSM Ciclo 2015 – I 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): 23 Facultad de Ingeniería de Sistemas e Informática Escuela académico profesional de Ingeniería de Software UNMSM Ciclo 2015 – I 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 utilización de la libreria glut, al llamar la funcion: glutInitDisplayMode (buffers), buffers es una combinación 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 acumulación. 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: a. Función 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 24 Facultad de Ingeniería de Sistemas e Informática Escuela académico profesional de Ingeniería de Software UNMSM Ciclo 2015 – I 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. b. 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. 25 Facultad de Ingeniería de Sistemas e Informática Escuela académico profesional de Ingeniería de Software UNMSM Ciclo 2015 – I Compilación OpenGL Compilacion de aplicaciones OpenGL en Windows Para compilar un programa OpenGL suponiendo que se está en un sistema operativo MS-Windows, con el compilador VisualC++, se debe recuperar la librería 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. Pero también se pueden utilizar distintos lenguajes de programación, como por ejemplo: C++ Builder o Delphi. Compilacion de aplicaciones OpenGL en Linux Para compilar un programa OpenGL suponiendo que se está en sistema operativo UNIX o Linux con las librerias instaladas en lugares estandar se debe dar el siguiente comando suponiendo que todo el programa está 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: 26 Facultad de Ingeniería de Sistemas e Informática UNMSM Escuela académico profesional de Ingeniería de Software Ciclo 2015 – I Las identaciones en el archivo son tabulador no espacios en blanco!. Solo se debe cambiar nombre programa 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: falle: man make ,o en caso que este info make Para mas informacion sobre opciones gcc/g++ dar el comando: man gcc ,o: man g++ 27