Mostrando entradas con la etiqueta tutorial opengl. Mostrar todas las entradas
Mostrando entradas con la etiqueta tutorial opengl. Mostrar todas las entradas

domingo, 5 de mayo de 2013

OpenGL desde 0. Capitulo 5: Cámaras

Capítulo perteneciente al tutorial de opengl desde cero

Hola a todos,

Ya sabemos crear objetos 3D, y también sabemos moverlos, rotarlos,escalarlos e iluminarlos. Pero.... como los vemos? Cual es el proceso que se encarga de poner esos objetos en nuestra pantalla?

La respuesta es la cámara. La cámara no es más que una forma de decirle a OpenGL como queremos que renderice la escena.

Hay un concepto previo a la cámara que tengo que explicar que es el cambio de tipo de matrices (es un tema que he ido dejando caer en otros capítulos pero que no he profundizado).

En OpenGL tenemos tres tipos de matrices:

GL_PROJECTION: Matriz de proyección. Es que nos permite escoger como va a ser nuestra cámara.
GL_MODELVIEW: Matriz de visión de modelo. La que hemos usado hasta ahora, es la que nos permite decir como queremos que se vea el modelo. (traslaciones,rotaciones,etc...)
GL_TEXTURE: Matriz de texturizado. Por ahora lo dejamos para cuando toquemos texturas. Con que sepáis que existe, suficiente por ahora.

Para escoger en que tipo de matriz queremos trabajar tenemos que usar la función glMatrixmode(tipo).

Una vez tenemos seleccionado la matriz de proyección tenemos que seleccionar que tipo de cámara queremos.

Ortogonal (o paralela):  Es una cámara que no tiene perspectiva (si nos alejásemos 2 metros para atrás veríamos lo mismo.) En 3D no tiene mucha utilidad pero en 2D es básica. Para más ejemplos podéis mirar el pakengine.
A nivel de código se selecciona esta cámara mediante la siguiente instrucción:
glOrtho (left,right,bottom,top,near,far);

Perspectiva: Es aquella en que los objetos más alejados aparecen más pequeños y por lo tanto da una mayor sensación de realismo. Es la más usada en 3D.
Para que el código pida usar esta cámara se ha poner:
glFrustum(left,right,bottom,top,near,far);

Siendo los parámetros que reciben ambas funciones las dimensiones de la caja donde se realiza el renderizado (es decir, todo lo que entre en la caja se renderizará y el resto no se tendrá en cuenta)

De todas maneras, estas dos funciones no son demasiado claras. Con esto quiero decir que si bien es sencillo entender los parámetros es complicado darle un significado físico a estos. Para ello aparecieron dos funciones nuevas que solucionaban bastante la vida:
gluPerspective(fovy,aspect,near,far) siendo fovy el field of view (angulo de abertura vertical de la cámara) y aspect la relación entre el ancho y el alto de la pantalla.
gluLookAt (eyex,eyey,eyez, centerx,centery,centerz, upx,upy,upz) siendo eye las coordenadas de nuestro punto de vista, center las coordenadas del punto al que estamos mirando y up es un vector que nos define hacia donde está nuestra verticalidad (para poder hacer giros de cámara).

Y hasta aquí el tema de hoy. No obstante volveremos a trabajar sobre esto por que la mayoría de lo que he explicado hoy ya está deprecated en las últimas versiones de OpenGL :) , así que tendremos que darle otro enfoque. Por ahora, esto ya os servirá.

Espero que os haya gustado y hayáis aprendido.

Nos vemos


LordPakusBlog

sábado, 4 de mayo de 2013

OpenGL desde 0. Capitulo 4: Luces y Materiales

Capítulo perteneciente al tutorial de opengl desde cero

Hola a todos,

Este capítulo tal vez es de lo más complicados de la teoría necesaria para aprender OpenGL. Si entendéis bien este capítulo no tendréis ningún problema con el resto de OpenGL. Si no lo entendeís a la primera, tranquilos, es un tema denso,  daos tiempo y lo ireis pillando.

El tema de hoy se llama luces y materiales (dos conceptos a priori bastante alejados para un profano) debido a que si llego a poner como titulo "composición de color mediante openGL" todavía se me habría entendido menos :). En el mundo real, los colores que percibimos dependen de muchos factores pero principalmente de las características físicas del objeto que estamos mirando y del tipo e intensidad de luz que le llega.  OpenGL, como ya deberíais saber a estas alturas, no es más que un sistema que simula lo mejor posible la realidad, así que en el tema de la iluminación hace lo mismo. Nos permite definir las características físicas de los materiales y de las luces que tenemos en la escena.

OpenGL modela la luz en tres componentes diferentes:

- Luz Ambiental: Aquella que proviene de un lugar indeterminado y que rebota en los objetos en todas direcciones. Es aquella luz que no tiene un punto de inicio. Es lo más parecido a la luz del Sol o a la iluminación en una sala grande. Es lo que nos permite ver los objetos con claridad.

-Luz difusa: Es aquella que proviene de un foco cuyo inicio podemos determinar. Es la típica que nos va a generar zonas iluminadas y de sombras en un objeto. Se la conoce también como iluminación suave.

-Luz especular: Conceptualmente es muy parecida a la luz difusa, solo que es aquella que nos genera "brillos" en el objeto. Se la conoce como iluminación fuerte.

A modo de ejemplo, una bombilla generará luz ambiente y difusa (casi nada de especular) mientras que un láser generará  solamente luz especular.

Como ya sabemos como trata la luz OpenGL tendremos que definir como queremos que nos interpole el color en nuestro modelo. Hay dos modos:    GL_FLAT y GL_SMOOTH y se activan mediante glShadeModel (ejemplo:  glShadeModel(GL_SMOOTH);  ). La opción FLAT calcula el color medio del poligono y lo pone todo con el mismo color plano, la opción SMOOTH realiza una suavizado del color de todos los vertices. La que queda mejor es la SMOOTH ya que queda mucho más suave y realista. La opción FLAT no tiene mucho sentido ya que la mayoria de tarjetas gráficas actuales son lo suficientemente potentes para realizar los cálculos que exige la opción SMOOTH. En todo caso, probad ambas opciones y decidid vosotros mismos.

Materiales
Para los materiales hay dos funciones básicas para setear sus características "lumínicas":
glMaterialfv (GLenum face, GLenum name, GLfloat vector_param);
glMaterialf (GLenum face, GLenum name, GLfloat param);

- face hace referencia a que cara se han de aplicar los cambios (frontal o trasera, generalmente será la frontal = GL_FRONT)
 - name hace referencia a que propiedad vamos a modificar
- param (o vector_param) hace referencia al valor  (o vector de valores) que utilizaremos para cambiar las características de nuestro material

Las características que se pueden cambiar de los materiales són:
GL_AMBIENT: Respuesta del material a la luz ambiental
GL_DIFFUSE: Respuesta del material a la luz difusa.
GL_SPECULAR: Respuesta del material a la luz especular.
GL_EMISSION: Cantidad de luz que "emite" el objeto. Cuidado que realmente no emite luz y no ilumina al resto de objetos de la escena. solo es una especie de brillo que habrá en su superficie,
GL_SHININESS: Brillo del material
GL_AMBIENT_AND_DIFFUSE: Este parametro sirve para setear de manera rápida la respuesta ambiental y difusa del material (suponiendo que sea la misma).
GL_COLOR_INDEXES: Por ahora no nos preocuparemos de este parámetro. En posteriores capitulos trabajaremos con el.

Aquí teneís un código de ejemplo de como se pueden usar estos parámetros:
GLfloat mat_ambient[] = {0.1, 0.1, 0.1, 1.0f}; 
GLfloat mat_diffuse[] = {0.0, 0.0, 0.8, 1.0f};
GLfloat mat_specular[] = {0.9, 0.9, 0.9, 1.0f};


glMaterialfv (GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv (GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv (GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialf (GL_FRONT, GL_SHININESS, 100.0f);




Luces
Para las luces hay un poco más de teoria que no un par de funciones como en el caso de los materiales.

OpenGL es capaz de manejar 8 luces a la vez (nombradas como GL_LIGHT0,GL_LIGHT1...GL_LIGHT7) . La manera de activarlas es mediante glEnable.

Así pues si quisieramos activar dos luces en la escena se debería hacer algo de este estilo:

glEnable(GL_LIGHTING); //Para activar la iluminación
glEnable(GL_LIGHT0);
glEnable(GL_LIGHT1);

Todos los parámetros de las luces se pueden modificar mediante la función glLightfv( GL_LIGHTn, modo, valores) 
GL_LIGHTn : Identificador de luz
modo: El parámetro que estamos modificando
valores: Los valores de los parámetros que estamos seteando.

Los parámetros que se pueden modificar con esta función son:
GL_AMBIENT: Contribución de esta luz a la luz ambiental total.
GL_DIFFUSE: Luz que rebotará en los objetos proviniente de este foco.
GL_SPECULAR: Luz que rebotará solo en las partes más cercanas del objeto al foco.
GL_POSITION: Posición de la luz. Acepta como parámetros x,y,z,w. Si w es 0 es una luz direccional. 
GL_SPOT_DIRECTION: Dirección en la que ilumina el foco.
GL_SPOT_CUTOFF: Apertura del foco.
GL_SPOT_EXPONENT: Concentración de la luz. Cuanto más alto sea el valor más concetrada estará la luz en el centro del foco.

Factor de atenuación de la luz:  Con la misma función se puede seleccionar como queremos que vaya decreciendo la luz:
GL_CONSTANT_ATTENUATION, GL_LINEAR_ATTENUATION, GL_QUADRATIC_ATTENUATION. 

Dependiendo de vuestras necesidades os va a ir mejor una u otra , así que os recomiendo que vayaís probandolas todas.

Aparte de todo esto, si queremos, podemos modificar la luz ambiental directamente sin tener que tocar ninguna luz de la siguiente manera:
glLightModelfv( GL_LIGHT_MODEL_AMBIENT, color)

Existen más funciones y parámetros, pero ya elevan demasiado el nivel de complejidad. Por ahora, con ir trasteando con lo que tenemos ya tenemos mucho. En breve os colgaré código de una escena sencilla para que veais todo esto como funciona.

Mis disculpas por el tocho-post, pero era un capítulo ineludible que más tarde o más temprano se tenía que abordar. A partir de ahora el resto será bastante más liviano (espero :) )

Espero que os haya gustado.

Nos vemos.
LordPakusBlog

jueves, 2 de mayo de 2013

OpenGL desde 0. Capitulo 3: Transformaciones del espacio

Capítulo perteneciente al tutorial de opengl desde cero

Hola a todos,

Si estáis siguiendo este tutorial ya sabéis (más o menos) como se representan objetos 3D, pero...como los movemos , rotamos o escalamos??

OpenGL provee de transformaciones del espacio que nos permiten modificar nuestros modelos 3D en función de nuestras necesidades.

Las 3 funciones principales que nos permiten realizar estas operaciones son:

glTranslatef(x,y,z): Esta función hace que todo lo que pintemos a partir de la llamada de esta función lo haga desplazado hacía las coordenadas x,y,z

glScalef(x,y,z): Esta función escala (modifica el tamaño) de todo lo que se pinte a partir de la llamada a esta función.

glRotatef(angle, x, y ,z): Esta función se encarga de rotar toda la escena alrededor del vector x,y,z en el angulo que le digamos.

Junto con estas tres funciones considero necesario también hablar de dos funciones más que nos serán muy útiles:

glPushMatrix(): Esta función apila (guarda) la matriz de transformación tal y como la tenemos.

glPopMatrix(): Esta función nos pone como matriz de transformación actual la última que hayamos guardado mediante glPushMatrix() (y la quita de la pila)

Estas dos funciones son extremadamente útiles para realizar transformaciones de solo un objeto , dejando el resto de objetos como estaban. Por ejemplo:

glPushMatrix();

glTranslatef(1.0f,1.0f, -5.0f);

glBegin(GL_POINTS);
glVertex3f(1.0f, 2.0f, 3.0f);
glEnd();

glPopMatrix();

Al finalizar este trozo de código la operación glTranslatef solo habrá resultado efectiva para el pintado del punto, pero no para el resto de objetos que podamos pintar en pantalla.


Un tema importante que tenéis que recordar es que las operaciones de transformación del espacio en el fondo aplican matrices de transformación a la matriz actual de trabajo y que por lo tanto, el ORDEN DE LOS FACTORES ALTERA EL PRODUCTO. Es decir, no es lo mismo rotar y mover que mover y rotar. Mi consejo es que vayáis probando y veáis como funciona, no hay mejor manera de aprender que ir experimentando.

Otro tema importante (ya para finalizar), es que si os fijáis  todo lo que se ha explicado en este capítulo nos puede servir para montar nuestra primera cámara. Con esto quiero decir que si bien no he explicado nada de cámaras supongo que ya intuis que una cámara es un "ente" que permite posicionarnos en la escena para verla desde un punto de vista u otro. Lo gracioso de las transformaciones del espacio es que tanto podemos mover la cámara, como podemos mover todo el mundo 3D que afectos prácticos para nosotros va a ser lo mismo (el renderizado haciéndolo de una manera u otro no va a variar)

Ya sin más os dejo tranquilos.

Espero que os haya gustado y hayáis aprendido

Nos vemos



LordPakusBlog

martes, 30 de abril de 2013

OpenGL desde 0. Capitulo 2: Primitivas de pintado

Capítulo perteneciente al tutorial de opengl desde cero

Hola a todos,

Antes que nada quiero que tengáis en cuenta que gran parte de lo que os explicaré hoy no tiene utilidad más allá de los fines didácticos. Actualmente, con las nuevas versiones de OpenGL gran parte del tema de primitivas queda obsoleto, no obstante creo que es necesario que sepáis como funciona todo para ir aprendiendo el resto poco a poco (entendiendo las primitivas el resto viene directo).

En el capítulo anterior os comenté que existían unos bloques de código donde yo podía poner mi pintado de objetos 3D , los bloques glBegin / glEnd. La función glBegin acepta un parámetro que nos dicta de que manera va a utilizar los vértices que le pasemos a la tarjeta gráfica (si los va a entender como puntos sueltos, componentes de un triangulo o un cuadrado o bien extremos de una linea, por ejemplo)

Las primitivas gráficas que acepta OpenGL son las siguientes:

GL_POINTS: Cada vértice que le pasemos lo pintará como un punto. Esta primitiva puede ser útil en determinados momentos de desarrollo, ya lo explicaremos cuando lleguemos a ello.

GL_LINES: Acepta vértices de dos en dos siendo el primero el punto de inicio y el segundo el punto final de una linea.

GL_LINE_STRIP: Cada vértice que le pasemos será el punto final de una recta formada por este punto y el anterior y a la vez será el punto de inicio de una recta con el siguiente punto que le pasemos a OpenGL. Es decir, encadena puntos mediante rectas.

GL_LINE_LOOP: Es lo mismo que el GL_LINE_STRIP con la única diferencia que genera una recta adicional al final que une el punto incial de la lista con el punto final, formando siempre ciclos cerrados.

GL_TRIANGLES: Esta es una de las primitivas más usadas con diferencia. Acepta tres vértices que conformarán un triángulo. Si le llegan menos de tres vértices no pintará nada.

GL_TRIANGLE_STRIP: Esta directiva es la homóloga de GL_LINE_STRIP en el caso de los triángulos. Cada vértice nuevo genera un triángulo nuevo formado por este último punto y los dos anteriores. En su momento se usaba mucho para pintar modelos complejos (si no recuerdo mal los modelos MD2 del Quake2 se pintaban así).

GL_TRIANGLE_FAN: Este es un caso especial de pintado de triángulos ya que los triángulos nuevos se forman con el último punto que hemos introducido, el anterior y el primero de la lista. Al compartir todos los triángulos un punto (el inicial) acaban siempre quedando como un abanico. Su única utilidad era para simular circunferencias, pero hoy en dia está totalmente en desuso.

GL_QUADS: Esta primitiva está eliminada de las versiones más recientes de OpenGL. Esta primitiva utilizaba cuatro vértices para pintar un cuadrado. Su máxima utilidad residia en aplicaciones 2D como por ejemplo el PakEngine

GL_QUAD_STRIP: Esta primitiva está eliminada de las versiones más recientes de OpenGL. Su funcionamiento era homólogo a las otras primitivas STRIP pero en el caso de QUADS.

GL_POLYGON: Esta primitiva genera un polígono de tantos vértices como se le pase. En general tenía poco uso.


Para demostrar como funciona cada una de las primitivas he desarrollado un código muy sencillo que nos permitirá ver como renderiza cada primitiva teniendo todas  la misma información de vertices.

int DrawGLScene(GLvoid)
{
static int cont = 0 ;

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();

glTranslatef(0.0f,0.0f,-6.0f);

cont++;

switch ( (cont/50) )
{
case 0: glBegin(GL_POINTS); break;
case 1: glBegin(GL_LINES); break;
case 2: glBegin(GL_LINE_STRIP); break;
case 3: glBegin(GL_LINE_LOOP); break;
case 4: glBegin(GL_TRIANGLES); break;
case 5: glBegin(GL_TRIANGLE_STRIP); break;
case 6: glBegin(GL_TRIANGLE_FAN); break;
case 7: glBegin(GL_QUADS); break;
case 8: glBegin(GL_QUAD_STRIP); break;
case 9: glBegin(GL_POLYGON); break;
default: cont = 0; glBegin(GL_POINTS); break;
}

glVertex3f(-1.0f, 1.0f, 0.0f);
glVertex3f( 1.0f, 1.0f, 0.0f);
glVertex3f( 1.0f,-1.0f, 0.0f);
glVertex3f(-1.0f,-1.0f, 0.0f);
glEnd();

return TRUE;
}

Aquí tenéis un vídeo que os demostrará como funciona: 


Espero que os haya gustado.... en breve colgaré otro tutorial para ir profundizando más sobre OpenGL.

Recordad que estoy abierto a sugerencias y dudas.

Nos vemos


LordPakusBlog

domingo, 28 de abril de 2013

OpenGL desde 0. Capitulo 1: Bases de OpenGL

Capítulo perteneciente al tutorial de opengl desde cero

Hola a todos,

En este blog ya hemos trabajado con OpenGL en varias ocasiones (por ejemplo en motores gráficos y pakengine), pero nunca os he explicado realmente como funciona OpenGL.

Las bases son sencillas:
- Todo lo que pintamos por pantalla es una representación ortogonal de objetos 3D. Es decir, "aplastamos" objetos 3D contra el monitor del PC.
- Estos objetos 3D estan representados por triangulos 2D que conforman su superficie. Es decir, el objeto 3D que vemos solo es una carcasa hecha con triangulos (en general) muy pequeños.
- Estos triangulos se definen mediante 3 puntos en coordenadas 3D. A la vez que definimos estos puntos normalmente también definimos coordenadas de cada punto a una región de una textura, haciendo que el modelo 3D que estamos representando tenga un "dibujo" que nos de mayor sensación de realidad (sin las texturas los objetos 3D serían de color sólido sin ningún tipo de detalle)

Resumiendo (a muy grosso modo) , openGL para pintar escenas 3D solamente usa puntos en 3D y relaciones de esos puntos con coordenadas 2D de una textura.

Fácil no?? Bueno, realmente esto solo es el principio. Solo hemos explicado como crear un mundo 3D, pero... como lo visualizamos?

Una vez el mundo 3D ya está creado hemos de generar una cámara que nos permita visualizar toda esa información. La cámara es la que se encargará de "aplastar" esos objetos 3D contra nuestra pantalla 2D. La gran pregunta es.... y como funciona todo ese proceso de "aplastamiento" ?? (A partir de ahora le llamaremos renderizado :) )
La respuesta es fácil de explicar pero dificil de entender al 100%. Con matemáticas, todo el proceso de renderizado se basa en cálculo matricial (tranquilos que todo el proceso lo hará la tarjeta gráfica) que si bien no deberemos saber como funciona realmente si que tendremos que irlo interiorizando con el tiempo. Esto quiere decir que openGL permite acumular matrices de transformación que nos permiten realizar diferentes tareas (traslaciones, rotaciones, etc...).

Ya sabemos la teoria de como crear un mundo 3D y de como visualizarlo, así que ahora solo nos falta un glosario rápido de funciones openGL para poder ir aprendiendo de códigos que vayamos viendo por la red (Nehe es un muy buen sitio para comenzar).


glViewport(0,0,width,height) : Esta función sirve para decirle a OpenGL en que zona de la ventana queremos que nos renderice la escena. Por defecto querremos que nos renderice la escena en toda la pantalla.


glMatrixMode (modo de matriz) : Esta función nos cambia el tipo de matriz sobre la que vamos a trabajar. Hay 4 tipos básicos de matrices sobre las que vamos a trabajar (ya las explicaremos más a fondo): GL_MODELVIEW, GL_PROJECTION,  GL_TEXTURE y  GL_COLOR.
    
glLoadIdentity(): Carga la matriz identidad. En matemáticas la matriz identidad es la mátriz neutra, es decir, limpiamos toda la pila de operaciones que teniamos hasta el momento.

glBegin/glEnd : Nos indica una zona de código donde le pasaremos a openGL los puntos de nuestros objetos 3D y el formato como queremos que los una (ya lo explicaremos en posteriores capitulos)

glVertex: Nos permite enviar puntos 3D que conforman un objeto 3D a la tarjeta gráfica. Ya iremos profundizando sobre esta funcionalidad.

Pese a que nos faltan todavía muchas funciones, con esto tendreis una pequeña ayuda para ir entendiendo códigos.

Espero que para ser el primer capítulo no se os haya hecho pesado y que os lo hayaís pasado bien. En el próximo capitulo iré introduciendo un poco de código para que lo veais más claro.

Nos vemos en el siguiente capitulo de openGL desde 0.


LordPakusBlog

Entradas populares