OpenGL no es muy rápido dibujando con “glBegin();” más incluso si utilizamos cálculos matemáticos para calcular los vértices. En el siguiente ejemplo utilizamos trigonometría para dibujar un polígono en este caso de cinco caras y radio 1. Pero cada vez que lo dibujemos hay que realizar los cálculos de nuevo, esto es una autentica chapuza. Y la solución no es meter los vértices con datos literales, puede que el programa nos pregunte el número de caras que tenga el polígono.
#define PI2 6.28318530717958647692 { glDisable( GL_CULL_FACE ); glPolygonMode( GL_FRONT, GL_FILL ); glPolygonMode( GL_BACK, GL_LINE ); GLint caras = 5; GLfloat radio = 1.0f; GLfloat x_aux = 0.0f, y_aux = 0.0f, z_aux = 0.0f, inc = PI2/caras; glBegin( GL_POLYGON ); for ( GLfloat angulo = PI2; angulo > 0.0f; angulo -= inc ) { x_aux = GLfloat( radio * sin(angulo) ); y_aux = GLfloat( radio * cos(angulo) ); glColor3ub( 255, 0, 0 ); glVertex3f( x_aux, y_aux, z_aux ); } glEnd(); }
Una solución a este problema es usar listas compiladas. Solo tenemos que dibujar la figura una vez entre las funciones “glNewList();” y “glEndList();”. Luego cuando queramos dibujarla solo tenemos que llamar a la función “glCallList();”. Y por último cuando ya no la necesitemos la borramos con “glDeleteLists();”. En la tabla inferior tenemos un ejemplo de todas estas funciones y su definición:
Definición de las Listas
glListBase( 0 ); // Valor inicial para glGenLists() normalmente 0. GLuint lista = glGenLists(1); // Reserva espacio para una lista. Compilación de una Lista glNewList( lista, GL_COMPILE ); /* DIBUJAR PRIMITIVAS */ glEndList(); Llamada y Eliminación de Listas glCallList( lista ); // Dibuja la lista compilada glDeleteLists( lista, 1 ); // Borra una lista
En una lista además de vértices y colores también se pueden compilar normales, materiales, luces, texturas de coordenadas, otras listas, objetos cuadráticos, curvas bezier, nurbs, y algunas otras funciones de las librerías GL y GLU. No os preocupéis por esto de momento, solo quedaros con que una lista puede incluir a otra/s lista/s pero lo que no puede incluir son “TEXTURAS”. Las texturas van aparte de forma similar a las listas, ya las veremos más adelante. Con todo esto el ejemplo anterior nos quedaría así:
#define PI2 6.28318530717958647692 glListBase( 0 ); GLuint lista = glGenLists(1); glNewList( lista, GL_COMPILE ); glDisable( GL_CULL_FACE ); glPolygonMode( GL_FRONT, GL_FILL ); glPolygonMode( GL_BACK, GL_LINE ); GLint caras = 5; GLfloat radio = 1.0f; GLfloat x_aux = 0.0f, y_aux = 0.0f, z_aux = 0.0f, inc = PI2/caras; glBegin( GL_POLYGON ); for ( GLfloat angulo = PI2; angulo > 0.0f; angulo -= inc ) { x_aux = GLfloat( radio * sin(angulo) ); y_aux = GLfloat( radio * cos(angulo) ); glColor3ub( 255, 0, 0 ); glVertex3f( x_aux, y_aux, z_aux ); } glEnd(); glEndList();
glCallList( lista ); // Dibuja la lista compilada
glDeleteLists( lista, 1 ); // Borra una lista
Una observación, si queremos compilar más de una lista solo hay que obrar de la siguiente forma:
glListBase( 0 ); GLuint lista = glGenLists( 3 ); glNewList( lista + 0, GL_COMPILE ); ... glEndList(); glNewList( lista + 1, GL_COMPILE ); ... glEndList(); glNewList( lista + 2, GL_COMPILE ), ... glEndList(); glCallList( lista + 0 ); glCallList( lista + 1 ); glCallList( lista + 2 ); glDeleteLists( lista, 3 );
Como viene siendo habitual en OpenGL el orden siempre altera el resultado. Vamos a estudiar las tres transformaciones básicas: Trasladar, Rotar y Escalar...
glTranslatef( GLfloat x, GLfloat y, GLfloat z ); Esta función traslada el espacio de coordenadas en x, y, z unidades sobre sus respectivos ejes de coordenadas X, Y, Z.
glRotatef( GLfloat angle, GLfloat x, GLfloat y, GLfloat z ); Realiza una rotación del espacio de coordenadas por una medida de (angle) tomados en grados a lo largo del vector determinado. Es recomendable que (x,y,z) sea un vector normalizado (o sea magnitud(x,y,z) = 1).
glScalef( GLfloat x, GLfloat y, GLfloat z ); Cambia la proporción de los objetos respecto a los ejes de coordenadas, lo que es equivalente a decir que los estira o encoge una cantidad determinada por los parámetros de la función x,y,z.
Normalmente primero trasladaremos un objeto, después lo giraremos y por último lo escalaremos. Esto es porque OpenGL trabaja con matices de 4x4 “GLfloat Matriz[4][4];” para hacer transformaciones multiplicándolas por la matriz de modelado. En el primer ejemplo trasladamos un cubo y después lo rotamos siendo evidente el resultado:
En este segundo ejemplo primero rotamos un cubo y luego lo trasladamos. Lo que pasa es que no rotamos el objeto sino todo el espacio, así cuando después trasladamos este no se encuentra donde nosotros pensábamos. Lo mismo pasa con el escalado, si primero agrandamos el cubo al doble, cuando lo traslademos se trasladará el doble y de nuevo no se encontrará donde habíamos pensado.
Como hemos visto, no trasladamos, ni giramos, ni escalamos los objetos sino que lo hacemos sobre el espacio por así decirlo. Y para colmo estas transformaciones son acumulativas, lo cual es un serio problema cuando queremos transformar varios objetos de diferentes formas cada uno.
Para este en farragoso asunto OpenGL nos da dos soluciones. La primera y más simple es una función que borra la matriz dejándola a cero pelotero “glLoadIdentity();”. Recordar que hay varias matrices así que borra la matriz actual. Y la segunda solución es más ingeniosa consiste en hacer una copia de la matriz para luego restaurarla mas tarde. Esto se consigue con las funciones “glPushMatix();” y “glPopMatix()”. ¿A que son graciosos estos nombres? Cuando haces PUSH... ya no hay POP... Por lo visto hacen referencia al ruido cuando introduces algo PUSH y cuando lo sacas POP como si fuesen dibujos animados. Bueno ya esta bien de cachondeo y guasa con el asunto.
Tabla con las funciones mencionadas
glMatrixMode( GL_PROJECTION ) // Selecciona la Matriz de Proyección. La Perspectiva y la Cámara. glMatrixMode( GL_MODELVIEW ) // Selecciona la Matriz de Modelado. Donde dibujamos. glMatrixMode( GL_TEXTURE ) // Selecciona la Matriz de Textura. Donde están las Coord. de Textura. glLoadIdentity(); // Borra la matriz actual. glPushMatrix(); // Mete en la Pila una Copia de la Matriz actual. glPopMatrix(); // Saca de la Pila la última Copia de la Matriz.
Un ejemplo teórico usando listas compiladas:
glPushMatrix(); // Guardamos la Matriz de Modelado. glTranslatef( 1.0f, 0.0f, 0.0f ); // Trasladamos X una unidad. glRotatef( 45, 0.0f, 0.0f, 1.0f ); // Rotamos en Z 45 grados. glScalef( 2.0f, 2.0f, 2.0f ); // Escalamos todo al doble. glCallList( Lista + 0 ); glLoadIdentity(); // Borramos la matriz. glScalef( 2.0f, 2.0f, 2.0f ); // Escalamos todo al doble. glRotatef( 45, 0.0f, 0.0f, 1.0f ); // Rotamos en Z 45 grados. glTranslatef( 1.0f, 0.0f, 0.0f ); // Trasladamos en X dos unidades a 45 grados. glCallList( Lista + 1 ); glLoadIdentity(); // Borramos la matriz. glPopMatrix(); // Restauramos la Matriz de Modelado. glTranslatef( 0.0f, 1.0f, 0.0f ); // Trasladamos Y en una unidad. glRotatef( 90, 0.0f, 1.0f, 0.0f ); // Rotamos en Y 90 grados. glCallList( Lista + 2 );
Solo comentar que la función "glMultMatrixf();" multiplica la matriz actual por la indicada como argumento. Pero no voy ha explicar operaciones con matrizes de momento, solo que os suene...
GLfloat Matriz[4][4]; glMultMatrixf( (const GLfloat *) Matriz ); // Multiplica la Matriz Actual.
Para simplificar el trabajo la librería GLU permite dibujar rápidamente esferas, cilindros y discos. Esto nos va ha servir de iniciación a nuevos conceptos como las normales de iluminación y las texturas de coordenadas. Las normales de iluminación son vectores que definen como rebotan los rayos de luz en una superficie, jugando con ellos se pueden conseguir efectos de suavizado en los objetos. ¿Os suena esta tabla de abajo?
GL_FLAT
|| GL_SMOOTH
Aunque parezca increíble es la misma esfera, con el mismo número de caras. La diferencia es que en la primera las normales de iluminación son perpendiculares a las caras y en la segunda son todas radiales desde el centro. De momento no vamos a adentrarnos mas en el tema, más adelante veremos como se pueden calcular estas normales pero por ahora dejaremos que sea OpenGL quien las calcule automáticamente.
El otro concepto nuevo son las coordenadas de textura, estas definen como se aplica una textura a un objeto. Sobre estas coordenadas hay mucho que estudiar pero por ahora no las vamos a usar. Yo no se muy bien que significa objeto cuadrático pero son objetos en los que sus vértices, normales y coordenadas de textura pueden calcularse matemáticamente. El ejemplo más típico es la esfera:
#include <cmath> #define M_PI 3.14159265358979323846 void Esfera( float radio, int nlatitud, int nlongitud ) { float inct, incf; int i, j; float vertice[3]; inct = 2 * M_PI / nlatitud; incf = M_PI / nlatitud; for( i=0; i<nlatitud; i++ ) { glBegin( GL_LINE_STRIP ); vertice[0] = vertice[1] = 0; vertice[2] =- radio; glVertex3fv( vertice ); for( j=1; j<nlongitud-1; j++ ) { vertice[0] = radio * cos(i*inct) * cos(j*incf-0.5*M_PI); vertice[1] = radio * sin(i*inct) * cos(j*incf-0.5*M_PI); vertice[2] = radio * sin(j*incf-0.5*M_PI); glVertex3fv( vertice ); vertice[0]=radio*cos((i+1)*inct)*cos(j*incf-0.5*M_PI); vertice[1]=radio*sin((i+1)*inct)*cos(j*incf-0.5*M_PI); glVertex3fv(vertice); } vertice[0] = vertice[1] = 0; vertice[2] = radio; glVertex3fv( vertice ); } glEnd(); } glColor3ub( 255, 255, 255 ); Esfera( 2.0f, 32, 32 );
Pero no os preocupéis que no nos hace falta implementar todas estas funciones matemáticas para dibujar esferas, cilindros y discos. En la tabla inferior vemos listadas todas las funciones de la librería GLU sobre objetos cuadráticos.
gluNewQuadric(); // Crea un objeto cuadrático. GLUquadricObj *Esfera = gluNewQuadric(); gluQuadricOrientation( GLUquadricObj *Quadratic, GLenum Parametros ); Parámetros: Determina la Orientación de las Normales de Iluminación. - GLU_OUTSIDE Normales Exteriores - GLU_INSIDE Normales Interiores gluQuadricNormals( GLUquadricObj *Quadratic, GLenum Parametros ); Parámetros: Determina el tipo de Normales de Iluminación. - GLU_SMOOTH Normales Suaves - GLU_FLAT Normales Planas gluQuadricDrawStyle( GLUquadricObj *Quadratic, GLenum Parametros); Parámetros: Define la forma de dibujar. - GLU_FILL Lleno. - GLU_LINE Lineas - GLU_POINT Puntos. gluQuadricTexture( GLUquadricObj *Quadratic, GL_TRUE ); // Activa las Coordenadas de Textura. gluSphere( GLUquadricObj *Quadratic, GLfloat radio, GLint Longitud, GLint Latitud ); gluCylinder( GLUquadricObj *Quadratic, GLfloat radioInferior, GLfloat radioSuperior, GLfloat altura, GLint segmentos, GLint pilas ); gluDisk( GLUquadricObj *Quadratic, GLfloat radioInterior, GLfloat radioExterior, GLint segmentos, GLint pilas ); gluPartialDisk( GLUquadricObj *Quadratic, GLfloat radioInterior, GLfloat radioExterior, segmentos, pilas, GLfloat anguloInicial, GLfloat anguloFinal ); gluDeleteQuadric( GLUquadricObj *Quadratic ); // Borra un objeto cuadrático.
El código de ejemplo que viene a continuación está sacado de la práctica número 04. Este tutorial es complementario a las prácticas, así que acostumbraros a ir viendo las prácticas a la vez que leéis este tutorial.
Ejemplo de uso de objetos cuadráticos: // -- CREAMOS LAS LISTAS COMPILADAS -- glListBase( 0 ); GLuint lista = glGenLists(6); // * * * CREAMOS 6 LISTAS * * * glPointSize( 2.0f ); glLineWidth( 2.0f ); GLUquadricObj *Quadric = gluNewQuadric(); gluQuadricDrawStyle( Quadric, GLU_LINE ); glNewList( lista + 0, GL_COMPILE ); glColor3ub( 255, 0, 0 ); // -- ESFERA -------------------------------- // -------------- Radio, Longitud, Latitud -- gluSphere( Quadric, 1.0f, 12, 12 ); glEndList(); gluQuadricDrawStyle( Quadric, GLU_FILL ); glNewList( lista + 1, GL_COMPILE ); glDisable( GL_CULL_FACE ); glPolygonMode( GL_FRONT, GL_FILL ); glPolygonMode( GL_BACK, GL_LINE ); glColor3ub( 0, 255, 0 ); // -- CILINDRO SIN TAPAS ---------------------------------------- // ------- RadioInferior, R_Superior, Altura, Segmentos, Pilas -- gluCylinder( Quadric, 1.0f, 0.5f, 1.0f, 12, 3 ); glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); glEnable( GL_CULL_FACE ); glEndList(); gluQuadricDrawStyle( Quadric, GLU_POINT ); glNewList( lista + 2, GL_COMPILE ); glColor3ub( 0, 0, 255 ); // -- DISCO ----------------------------------------- // --- RadioInterior, R_Exterior, Segmentos, Pilas -- gluDisk( Quadric, 0.5f, 1.0f, 12, 3 ); glEndList(); gluQuadricDrawStyle( Quadric, GLU_LINE ); glNewList( lista + 3, GL_COMPILE ); glColor3ub( 255, 255, 0 ); // -- DISCO PARCIAL -------------------------------------------------------- // -- RadioInterior, R_Exterior, Segmentos, Pilas, AnguloInicial, AnguloFinal gluPartialDisk( Quadric, 0.5f, 1.0f, 12, 3, 0.0f, 270.f ); glEndList(); gluQuadricDrawStyle( Quadric, GLU_FILL ); glNewList( lista + 4, GL_COMPILE ); glColor3ub( 255, 64, 64 ); gluSphere( Quadric, 1.0f, 32, 32 ); glEndList(); gluQuadricNormals( Quadric, GLU_SMOOTH ); // -- ACTIVAMOS SUAVIZADO -- glNewList( lista + 5, GL_COMPILE ); glPushAttrib( GL_LIGHTING_BIT ); // -- GUARDA EL ESTADO DE LAS LUCES -- glEnable( GL_LIGHTING ); // -- ACTIVA LA LUZ -- glEnable( GL_LIGHT0 ); // -- ACTIVA LA LUZ Nº 0 -- glColor3ub( 64, 64, 255 ); gluSphere( Quadric, 1.0f, 32, 32 ); glPopAttrib(); // -- RESTAURA EL ESTADO DE LAS LUCES -- glEndList(); glDeleteLists( lista, 6 ); // -- BORRAMOS LAS LISTAS -- gluDeleteQuadric( Quadric ); // -- BORRAMOS EL QUADRIC --
Aún no hemos estudiado las luces así que ignorar de momento las funciones "GL_LIGHT...", pero aquí tenemos otra vez las palabritas "PUSH" y "POP" en las funciones "glPushAttrib();" y "glPopAttrib();". Ellas guardan y restauran los atributos especificados con la función "glEnable();". Ahora no entraremos en más profundidad sobre el tema.
Lo primero de todo, el entorno 3D está inicializado por la librería "SDL_con_OpenGL.h" , vamos a utilizar C++ (con sus librerías estándar), SDL y OpenGL.
En "Configuracion.h" están las definiciones comunes y en "TUTORIAL_OPENGL.h" hay información resumida sobre las funciones OpenGL utilizadas en las prácticas. De momento ignorar la clase "Fuentes_Bitmap.h" y "Entorno.h".
Sobre "Mirror.h" decir que invierte las imágenes, por alguna extraña razón SDL y OpenGL tiene el eje X de las imágenes invertidos. Para poder pasar imágenes de SDL a OpenGL y viceversa es necesario invertir X.
La estructura básica va ha ser la siguiente:
INCLUDES INICIO DE MAIN CREACIÓN DEL ENTORNO 3D ( Con o sin Entorno Envolvente ) VERIFICACIÓN COMPATIBILIDADES TARJETA GRAFICA CREACIÓN DE FUENTES DE TEXTO CREACIÓN DE LUCES CREACIÓN DE MATERIALES CARGA DE TEXTURAS CREACIÓN DE LISTAS COMPILADAS CÁLCULOS PROYECCIÓN DE SOMBRAS CONFIGURACIÓN DE VISUALIZACIÓN POSICIONAMIENTO DE LA CAMARA BUCLE PRINCIPAL DE DIBUJADO 3D POSICIONAMIENTO DE LUCES ACTIVACIÓN DE MATERIALES ACTIVACIÓN DE TEXTURAS DIBUJADO DE SOMBRAS LLAMADA A LAS LISTAS COMPILADAS ... FIN BUCLE PRINCIPAL DE DIBUJADO 3D MOSTRAR INFORMACIÓN DE ESTADOS BORRADO DE OBJETOS FIN DE MAIN
CONTROLES DE LA CAMARA:
CONTROLES DE ROTACIÓN:
CONTROLES DE ILUMINACIÓN:
OTROS CONTROLES:
Descargar Practica nº01 (Solo usuarios registrados)
Dibujaremos Triángulos, con diferentes tamaños formas y colores. Que tengan diferentes vistas por delante que por detrás. Usaremos una textura de Patrón para rellenar un cuadrado.
Un detalle importante es la función "glEdgeFlag();" en el caso de dibujar un cuadrado usando dos triángulos en el tercer ejemplo. Con ella podemos ver el cuadrado sin una diagonal que lo cruce.
CONTROLES:
Así podrás rotarlos para ver las caras traseras.
Descargar Practica nº02 (Solo usuarios registrados)
Dibujar Cuadrados, con diferentes tamaños formas y colores. Que tengan diferentes vistas por delante que por detrás. Usar una textura de Patrón.
Descargar Practica nº03 (Solo usuarios registrados)
Dibujar "GLUquadricObj" objetos Cuadráticos con la librería GLU. Activa la Iluminación y juega con activar o desactivar el color.
CONTTROLES:
Descargar Practica nº04 (Solo usuarios registrados)
Comentarios
Arreglar los links
Alguien puede por favor arreglar los links de los ejemplos 1 y 2 por favor
Muchas
gracias por poner tutos aqui en scenebeta de OpenGL y tambien por explicarlos de esa forma asi hasta dan mas ganas de seguir leyendo,me seran de ayuda para aprender mas sobre esto :)
Saludos.
v10.0