OpenGL sirve para dibujar “en tiempo real” objetos en tres dimensiones. Estos objetos al iluminarlos (Luces) pueden tener unas características físicas de refracción de la luz (Normales y Materiales), también se les pueden pegar diferentes clases de texturas para mayor realismo.
Estos pueden ser representados en un sistema ortográfico u de perspectiva y través de una cámara. Pueden ser trasladados, rotados, escalados, etc...
OpenGL no gestiona el teclado ni el ratón, ni las ventanas, ni es capaz de cargar imágenes para las texturas, ni sonidos ni músicas.
Tampoco es capaz de escribir directamente en pantalla, ni de proyectar sombras, ni de calcular colisiones, ni de usar huesos (Bones) para animar modelos, etc...
La mayor parte de las carencias de OpenGL las supliremos con las librerías SDL, y el resto con programación.
Solo necesitamos incluir dos archivos de cabecera “#include <GL/gl.h>” y “#include <GL/glu.h>”. En ellos vienen incluidos los siguientes tipos de datos que usaremos preferentemente cuando programemos para funciones OpenGL:
La base del dibujo 3D es el punto, este se representa por sus coordenadas en los ejes Ancho “X”, Alto “Y” y Profundo “Z”. Con dos puntos se representa una línea y con tres un triángulo...
Ejemplo del punto (10,5,3)
Aunque los ejes de coordenadas “X” e “Y” sean infinitos, no lo es así la pantalla. Además esta utiliza Píxeles y no Puntos. Hay que dejar claro que “1 Pixel” no tiene por que ser igual a “1 Punto”. Con la siguiente función OpenGL especificamos como los Puntos de los ejes de coordenadas “X” e “Y” serán representados en la pantalla en Píxeles.
void glViewport( GLint X, GLint Y, GLsizei Ancho, GLsizei Alto );
Modo 1: X -100, Y -100, Ancho 200, Alto 200 Modo 2: X 0, Y 0, Ancho 200, Alto 200 Modo 3: X -100, Y 100, Ancho 200, Alto 200
Este tipo de vista es la más simple de calcular pero no se ajusta a la realidad, en ella todos los rayos de proyección son paralelos entre si. Esto se traduce en que el tamaño de un objeto será siempre el mismo sin importar la distancia del observador. Pero este tipo de vista es útil por ejemplo, para pintar texto en pantalla, pintar menús, o simplemente dibujar objetos en 2D en los cuales no nos interese la profundidad “Z”.
glOrtho( GLdouble Izquierda, GLdouble Derecha, GLdouble Abajo, GLdouble Arriba, GLdouble Cerca, GLdouble Lejos );
La forma de cargar la matriz de proyección en modo ortográfico es la siguiente:
glMatrixMode( GL_PROJECTION ); glLoadIdentity(); glOrtho( 0, 640, 0, 480, -1, 1 );
OpenGL utiliza una matriz llamada “GL_PROJECTION” matriz de proyección para calcular la vista en el mundo 3D. Las diferentes operaciones y transformaciones de esta matriz darán como resultado la visión espacial desde un punto dado, normalmente la cámara. Ni sé ni quiero entrar en detalles sobre esta matriz puesto que OpenGL en sus librerías GL y GLU incluye funciones para operar con ella de forma cómoda.
Este tipo de proyección es con la que vemos normalmente el mundo que nos rodea. Los rayos de proyección convergen en un único punto llamado “Punto de Fuga” que es la pupila de nuestro ojo. Estos rayos tienen un ángulo determinado de apertura o campo de visión, y con esto todos los objetos son proyectados en el “Plano de Proyección” que es la ventana de visualización que definimos con la función “glViewport()”.
gluPerspective( GLdouble Apertura, GLdouble Aspecto, GLdouble Z_Cerca, GLdouble Z_Lejos );
Apertura: Es el ángulo de apertura en grados del eje “Y” del Punto de Fuga. Aspecto: Es la relación entre la Anchura y Altura. Ósea Anchura / Altura. Z_Cerca: Es el valor mínimo del eje “Z”, Plano de corte cercano. Z_Lejos: Es el valor máximo del eje “Z”. Plano de corte lejano.
La forma de cargar la matriz de proyección en modo de perspectiva es la siguiente:
glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective( 45.0f, 640.0f/480.0f, .5f , -1000.0f );
Y para terminar todo lo referente a la matriz de proyección nos falta la cámara. Cuando nos movemos por el espacio tendríamos que mover todos los objetos para dar la sensación de movimiento. Pero es más sencillo mover la matriz de proyección y está se encargará de calcular la nueva proyección. Para hacer esto de forma fácil la librería GLU incluye la siguiente función llamada “gluLookAt();”. Esta función transforma la matriz de proyección a través de la posición del ojo, el punto de enfoque y un vector que indica la inclinación de la cámara. Este vector de inclinación es normalmente ( 0, 1, 0 ), que corresponde con la posición normal en la que se usa una cámara.
void gluLookAt( GLdouble Ojo_X, GLdouble Ojo_Y, GLdouble Ojo_Z, GLdouble Enfoque_X, GLdouble Enfoque_Y, GLdouble Enfoque_Z, GLdouble Inc_X, GLdouble Inc_Y, GLdouble Inc_Z );
La librería OpenGL utiliza varios tipos de buffers, pero ahora nos vamos a centrar en los dos más importantes.
El primero es el buffer de color “GL_COLOR_BUFFER_BIT”, en el se almacenan los colores de cada píxel de la ventana de visualización. Cuando OpenGL dibuja objetos los almacena en este buffer, pero este buffer en su configuración típica es doble. El motivo es simple, evitar que veamos como se forman los dibujos en la pantalla. Cuando se ha terminado de dibujar todo se conmuta el buffer “SDL_GL_SwapBuffers();” volcándolo a la pantalla. Ahora podemos seguir dibujando en la otra parte del buffer, y así tantas veces como queramos, normalmente entre 15 y 30 veces por segundo que es la velocidad necesaria para que nuestros ojos vean una composición en movimiento y no una serie de imágenes estáticas (Efecto Cinematográfico). Recomendablemente debemos limpiar el buffer con un color de fondo antes de dibujar de nuevo. Esto se define con la función “glClearColor( GLfloat Red, GLfloat Green, GLfloat Blue, GLfloat Alpha );”. La función “glColorMask();” permite activar o desactivar la escritura en el buffer de color por componentes usando el boleano “true | false”;
glClearColor( GLfloat Red, GLfloat Green, GLfloat Blue, GLfloat Alpha ); glClear( GL_COLOR_BUFFER_BIT ); glColorMask( GLboolean Red, GLboolean Green, GLboolean Blue, GLboolean Alpha );
El segundo buffer es el de profundidad “GL_DEPTH_BUFFER_BIT” y su función es importantísima, cada vez que se dibuja un píxel guarda la coordenada “Z” de profundidad respecto a la cámara. Ahora cuando se pinte un nuevo píxel puede comprobar si este está por delante o por detrás del anterior, evitando dibujar objetos lejanos por encima de objetos cercanos. Al igual que el buffer de color es recomendable limpiarlo antes de dibujar de nuevo, este valor se define con la función “glClearDepth( GLfloat z );”. Además tenemos que definir el tipo de comparación que realizará este buffer con la función “glDepthFunc( GLenum funcion );” :
glClearDepth( GLfloat z ); // Define el valor para limpiar. glClear( GL_DEPTH_BUFFER_BIT ); // Limpia el buffer con "z". glEnable( GL_DEPTH_TEST ); // Activa el buffer de profundidad. glDisable( GL_DEPTH_TEST ); // Desactiva el buffer.
glDepthFunc( GLenum funcion ); funcion: - 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. ( Normal ) - 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.
El conjunto de todas estas funciones quedaría así, véase que la función que realmente realiza la limpieza de los buffers es “glClear();”, pero antes es necesario definir todas las funciones anteriores:
glClearColor( GLfloat Red, GLfloat Green, GLfloat Blue, GLfloat Alpha ); glClearDepth( GLfloat z ); glDepthFunc( GLenum funcion ); glColorMask( GLboolean Red, GLboolean Green, GLboolean Blue, GLboolean Alpha ); glEnable( GL_DEPTH_TEST ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
Ejemplo típico:
glClearColor( 0.0f, 0.0f, 0.0f, 0.0f); glClearDepth( 1.0f ); glDepthFunc( GL_LEQUAL ); glColorMask( true, true, true, true ); glEnable( GL_DEPTH_TEST ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
En OpenGL el color se representa por sus componentes roja, verde, azul y opcionalmente alfa que es la transparencia. Ahora vamos a estudiar como aplicar un color a un punto o vértice. En la imagen izquierda tenemos un pentágono en el cual cada vértice tiene un color distinto, obsérvese como se produce un efecto de degradado entre los diferentes colores.
Cada componente de color tiene un rango que depende del tipo del dato, en el caso de GLubyte el rango vá de [ 0 a 255 ]. Si fuera tipo GLbyte sería [ -127 a 128 ] y si es GLfloat de [ 0.0 a 1.0 ].
void glColor3ub( GLubyte Rojo, GLubyte Verde, GLubyte Azul ); void glColor4ub( GLubyte Rojo, GLubyte Verde, GLubyte Azul, GLubyte Alfa ); void glColor3f( GLfloat Rojo, GLfloat Verde, GLfloat Azul ); void glColor4f( GLfloat Rojo, GLfloat Verde, GLfloat Azul, GLfloat Alfa ); void glColor4fv( GLfloat *Color );
Después de definir el color es necesario definir el vértice. Al igual que con el color la función para vértices sigue una estructura definida que es típica de las funciones OpenGL. Vamos a aprovechar para explicar un poco esta estructura común a muchas funciones OpenGL:
retorno: Como en cualquier function de C o C++. librería: Puede ser “gl” o “glu” dependiendo de donde esté definida. nombre: Es el nombre de la función, “Color”, “Vertex2, “Normal”... #parámetros: Es el número de parámetros de entrada de la función, “2”, “3” o “4” normalmente. tipo parámetros: El tipo OpenGL de los parámetros, “GLbyte”, “GLubyte”, “GLint”, “GLfloat” ... si hay una "v" ejemplo "4fv" el tipo es un puntero a un vector. parámetros: Los parámetros propiamente dichos.
El ejemplo de arriba, pentágono de colores:
glcolor3ub( 255, 255, 255 ); // BLANCO glvertex3i( -10, 15, 0 ); glcolor3ub( 0, 0, 255 ); // AZUL glvertex3i( -10, -5, 0 ); glcolor3ub( 0, 255, 0 ); // VERDE glvertex3i( 0, -10, 0 ); glcolor3ub( 255, 0, 0 ); // ROJO glvertex3i( 10, -5, 0 ); glcolor3ub( 0, 0, 0 ); // NEGRO glvertex3i( 10, 5, 0 );
En el ejemplo anterior los vértices siguen un orden específico, esto no es por casualidad. En OpenGL el orden de los vértices si altera el resultado. Tres puntos definen un triángulo, pero un triángulo tiene dos caras. Como podemos aplicar diferentes características a cada cara, tenemos que poder diferenciar las caras frontales de las traseras. El método consiste en dibujar los vértices en sentido horario para una cara y antihorario para la otra. Para definir esto se utiliza la función “glFontFace();”:
Definición de las Caras Frontales. glFrontFace( GL_CCW ); // Sentido Antihorario. glFrontFace( GL_CW ); // Sentido Horario.
Ahora que ya sabemos cuales son las caras frontales y cuales las traseras, podemos especificar cuales no queremos que se dibujen cuando no nos sea necesario. Imagínate dibujar las caras interiores de un cubo ¿Quién las va ha ver?, pero si dibujamos una hoja de un árbol tiene que verse por los dos lados.
glCullFace( GL_enum Parámetros ); Parámetros: - GL_FRONT // NO CALCULA LAS CARAS FRONTALES. - GL_BACK // NO CALCULA LAS CARAS TRASERAS.
glEnable( GL_CULL_FACE ); // ACTIVA EL CORTE DE CARAS. glDisable( GL_CULL_FACE ); // DESACTIVA EL CORTE DE CARAS.
Nosotros utilizaremos como caras frontales las de sentido antihorario. Pero aún hay más. También podemos decir como se dibujarán las caras de un polígono con la función “glPolygonMode();” ver tabla inferior:
glPolygonMode( GL_enum Parámetros, GL_enum Modo ); Parámetros: - GL_FRONT // CARA FRONTAL. - GL_BACK // CARA TRASERA. - GL_FRONT_AND_BACK // AMBAS CARAS. Modo: - GL_FILL // RELLENO. - GL_LINE // LINEAS. - GL_POINT // PUNTOS.
Visto todo lo anterior la configuración típica resultante sería la siguiente:
glFrontFace( GL_CCW ); glCullFace( GL_BACK ); glPolygonMode( GL_FRONT_AND_BACK, GL_FILL); glEnable( GL_CULL_FACE );
Bueno, hasta ahora hemos aprendido mucho sobre vértices pero aún nos falta. ¿Cuántas figuras podrías dibujar con cuatro vértices? 1 Cuadrado, 2 Triángulos, 1 Triángulo y 1 Punto, 4 Líneas, 2 Líneas y 1 Punto, 1 Línea y 2 Puntos, 4 Puntos...
Nos queda claro que los vértices por sí solos no dibujan nada, tenemos que decirle a OpenGL qué queremos que dibuje.
Para ese fin una de las funciones que tiene OpenGL es la función “glBegin();” y “glEnd();”. En la primera hay que definir el tipo de primitiva que queremos dibujar, luego dibujamos los vértices y terminamos con la segunda.
La función glBegin() - glEnd():
glBegin( GLenum Parámetros ); Dibuja diferentes tipos de primitivas. Parámetros: - GL_POINTS Dibuja puntos. - GL_LINES Dibuja líneas usando dos puntos. - GL_LINE_STRIP Dibuja líneas con dos puntos iniciales, los siguientes puntos continúan la línea. - GL_LINE_LOOP Dibuja líneas como (GL_LINE_STRIP) pero cierra el último punto con el primero. - GL_TRIANGLES Dibuja triángulos con 3 puntos. - GL_TRIANGLE_STRIP Dibuja triángulos encadenados. Con los trés primeros puntos hace el primer triángulo, luego con cada nuevo punto y los dos anteriores crea los siguientes. - GL_TRIANGLE_FAN Dibuja triángulos usando el primer punto de todos y los dos últimos. El primer punto de cada triángulo es común y es el primero de todos. - GL_QUADS Dibuja un Rectángulo usando 4 puntos. - GL_QUAD_STRIP Dibuja un Rectángulos usando los 2 puntos anteriores y los 2 posteriores. - GL_POLYGON Dibuja un Polígono Convexo de "n" puntos.
glEnd(); Termina de dibujar puntos.
El ejemplo pentágono de colores:
glBegin( GL_POLYGON ); glcolor3ub( 255, 255, 255 ); // BLANCO glvertex3i( -10, 15, 0 ); glcolor3ub( 0, 0, 255 ); // AZUL glvertex3i( -10, -5, 0 ); glcolor3ub( 0, 255, 0 ); // VERDE glvertex3i( 0, -10, 0 ); glcolor3ub( 255, 0, 0 ); // ROJO glvertex3i( 10, -5, 0 ); glcolor3ub( 0, 0, 0 ); // NEGRO glvertex3i( 10, 5, 0 ); glEnd();
En el caso de dibujar puntos y líneas tenemos unas funciones encargadas de definir sus propiedades, el grosor y en el caso de líneas podemos aplicar un patrón de bits. Esto significa que podremos definir líneas que sigan un patrón de puntos y rallas según los bits de una variable tipo GLushort. Los bits en “true” dibujan ralla y los que estén en “false” hueco.
Cambiar el grosor de puntos y líneas:
glPointSize( GLfloat tamaño ); // Establece el tamaño de los puntos glLineWidth( GLfloat grosor ); // Establece el grosor de las líneas
Ejemplo de un triángulo usando Líneas con Patrón:
GLushort patron = 0x5555; // Patrón de la Línea, usando los BITS. GLint factor = 5; // Factor de repetición del Patrón. glEnable( GL_LINE_STIPPLE ); // Activación de líneas usando Patrones. glLineStipple( factor, patron ); // Configuración de la Línea. glBegin( GL_LINE_LOOP ); glcolor3ub( 0, 0, 255 ); // AZUL glvertex3i( 0, 1, 0 ); glcolor3ub( 0, 255, 0 ); // VERDE glvertex3i( -1, -1, 0 ); glcolor3ub( 255, 0, 0 ); // ROJO glvertex3i( 1, 1, 0 ); glEnd(); glDisable( GL_LINE_STIPPLE );
Ahora vamos con un ejercicio completo en el cual hay algunas funciones que aún no hemos explicado como por ejemplo “glShadeModel();”, “glHint();” y “glEnable( GL_COLOR_MATERIAL );”. Aún no quiero explicarlas a fondo porque afectan a temas que veremos más adelante.
glShadeModel( GLenum Parámetro );
GL_FLAT || GL_SMOOTH
Si quiero recordaros que la función “SDL_GL_SwapBuffers();” es la encargada de volcar el buffer de color a la pantalla. Todas las funciones que empiecen por el prefijo “SDL_” son propias de la librería SDL que no es motivo de estudio en este tutorial.
// ----------------------------------------------------------------------------- // Práctica 00 // ----------------------------------------------------------------------------- // Aquí está implementada la forma más simple plosible de iniciar SDL con Opengl // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // LINKER: // -lmingw32 -lSDLmain -lSDL -lopengl32 -lglu32 -mwindows // ----------------------------------------------------------------------------- #include <SDL/SDL.h> #include <GL/gl.h> #include <GL/glu.h> #define MOVER 0.2f #define V_Ancho 640 #define V_Alto 480 #define FOTOGRAMAS 25 // Fotogramas por segundo. #define N_Ventana "Curso OpenGL con SDL" class c_sdl_con_opengl { public: c_sdl_con_opengl() { // -- INICIACION SDL ----------------------------------------------------- atexit( SDL_Quit ); if( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_TIMER ) < 0 ) exit(1); pantalla = SDL_SetVideoMode( V_Ancho, V_Alto, 16, SDL_OPENGL | SDL_GL_DOUBLEBUFFER | SDL_HWPALETTE | SDL_HWSURFACE | SDL_HWACCEL ); if( pantalla == NULL ) exit(1); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); // -- Activa el Doble Buffer -- SDL_WM_SetCaption( N_Ventana, NULL ); // -- Pone nombre a la ventana. -- if ( SDL_EnableKeyRepeat( 1, SDL_DEFAULT_REPEAT_INTERVAL ) ) exit(1); // -- INICIACION OPENGL -------------------------------------------------- glClearColor( 0, 0, 0, 0 ); // Borrado del Buffer Color. glClearDepth( 1.0f ); // Borrado del Buffer Depth. glDepthFunc( GL_LEQUAL ); // Función de Borrado Buffer Depth. glEnable ( GL_DEPTH_TEST ); // Activamos el Test de Profundidad. glFrontFace( GL_CCW ); // GL_CCW o GL_CW. glCullFace( GL_BACK ); // GL_FRONT o GL_BACK. glEnable( GL_CULL_FACE ); // NO DIBUJA LAS CARAS TRASERAS glShadeModel( GL_SMOOTH ); // Activamos el Sombreado Suave, (GL_FLAT) Plano. glPolygonMode( GL_FRONT_AND_BACK, GL_FILL); glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); glViewport( 0, 0, V_Ancho, V_Alto ); glMatrixMode( GL_PROJECTION ); // Selecciona la Matriz de Proyección. glLoadIdentity(); gluPerspective( 45.0f, (GLfloat)V_Ancho/(GLfloat)V_Ancho, 0.5f , -1000.0f ); glMatrixMode( GL_MODELVIEW ); // Selecciona la Matriz de Modelado. glLoadIdentity(); // -- INICIACION DE LAS VARIABLES ---------------------------------------- terminar = false; cox = 0.0f; coy = 0.0f; coz = -5.0f; cex = cox; cey = coy; cez = coz + 1; frames = Uint32( 1000 / FOTOGRAMAS ); tiempo = SDL_GetTicks() + frames; }; ~c_sdl_con_opengl() { SDL_FreeSurface( pantalla ); pantalla = NULL; SDL_Quit(); }; void empieza( void ) { glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glLoadIdentity(); gluLookAt( cox, coy, coz, cex, cey, cez, 0.0f, 1.0f, 0.0f ); }; bool termina( void ) { leer_teclado(); espera_frame(); SDL_GL_SwapBuffers(); return terminar; }; private: void leer_teclado(void) { while( SDL_PollEvent( &evento ) ) { switch( evento.type ) { case SDL_QUIT: { terminar = true; break; } case SDL_KEYDOWN: if( evento.key.keysym.sym == SDLK_ESCAPE ) { terminar = true; break; } } } SDL_PumpEvents(); teclado = SDL_GetKeyState( NULL ); if (teclado[SDLK_UP] ) coz += MOVER; if (teclado[SDLK_DOWN] ) coz -= MOVER; if (teclado[SDLK_LEFT] ) cox += MOVER; if (teclado[SDLK_RIGHT]) cox -= MOVER; cex = cox; cez = coz + 1; }; void espera_frame( void ) { t_espera = tiempo - SDL_GetTicks(); if ( ( t_espera > 0 ) && ( t_espera < frames ) ) SDL_Delay( t_espera ); else tiempo = SDL_GetTicks(); tiempo += frames; } SDL_Surface *pantalla; // Superficie Pantalla. SDL_Event evento; // Eventos del Teclado y Ratón. Uint8 *teclado; // Teclado. bool terminar; GLfloat cox, coy, coz, cex, cey, cez; Uint32 frames, tiempo, t_espera; }; // -- INICIO DE MAIN ----------------------------------------------------------- int main(int argc, char *argv[]) { GLfloat angulo = 0; c_sdl_con_opengl *sdl_con_opengl = new c_sdl_con_opengl; do { sdl_con_opengl->empieza(); glRotatef( angulo, 1, 1, 0 ); angulo += 1; if ( angulo >= 360 ) angulo = 0; glBegin( GL_TRIANGLE_STRIP ); // CARA FRONTAL -------------------------------------------- glColor3f( 1.0f, 1.0f, 0.0f ); glVertex3f( -1.0f, -1.0f, 1.0f ); // Izq. Abajo Frente glColor3f( 1.0f, 0.0f, 0.0f ); glVertex3f( 1.0f, -1.0f, 1.0f ); // Derecha Abajo Frente glColor3f( 1.0f, 1.0f, 1.0f ); glVertex3f( 0.0f, 1.0f, 0.0f ); // Centro Arriba Centro // CARA DERECHA -------------------------------------------- glColor3f( 0.0f, 1.0f, 0.0f ); glVertex3f( 1.0f, -1.0f, -1.0f ); // Derecha Abajo Fondo // CARA TRASERA -------------------------------------------- glColor3f( 0.0f, 0.0f, 1.0f ); glVertex3f( -1.0f, -1.0f, -1.0f ); // Izq. Abajo Fondo // BASE, CUADRADO FORMADO POR DOS TRIANGULOS --------------- glColor3f( 1.0f, 0.0f, 0.0f ); glVertex3f( 1.0f, -1.0f, 1.0f ); // Derecha Abajo Frente glColor3f( 1.0f, 1.0f, 0.0f ); glVertex3f( -1.0f, -1.0f, 1.0f ); // Izq. Abajo Frente // CARA IZQUIERDA ------------------------------------------- glColor3f( 1.0f, 1.0f, 1.0f ); glVertex3f( 0.0f, 1.0f, 0.0f ); // Centro Arriba Centro glColor3f( 0.0f, 0.0f, 1.0f ); glVertex3f( -1.0f, -1.0f, -1.0f ); // Izq. Abajo Fondo glEnd( ); glBegin( GL_LINE_LOOP ); glColor3ub( 255, 0, 0 ); // ROJO glVertex3i( -1, 1, 0 ); // Izquierda - Arriba glColor3ub( 255, 255, 0 ); // AMARILLO glVertex3i( 1, 1, 0 ); // Derecha - Arriba glColor3ub( 0, 255, 0 ); // VERDE glVertex3i( 1, -1, 0 ); // Derecha - Abajo glColor3ub( 0, 0, 255 ); // AZUL glVertex3i( -1, -1, 0 ); // Izquierda - Abajo glEnd(); } while ( !sdl_con_opengl->termina() ); delete sdl_con_opengl; return EXIT_SUCCESS; };
En esta práctica esta abordada la forma más simple posible de iniciar SDL con OpenGL dentro de una clase. Miraros bien el tutorial adjunto a esta práctica.
Descargar Práctica nº00 (Sólo usuarios registrados)
Comentarios
Gracias por el tutorial
Muy interesante el tutorial, recien empiezo a retomar opengl con interes para usarlo con SDL
Genial
Muchas gracias, me podre a intentra seguirlo. Una sugerencia es que sigas con algo de XNA. un saludo
Se ve bien
Pero ya me dio meio
ohh :O
muy bueno, excelente para aprender mas sobre la programacion (en general el unico motivo es programar xD)
SALUDOS.!
+3 :P no tengo mas
WooW pipagerardo, cuento
WooW pipagerardo, cuento tiempo sin verte por aqui!!
Espero más tutos tuyos! Felicidades!!!
¡WOHOHOHOHOHOHOHOHOHOHOHO!
¡El mejor tuto de SB de la historia! ¡WAAAU! Intenté aprender OpenGL, pero me costaba... demasiado. :P Me pondré con esto para las vacaciones. ;)
Me arrodillo ante ti
Madre mia como se nota que te lo has currado. Eres un crack.
Saludos y sigue asi.
Por fin empieza a dársele
Por fin empieza a dársele imporancia a la programación en la sección de PC.
Muchas gracias eres el amo. +100 SB
siempre
Siempre se le ha dado importancia :-P
Otra cosa es que hubiera voluntarios para desarrollarlo.
A eso me refería :p
A eso me refería :p
Me he quedado noqueado :S.
Me he quedado noqueado :S. Para ti 50 scenebitos :).