Monday, May 28, 2007


La animacion esqueletal:
En ocasiones, podemos hacer una aniamcion dividnedola en dos secciones, la parte de la piel, y una parte que contiene herarquia llamado el esqueleto. Con la piel, podemos definir como es la superficie de nuestro personaje, pero con el esqueleto se define la animacion.

Se construyen una serie de huesos. Cada hueso posee una transformacion tridimensional (incluye su posoicion, escala y orientacion) asi como un hueso padre( aunque este puede ser opcional) Es asi como los huesos forman una jerarquia. Toda la transformacion de un nodo hijo, es profcuto de la trnaformacion de su padre asi como la suya propia. Asi si se mueve el muslo se moviera tambien el gemelo de la pierna.

FBX

El FBX® es un formato de archivo para cosas 3d de tipo standard abierto, independiente de plataforma, con el puedes acceder a cualquier tipo de archivo 3d indepenientemente si tienes el sotware necesario. Los archivos que pueden ser transformados a este formato inclueyen :

Autodesk® Maya® software, Autodesk® 3ds Max® software, and Autodesk® VIZ software—providing high levels of interoperability between these packages—y Autodesk® MotionBuilder™ software.

File x format

El formato de tipo .x, guarda todo de un modo jerarquico. Cada nivel de jerarquia puede contener cualquier numero de objetos para resguardar datos, sim mebargo en ocasiones se suele despcmponer lo suficiente para tener poquitos objetos en el mismo nivel.

fx

El shader FX, es un shader de tiempo real especial para 3d MAX, permite que un maldito artista sin experiencia porgramando pueda construir complejos shaders para usarlos con el puerto de vista del MAX, asi como en engines e juegos externos.


S

Tuesday, May 22, 2007

Los key frames son los dibujos que son escenciales para indicar en donde empeiza y en donde termina una traslación . Se llaman frames o cuadros llave, debido a que su posición dentro del tiempo es medida respecto a los cuadros o frames que hay en un filme de pelicula. Una secuencia de frames define que movimientos el espectdor verá, mientras que la posición del keyframe en una película define el tiempo del movimeitn. Ahora bien, es claro que dos o tres keyframe a lo largo de un segundo no crearán la ilusión de movimiento, por lo que se deben llenar los demás cuadros conb dibujos intermedios.

Todas las películas con formato Mpeg están formadas por recuadros de 16 x 16. Para ahorrar espacio, los recuadros que son casi idénticos a aquellos recuadros del siguiente fotograma de la película son descartados. Esto produce una gran relación de compresión, ya que, en una escena en la que haya dos personas hablando, en la que no se mueven demasiado, los únicos recuadros que tendrán que cambiar serán aquellos en los que vemos sus bocas. Por esto, porque usamos recuadros de un fotograma en el siguiente, no tenemos una imagen completa. Por ejemplo, para tener el fotograma 5, carga los fotogramas 1, 2, 3 y 4 y ponlas juntas. Muchos archivos ASF y algunos de los primeros archivos codificados en DivX eran hechos así. El problema era que no se podía escoger el momento desde el que querías ver la película. No podías, por ejemplo, ver media película y volver mas tarde para terminarla. Tenias que empezar otra vez desde el principio. Esto se debe a que el avance rápido de cualquier reproductor de video tiene que examinar todos y cada uno de los fotogramas para poder reconstruir la película en el punto en el que lo dejaste.

Aquí es donde las Keyframes entran en juego. Una keyframe es introducida cada x segundos para que se pueda seguir el avance de la película. También proporciona una imagen perfecta en la que se puedan basar los medios fotogramas (delta frames). Los métodos de codificación en Mpeg dan el nombre de I-Frames o Intra-frames a estas keyframes. Los medios fotogramas vienen en dos tipos: B-frames o fotogramas en reversa y P-frames los fotogramas predichos. Puede sonar complicado, pero ambos están diseñados para almacenar la diferencia entre los fotogramas anterior y posterior a la keyframe como pequeños bloques.

La mayoría de los programas de compresión emplean un keyframe cada 5 o 10 segundos. Porque los keyframes, como imágenes completas, ocupan mucho mas espacio que los fotogramas parciales e incrementarán el espacio ocupado por el archivo de manera notable. Evidentemente, cuanto menos pongamos, mas compresión obtendremos. Si cortas un video en el espacio existente entre dos keyframes, el reproductor será incapaz de reconstruir los fotogramas de las películas hasta que no llegue al siguiente keyframe de la lista. Algunos programas “inteligentes” de Mpeg, son capaces de reconstruir las keyframes finales a partir de los trozos que le hayan llegado, evitando así el problema. De todas maneras, deberás estar pendiente en aquellos que no lo hagan.

VirtualDub puede, por norma general, arreglar una película sin keyframes si se utiliza en modo de reparación, pero una vez mas, no podrás cortar la película entre keyframes. Debes cortar en el punto donde se encuentre una de ellas.

Con esto en mente, y si usas los botones de keyframes de VirtualDub para avanzar y retroceder en la película que quieres cortar o unir, te será imposible cortar la película entre keyframes porque saltaras de una a otra =^)

DEsventajas keyFrames:ocupan mucho mas espacio que los fotogramas parciales e incrementarán el espacio ocupado por el archivo de manera notable.

Forward kinematic animationes un metodo muy usado en graficos 3d para aniamr modelos . funciona de la siguiente manera: Las posisicones de las partes de un modelo en particular en un determinado momento, son claculadas por la posición y la orientación del objeto, aunque también se ayuda de la información propircionada por las articulaciones (hombro,codo, muñeca, rodilla etc.) Ya que las articulaciones atan como se deberá mover el modelo. Lo delimitan, si el codo, por ejemplo esta en una posicion

Este metodo convendria cuando la persona , quiere que su animacion se mueva no siguiendo un patron definido, es decir hay un monito que se puede mover arriba abajo, bailar etc. puede moverse de muchos metodos, tonz es mejor programar como se debe mover, limitnado como se mueven sus extremidades, sin embargo, si se queire que sieeempre se baile simepre, pus mejor es osar los keyframes. te lo complicas sabroso, pero sabes que ya esta como tu deseas que se mueva

a cinemática se ocupa de la descripción del movimiento sin tener en cuenta sus causas.

El objetivo de la cinemática inversa consiste en encontrar el gesto que deben adoptar las diferentes articulaciones para que el final del sistema articulado llegue a una posición concreta.

Por ejemplo imaginemos que tenemos un robot con piernas, las piernas surgen de la cadera y a continuación estaría el fémur, la rodilla, la tibia y el pie (para simplificar acabaremos en el pie sin mas articulaciones).

En esta figura podemos observar el sistema que pretende imitar una pierna, como es lógico la posición de la cadera es fija o la podremos calcular en relación al cuerpo del robot o anclaje, pero ahora el problema que surge es, el posicionar el pie.

Basándonos en un sistema de coordenadas cartesianas podemos definir la posición que queremos que tome el pie, por ejemplo a 4cm en vertical y a 2,5cm desde la cadera, pero nos surgen los siguientes problemas;

- Que ángulo debe adoptar el fémur respecto a la cadera?

- Que ángulo debe adoptar la tibia respecto a la rodilla?

- Donde posicionamos la rodilla para que el pie tome la posición que queremos adoptar?

La cinemática inversa pretende solucionar el problema, veamos como podemos hacerlo.

Tomaremos la posición de la cadera como punto de origen de los vectores fémur y tibia, estos vectores tienen un radio o largo invariable, por ejemplo 10cm cada uno, con lo que el radio máximo que la pierna puede alcanzar será de 10cm de fémur + 10cm de tibia = 20cm. Para simplificar los cálculos es importante que el largo de fémur y tibia sea el mismo como veremos mas adelante.

Tomaremos el siguiente caso práctico, en el cual queremos posicionar el pie en un sistema cartesiano en la posición y=10 y x=5 con relación a la cadera.

Pensando un poco podemos observar que se forma un triángulo rectángulo entre los puntos de cadera y pie y disponemos del largo de los 2 catetos que son equivalentes a la posición X e Y en el sistema de representación cartesiano, así pues podemos calcular la hipotenusa de este triángulo como sigue.

Un cateto vale 10 y el otro 5, por lo tanto (10²+5²) = (100+25) = 125 ahora nos falta hacer la raiz cuadrada y quedaria Raíz(125) = 11,80 que seria la distancia entre los puntos de origen y destino(cadera a pie).

Ya sabemos la distancia y este dato es muy importante para el siguiente calculo, pero nos falta aun una cosa y es el ángulo que forma la hipotenusa que acabamos de calcular. Esto lo calcularemos con la siguiente formula:

ArcTan ( X / Y )

Y quedaría como sigue, (5 / 10) = 0,5 y ahora calculamos la ArcoTangente de 0,5 que nos dará como resultado 26,56º

Ya tenemos definido en coordenadas polares el punto donde queremos poner el pie del robot con respecto al punto de origen formado por la cadera que es de donde parte el fémur.

Ahora vamos a calcular que ángulo tendrán que tomar el fémur y la cadera para que el pie apunte justo a esa posición.

Sabemos que el fémur y la tibia tienen una longitud exactamente igual y esto como comentábamos al principio es de vital importancia para que el calculo sea simple ya que el punto donde convergen caerá siempre en la vertical de la mitad de la distancia entre cadera y pie (línea punteada en la siguiente figura), por lo tanto la rodilla siempre caerá en esa línea aunque no sabemos a que altura, y tampoco importa ya que solo nos interesa el ángulo de fémur y tibia.

Observamos que la línea roja y al línea punteada amarilla forman un ángulo de 90º y como en el caso anterior.. tendremos un triángulo rectángulo solo que ahora la hipotenusa es el propio fémur y contamos con la ventaja de saber su longitud, con lo que solo tendemos que calcular su ángulo, y tenemos que tener en cuenta que este ángulo que obtendremos será en relación a la línea entre cadera y pie por lo tanto para posicionar en el sistema cartesiano tendremos que sumarlos como veremos a continuación. Ahora vamos a calcular el ángulo del fémur de la siguiente manera.

La longitud del cateto opuesto a la rodilla (línea roja) es de 5,9cm (recordemos que es la mitad de la longitud entre cadera y pie) y la hipotenusa (fémur) mide exactamente 10cm con lo cual podemos calcular el seno de este triángulo rectángulo como sigue:

seno = cateto / hipotenusa

El cateto mide 5,9 y la hipotenusa 10 así que (5,9 / 10) = 0,59

Ahora solo tenemos que calcular el arcoseno de 0,59 para saber el ángulo que buscamos, esta función no se encuentra en muchos lenguajes de programación pero se puede calcular de la siguiente manera

ArcSen = Arctan(seno / Raíz(-seno * seno + 1))

Y obtenemos un ángulo de 36,16º correspondiente al seno 0,59

Este ángulo es el ángulo agudo del triángulo rectángulo así que hay que restarle 90º para obtener el ángulo que nos interesa, y esto sumado al ángulo formado por la línea entre cadera y pie nos da el ángulo total referenciado al sistema de coordenadas cartesiano o lo que es lo mismo referenciado al anclaje de la pierna o cuerpo del robot.

Angulo fémur = (90 - 36,16) + 26.56 = 80,4º

Así ya tenemos el fémur en su sitio y solo nos falta calcular el ángulo de la tibia, pero este calculo es mucho mas simple ya que tal ángulo es el doble del ángulo agudo calculado anteriormente con lo cuál:

Angulo Tibia = 36,16º x 2 =72,32º

Este ángulo es en relación al ángulo del fémur como puede observarse en la siguiente figura.

Llegados a este punto ya hemos conseguido el objetivo de saber que ángulo deben adoptar las extremidades de una supuesta pierna, para alcanzar un punto concreto.

Cuando lo uso????

Well, si andas haciendo un video juego y quieres que tu monito reaccione ante ciertos puntos es miy bueno usarlo, si no t importa como reaccione..nel pastel no lo uses.







Motion capture, Motion Tracking or Mocap, es una tecnica para digitalmente guardar movimientos, y es utilizado en los video juegos, deportes, applicaciones medicas.

Un usuario utiliza un par de marcadores en cada una de sus articulaciones para que asi se pueda identificar al movimiento, se idnetifica al movimeitno por las posiciones o los angulos que hay entre los marcadores. La computra capta y guarda estas posiciones, angulos, velocidades, accelraciones, impulsos, lo que da una representacion digital muy certera de como es el movimiento.


En los video juegos, esto puede reducir el costo de las animaciones, ya que no se necesita dibujar cada frame, motion capture ahorra tiempo y crea movimeitos mucho mas naturlaes que animacion creada manualemnte. Sin mebargo, esta limitada a animaciones que sean anatomicamente posibles. Asi por ejemplo, seria muy dificil animar al gracioso y guapo superman que todos amamos y conocemos.

In entertainment applications this can reduce the costs of animation which otherwise requires the animator to draw each frame, or with more sophisticated software, key frames which are interpolated by the software. Motion capture saves time and creates more natural movements than manual animation, but is limited to motions that are anatomically possible. Some applications might require additional impossible movements like animated super hero martial arts or stretching and squishing that are not possible with real actors.

In biomechanics, sports and training, real time data can provide the necessary information to diagnose problems or suggest ways to improve performance, requiring motion capture technology to capture motions up to 140 miles per hour for a golf swing.



Ahora bien...
unreal, implementan varios tipos de animaciones propias, pero de las que nos mandó investigar, utiliza inverse kinematics y motion capture.

Por otro lado,The Quake III engine utiliza para las animaciones los movimientos de tipo vertex. Con vertex , se logra guardar perfectamente la aniamacion. Se pueden tener muchos keyframes ascocidados a una animacion en un segundo, esto permite que haya mas complejidad, y las aniamciones sean menos pobres.

Tarea formales.

se auncian de modo c,ba...





Tuesday, May 08, 2007

INVESTIGACION........................................................................................................................................
VON WEGEN SOLL ICH DAS BENUTZEN!..ABER AHH HIER IST ES>



Previo

1.- Investigue cómo crear y definir una fuente de luz en OpenGL

- Creación de la luz

- Cuántas luces se pueden definir

- Parametrización de la fuente de la luz

- Tipos de luces

- Color de la luz

- Habilitación/deshabilitación de luces

- Luz por default y características por default

Luces

Como se ha dicho, una luz aporta iluminación a una escena, según unas determinadas componentes de color.

A partir de ahora distinguiremos entre fuente de luz, como entidad que proporciona luz a una escena, y luz, como aportación de esa fuente a la iluminación de la escena.

Una fuente puede emitir tipos diferentes de luz, que son complementarias, y pueden darse a la vez para un cierto tipo de luz.

El primer paso para utilizar la iluminación en una aplicación OpenGL es activarla, mediante la llamada

glEnable(GL_LINGHTING);

Una vez activada, hemos de establecer las propiedades de cada luz en la escena, y activarlas. La especificación inicial de OpenGL contempla que, al menos, cada implementación debe de poder definir 8 luces, identificadas por las constantes GL_LIGHTn, dónde 'n' es el número de la luz, comenzando por 0.

Una vez habilitadas las luces, hay que indicar los parámetros de estas. Para hacer esto utilizaremos las siguientes funciones: glLightf, glLighti, glLightfv, glLightiv. A continuación veremos algunos de los parámetros mas importantes de las luces, y como modificarlos:

· Posición y tipo de luz

Obviamente, si hay una luz, hay que indicar en que posición de la escena esta. Esto se consigue llamando a la siguiente función:

void glLightfv( GLenum light, GLenum pname, const GLfloat *params );

donde light es el identificador de la luz (GL_LIGHTi)

pname = GL_POSITION

params es un vector del tipo GLfloat position[4];

Con esta llamada indicamos dos cosas.

* Si params[3] tiene un valor igual a 0.0, entonces indicamos que es una luz direccional. En este caso el vector (params[0], params[1], params[2]), da el vector de la dirección de la luz.

* Si params[3] tiene un valor igual a 1.0, entonces indicamos que es una luz puntual. El vector (params[0], params[1], params[2]) indica la posición de la luz

· Color de la luz

Los colores de las luces en OpenGL tienen tres componentes:

*Ambiente
Esta componente afecta a todos los objetos de la, independientemente de la posición o orientación de estos. Viene a emular la luz que en el mundo real viene dada por la reflexión difusa en las paredes, la luz solar indirecta, etc.

*Difusa
Se puede pensar en ella como en el verdadero color de la luz. Su influencia sobre una superficie de un objeto depende de su orientación y su distancia.

*Especular
Influye en el brillo que va a tener el objeto.

Estas componentes se pueden modificar con la siguiente función :

void glLightfv( GLenum light, GLenum pname, const GLfloat *params );

donde light es el identificador de la luz (GL_LIGHTi)

pname = GL_AMBIENT, GL_DIFFUSE o GL_SPECULAR, dependiendo de que componente se quiere modificar.

params es un vector del tipo GLfloat color[4];

El formato de params debe ser el siguiente:

* color[3] debe valer 1.0

* (color[0], color[1], color[2]) da el vector RGB del color.

Los materiales de los objetos tienen las mismas componentes que la los colores de la luz. Vienen a indicar la reflectancia del material a cada una de las componentes de la luz. Para indicar el material de los objetos que se van a renderizar a continuación se debe utilizar la siguiente función :

void glMaterialfv( GLenum face, GLenum pname, const GLfloat *params );

Donde :

* face indica a que caras se van a modificar. Puede valer GL_FRONT, GL_BACK o GL_FRONT_AND_BACK, pero casi siempre trabajaremos con GL_FRONT.

* pname indica que componente se va a modificar. Puede valer GL_AMBIENT, GL_DIFFUSE o GL_SPECULAR.

* params es un vector de cuatro componentes que indica el nuevo valor del color. (params [0], params [1], params [2]) da el vector RGB del color. params [3] debe valer 1.0.

Para establecer las propiedades de una luz utilizaremos llamadas a las funciones del tipo glLight*(). Las propiedades de toda luz son las siguientes:

- Posición/Dirección:

Indica la posición/dirección de la luz, y especifica si ésta es una luz posicional o direccional. Una luz posicional tiene una posición concreta en el espacio, mientras que una luz direccional consiste en un conjunto de haces de luz paralelos. Un ejemplo de luz posicional es una lámpara, mientras que, debido a su distancia, podríamos considerar al sol como una fuente de luz direccional. Las luces posicionales pueden ser de dos tipos: luces puntuales, que emiten luz a su alrededor de manera radial, y en todas direcciones, y luces focales, que emiten luz en una dirección concreta, en un radio de acción con forma de cono (como un foco).


Los tipos de luz son:

􀀀 “Emitted” (emitida): es la luz emitida por un objeto. No se ve afectada por ningún otro tipo de luz. Por ejemplo, un fuego emite una determinada luz, y si lo miramos, lo veremos de ese color, independientemente del color de las luces que estén apuntando al fuego.

􀀀 “Diffuse” (difusa): es la luz que índice sobre un objeto, y proviene de un determinado punto. La intensidad con la que se refleja en la superficie del objeto puede depender del ángulo de incidencia, dirección, etc. Una vez incide sobre un objeto se refleja en todas direcciones.

􀀀 “Specular” (especular): es la luz que, al incidir sobre un objeto, se ve reflejada con un ángulo similar al de incidencia. Podemos pensar que es la luz que produce los brillos.

􀀀 “Ambient” (ambiental): podemos considerarlo como los restos de luz que aparecen defido a la reflexión residual de una luz que ya se ha reflejado sobre muchos objetos, y es imposible determinar su procedencia. Es algo así como la iluminación global de una escena.

Para establecer esta propiedad utilizaremos la llamada:

glLightfv(GL_LIGHTn,GL_POSITION,val_ptr);

“val_prt” es un puntero a un vector de cuatro componentes de tipo float, de la forma (x,y,z,w). En el caso de que w sea 1, estaremos ante una luz posicional, y su posición está determinada por (x,y,z). Si w es 0, la luz es direccional, y su dirección es el vector (x,y,z).

- Dirección del foco:

En el caso de una luz focal, debemos establecer su dirección. Esto lo haremos con la llamada:

glLightfv(GL_LIGHTn,GL_SPOT_DIRECTION,val_prt);

“val_ptr” es un puntero a un vector con la dirección, en formato (x,y,z).

- Apertura del foco:

El ángulo de apertura del foco se define mediante:

glLightf(GL_LIGHTn,GL_SPOT_CUTOFF,val);

“val” expresa en grados la mitad del ángulo de apertura del foco.

- Atenuación del foco:

La atenuación del foco (degradación de la intensidad a medida que nos acercamos al borde) se define mediante:

glLightf(GL_LIGHTn,GL_SPOT_EXPONENT,val);

- Intensidad de la luz:

Define el color ambiental, difuso y especular de la luz. Se define mediante la llamada:

glLightfv(GL_LIGHTn,GL_[AMBIENT|DIFFUSE|SPECULAR],val_ptr);

“val_ptr” es un puntero a un vector de cuatro componentes de color RGBA.

- Atenuación de la luz

Define la pérdida de intensidad de la luz a medida que nos alejamos del foco (no afecta a las luces direccionales). Se establece mediante:

glLightf(GL_LIGHTn,GL_[CONSTANT|LINEAR|QUADRATIC]_ATTENUATION,val);



Una vez establecidas las propiedades de una determinada luz, las activaremos con la llamada:

glEnable(GL_LIGHTn);

Para desactivarla se utiliza:

glDisable (GL_LIGTHn);

2.- Investigue la diferencia entre luz e iluminación (light/lighting)

- Habilitación/deshabilitacion de la iluminación (¿cómo?)

-¿Para qué hay que habilitar o deshabilitar la iluminación?

La iluminación de OpenGL se basa en luces y materiales. Una luz es una fuente de iluminación para la escena. Emite un haz de luz de un color determinado, dividido en las tres componentes de color RGB. Un material determina la cantidad de cada color que refleja un objeto determinado.

Por ejemplo, si un objeto tiene un material de color rojo -RGB(1,0,0)-, es decir, refleja todo el color rojo, y es iluminado por una luz blanca -RGB(1,1,1)-, reflejará toda la componente de color rojo, pero nada de la verde y azul, por lo que se verá de color rojo. Si este mismo objeto fuese iluminado por una luz verde -RGB(0,1,0)-, se vería de color negro, al no tener nada de luz roja que poder reflejar. Además, dependiendo del tipo de luz, el color final con el que se vea el objeto puede verse afectado por el ángulo de incidencia de la luz, la distancia a esta, etc.

Para especificar la iluminación de una escena hay que decidir como seran las luces, y cuales seran los materiales de los objetos de las escenas. Por defecto la iluminación esta deshabilitada. Lo primero que hay que hacer si se desea utilizar luces es habilitarlas.

Para activar y desactivar todo el cálculo de iluminación se utiliza:

glEnable(GL_LIGHTING);

glDisable (GL_LIGTHING);

3.- Investigue la definición de propiedades de material a una superficie

-Características de los materiales (difuso, especular, brillantes, emisividad, etc.)

-Cómo se definen

-Sobre qué cara(s) de un polígono se aplican los materiales.

ESPECIFICACIÓN DE LOS MATERIALES.

Antes de empezar a activar luces como unos cosacos tenemos que definir nuestros materiales. Para cada polígono de la escena hay que definir un material de forma que su respuesta a la incidencia de luz varíe según sea el caso.

Por tanto tenemos que decirle a OpenGL de que forma tendrá que tratar a cada trozo de geometría.

Se definen 5 características fundamentales para u material. Estas componentes son:

  • Reflexión Difusa (diffuse) o color de base que reflejaría el objeto si incidiera sobre él una luz pura blanca.

  • Reflexión especular (specular), que se refiere a los “puntos brillantes” de los objetos iluminados.

  • Reflexión ambiental (ambient) define como un objeto (polígono) determinado refleja la luz que no viene directamente de una fuente luminosa sino de la escena entre si.

  • Coeficiente de brillo o “shininess”. Define la cantidad de puntos luminosos y su concentración. Digamos que variando este parámetro podemos conseguir un objeto más o menos cercano al metal por ejemplo.

  • Coeficiente de emisión (emission) o color de la luz que emite el objeto.

Las componentes ambiental y difusa son típicamente iguales o muy semejantes. La componente especular suele ser gris o blanca. El brillo nos determinará el tamaño del punto de máxima reflexión de luz.

Se pueden especificar diferentes parámetros en cuanto al material para cada polígono. Es una tarea ardua pero lógicamente a más variedad de comportamientos más real será la escena. El funcionamiento es el normal en OpenGL. Cada vez que se llama a la correspondiente función se activan esos valores que no cambiarán hasta llamarla de nuevo con otros. Por tanto todo lo que se “renderice” a partir de una llamada heredará esas características. La función es:

GLvoid glMaterialfv(GLenum FACE, GLenum pname,const GLfloat *params );


n los valores que puedan adoptar los parámetros de la función. En este caso de FACE tenemos tres posibilidades dependiendo de si la característica en cuestión debe aplicarse al lado visible (FRONT), al no visible (BACK) o a ambos. En cuanto a pname se define aquí cuál es la característica que vamos a definir en concreto. Las posibilidades son las que hemos comentado para un material. De hecho son bastante obvias si miramos las constantes que podemos usar. Por último *params donde damos los valores concretos de la característica. Son tres valores de hecho tres números reales que especifican un color RGB. Ese color define exactamente como debe verse el objeto que se renderice después en cuanto a color ambiente, difusión, componente especular, etc..

Hay una excepción en el caso de GL_SHININESS. Si usamos esta constante como segundo parámetro, el tercero tendrá que ser un número entre 0 y 128 que controlará la concentración del brillo. Por defecto este valor vale 0.

La misma función tiene también las formas glMateriali y glMaterialiv. No suelen usarse por eso las versiones llamadas escalares (enteral) ya que sólo son útiles para definir GL_SHININESS.

Valores típicos, son los usados por defecto, son de 0.8 para las tres componentes en GL_DIFFUSE, de 0.2 para GL_AMBIENT y 0.0 en GL_EMISSION y GL_SPECULAR. Por supuesto tendremos que retocar estos valores hasta conseguir el efecto deseado.

4.- Investigue acerca del mapeo de texturas en OpenGL

- Carga/definición del bitmap

- Transformaciones de la textura

- Modos de aplicación de una textura(repeat, wrap, etc.)

- Mapeo de la textura a una superficie

Texturas

Las texturas permiten personalizar aún más el material de un objeto, y nos permiten obtener imágenes mucho más realistas en nuestras escenas. Por ejemplo, si queremos dibujar una pared de ladrillo, tenemos dos opciones: dibujar cada ladrillo, definiendo su material con colores, y dibujar el cemento entre los ladrillos, a base de polígonos, definiendo también su material, o dibujar un único cuadrado con la extensión de la pared, y hacer que su material, sea, por ejemplo, la foto de una pared de ladrillo.

Poner texturas a un objeto es como poner papel pintado en una pared. Cuando se esta dibujando un polígono, es posible indicar que se dibuje este polígono con una imagen, indicándole para cada vértice del polígono, que posición de la imagen le corresponde. Es posible mapear texturas en una, dos y tres dimensiones.

Para dibujar un objeto con texturas hay que seguir los siguientes pasos:

· Habilitar el mapeado de texturas

Esto se hace ejecutando la siguiente instrucción:

glEnable(GL_TEXTURE_2D);

· Especificar que imagen va a ser utilizada como textura

Para ello se utiliza la siguiente función:

void glTexImage2D( GLenum target, GLint level, GLint components, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels );

Donde:

· target debe valer GL_TEXTURE_2D

· level indica el nivel de detalle de la textura. Esto no se explica aquí, y habitualmente tiene un valor 0.

· components indica el nº de componentes del color. Usualmente se usan componentes RGB, y especificaremos 3. Pero también se pueden hacer texturas semitransparentes, con lo que se utiliza un formato RGBA (4 componentes). En ese caso indicaríamos un valor de 4.

· width indica el ancho de la imagen de la textura. Debe ser una potencia de 2.

· height indica el alto de la imagen de la textura. Debe ser una potencia de 2.

· border indica si se utiliza un borde en la textura (1) o no (0). Usualmente es 0.

· format indica el formato del valor de cada pixel. Normalmente se utiliza GL_RGB.

· type indica el tipo de datos usado para cada componente del valor de un pixel. Puede ser uno de los siguientes valores: GL_UNSIGNED_BYTE, GL_BYTE, GL_BITMAP, GL_UNSIGNED_SHORT, GL_SHORT, GL_UNSIGNED_INT, GL_INT o GL_FLOAT.

· pixels es un puntero al mapa de valores de los pixels. Es la imagen en si.

La imagen se puede obtener de dos formas. Una, generándola con código del propio programa. Esto es fácil si la textura es sencilla, como puede ser un tablero de ajedrez. Si la imagen es más complicada, hay que cargarla de un archivo. En el ejemplo que damos aquí, se muestra una función para cargar una textura a partir de un archivo BMP de Windows.

· Mapear la textura

Cuando se esta dibujando el objeto, hay que indicar, para cada vértice de este, que posición de la textura le corresponde. Esto se hace mediante la siguiente función:

void glTexCoord2f( GLfloat s, GLfloat t);

Donde (s,t) indica una posición sobre el mapa de la imagen.

Lo que se hace es indicar la coordenada de la textura antes de indicar el vértice del polígono. A continuación vamos a ver dos funciones, donde se dibujan un cuadrado y un triangulo, indicando las posiciones de la textura:

void Cuadrado(void)

{

glBegin(GL_QUADS);

glTexCoord2f(0.0,1.0);glVertex3f(-1.0,1.0,0.0);

glTexCoord2f(1.0,1.0);glVertex3f(1.0,1.0,0.0);

glTexCoord2f(1.0,0.0);glVertex3f(1.0,-1.0,0.0);

glTexCoord2f(0.0,0.0);glVertex3f(-1.0,-1.0,0.0);

glEnd();

}

void Triangulo(void)

{

glBegin(GL_QUADS);

glTexCoord2f(0.0,1.0);glVertex3f(-1.0,1.0,0.0);

glTexCoord2f(1.0,0.0);glVertex3f(1.0,-1.0,0.0);

glTexCoord2f(0.0,0.0);glVertex3f(-1.0,-1.0,0.0);

glEnd();

}

* Indicar como la textura va a ser aplicada a cada pixel

Aquí hay varios puntos que indicar. El primero de ellos es indicar que ocurre con el tamaño de las texturas. Cuando uno referencia las coordenadas de las texturas, se indican valores entre 0 y 1, que dan los límites de las texturas. Cuando uno referencia un valor mayor que 1 o menor que 0, se esta fuera del mapa de la imagen. ¿Que hacer en estos casos?. Hay dos posibilidades. La primera es repetir los píxeles de los bordes de la textura cuando se referencie fuera de ella, lo cual no parece que tenga mucha utilidad. La otra posibilidad es la de repetir la textura. Esto es, en lugar de tener un mapa con solo una imagen, se tiene un mapa donde la imagen de la textura esta repetida infinitas veces, unas contiguas a las otras.

Imaginemos que tenemos que la imagen de la textura es la imagen de una baldosa, y queremos dibujar un suelo que vaya a contener 20x20 baldosas. Entonces, para dibujar este suelo solo tendríamos que poner un código tal que así:

glBegin(GL_QUADS);

glTexCoord2f(0.0,20.0);glVertex3f(-1.0,1.0,0.0);

glTexCoord2f(20.0,20.0);glVertex3f(1.0,1.0,0.0);

glTexCoord2f(20.0,0.0);glVertex3f(1.0,-1.0,0.0);

glTexCoord2f(0.0,0.0);glVertex3f(-1.0,-1.0,0.0);

glEnd();

Para indicar si se quiere repetir el borde de la textura, o se quiere repetir la textura completa se utiliza la siguiente función:

void glTexParameterf( GLenum target, GLenum pname, GLfloat param );

Donde:

* target debe valer GL_TEXTURE_2D.

* pname puede valer GL_TEXTURE_WRAP_S o GL_TEXTURE_WRAP_T, donde el primero indica las coordenadas X de la textura, y el segundo las coordenadas Y.

* param indica si queremos que se repita el borde de la textura (GL_CLAMP) o si queremos que se repita la textura completa (GL_REPEAT).

Otro de los parámetros a tener en cuenta es el filtrado de las texturas. Cuando la cámara esta muy cerca de un objeto con texturas, debido al efecto del mapeado se pueden notar con mucha claridad la diferencia entre los píxeles contiguos de la textura, que se ven como unos cuadrados mas grandes cuanto mas cerca se esta del objeto.

Un efecto desagradable aparece también cuando se esta lejos de las texturas. Si se tiene objeto lejano con una textura del tipo de un tablero de ajedrez, debido a que solo se dibujan algunos de los píxeles de la textura, pueden aparecer formas muy extrañas en la textura.

Si se quiere evitar de forma parcial este efecto, existe la posibilidad de filtrar las texturas. Esto se hace con la siguiente llamada :

void glTexParameterf( GLenum target, GLenum pname, GLfloat param );

Donde:

* target debe valer GL_TEXTURE_2D.

* pname puede valer GL_TEXTURE_MIN_FILTER o GL_TEXTURE_MAG_FILTER, segun se este especificando un filtro para cuando la textura este lejos o cerca.

*param indica el tipo de filtro a aplicar. Puede valer GL_NEAREST o GL_LINEAR. El primero indica que no se filtran las texturas. El segundo indica que se va ha hacer un filtrado lineal de las texturas. Hay que tener en cuenta que aplicar un filtrado a las texturas es muy costoso en tiempo.

Las coordenadas de textura

Para saber qué partes de una imagen se dibujan en un polígono (por ejemplo un triángulo), utilizamos lo que se denominan coordenadas de textura, o coordenadas UV.



De esta manera, si queremos dibujar un triángulo con una textura, y aplicamos a cada vértice las coordenadas de textura indicadas en la figura, tenemos el siguiente resultado:


Aplicar las texturas

Para aplicar una textura tenemos que seguir una serie de pasos muy definidos:

1. Creamos la textura

2. Definimos las condiciones en que se va a aplicar la textura

3. Habilitar la aplicación de texturas

4. Dibujar las escenas, proporcionando las coordenadas de textura

Como ejemplo sencillo, vamos a ir viendo cómo se ha creado el ejemplo del mapeado de texturas:

- Creación de la textura [TEXT]:

Primero hemos de obtener un identificador para la textura. Para ello pedimos a OpenGL que nos devuelva un identificador de textura libre:

int texture;

glGenTextures(1,&texture);

Activamos la textura como textura activa:

glBindTexture(GL_TEXTURE_2D,texture);

Creamos la textura. El modo más sencillo de hacerlo es a través de una función de GLU que crea la textura y sus variaciones a aplicar según la distancia a la que se encuentre:

gluBuild2DMipmaps( GL_TEXTURE_2D, gimp_image.bytes_per_pixel,

gimp_image.width, gimp_image.height,GL_RGB, GL_UNSIGNED_BYTE,

gimp_image.pixel_data );

Con el primer parámetro indicamos el tipo de textura. En este caso (GL_TEXTURE_2D) es una textura 2D. Los siguentes parámetros indican el número de bytes por cada pixel, que dependerán de la imágen (p. ej. 3 para RGB y 4 para RGBA; en este caso, la imágen es de tipo RGB), su anchura y altura (que han de ser pares, y muy recomendable, múltiplos de 2), el formato de los datos (GL_RGB, GL_RGBA,...), el tipo de los datos, que en nuestro caso vendrá dado por bytes sin signo, por lo que usaremos la constante GL_UNSIGNED_BYTE, y finalmente, un puntero a los datos.

En este ejemplo, hemos utilizado una estructura para generar la imagen con el siguiente formato:

static const struct {

unsigned int width;

unsigned int height;

unsigned int bytes_per_pixel; /* 3:RGB, 4:RGBA */

unsigned char pixel_data[128 * 128 * 3 + 1];

} gimp_image = {

128, 128, 3, [...] }

Este formato de datos es producido de manera automática por el programa “Gimp” al grabar una imágen como un archivo en “c”.

- Definir las condiciones en que se va a aplicar la textura:

Estas condiciones se establecen a través de la función glTexEnvf().

glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

En nuestro caso, hemos especificado que la textura se va a fundir con el color de fondo del polígono (GL_MODULATE), aunque bien podría sustituirlo (usando GL_REPLACE).

La fusión con el color de fondo se utiliza para aplicar la iluminación a un objeto con textura. En el ejemplo vemos cómo se visualiza un objeto iluminado, con la textura aplicada mediante GL_MODULATE, y mediante GL_REPLACE:


- Habilitar la aplicación de texturas:

Mediante:

glEnable(GL_TEXTURE_2D);

- Dibujar la geometría proporcionando las coordenadas de textura de cada vértice:

glBegin(GL_TRIANGLES);

glTexCoord2d(0.0,1.0);

glVertex3f(-0.5,-0.5,0.5);

glTexCoord2d(1.0,1.0);

glVertex3f(0.5,-0.5,0.5);

glTexCoord2d(0.5,0.0);

glVertex3f(0.0,0.5,0.5);

glEnd();

Si en algún momento queremos cambiar la textura activa, tan sólo hemos de indicar qué textura queremos activar mediante la llamada:

glBindTexture(GL_TEXTURE_2D,texture);

“texture” es el identificador de la textura que queremos activar.

El código completo de los ejemplos utilizados puede verse en el anexo A.

Repetición de texturas

Imaginemos que queremos dibujar una pared de ladrillos junto a un césped, y tenemos estas dos texturas, para la pared y el suelo:


El suelo va a ser u cuadrado, y la pared un rectángulo, y vamos a mapear las esquinas del suelo y de la pared con las esquinas de de las texturas (código completo en el anexo A):

glBindTexture(GL_TEXTURE_2D,texture_floor);

glBegin(GL_QUADS);

glTexCoord2d(0.0,0.0);

glVertex3f(-6.0,0.0,-6.0);

glTexCoord2d(0.0,1.0);

glVertex3f(-6.0,0.0,6.0);

glTexCoord2d(1.0,1.0);

glVertex3f(6.0,0.0,6.0);

glTexCoord2d(1.0,0.0);

glVertex3f(6.0,0.0,-6.0);

glEnd();

glBindTexture(GL_TEXTURE_2D,texture_wall);

glBegin(GL_QUADS);

glTexCoord2d(0.0,0.0);

glVertex3f(-6.0,4.0,-6.0);

glTexCoord2d(0.0,1.0);

glVertex3f(-6.0,0.0,-6.0);

glTexCoord2d(1.0,1.0);

glVertex3f(6.0,0.0,-6.0);

glTexCoord2d(1.0,0.0);

glVertex3f(6.0,4.0,-6.0);

glEnd();

Un aspecto a resaltar es que la operación glBindTexture, para seleccionar cuál es la textura activa en este momento, tiene que realizarse fuera del contexto de glBegin/glEnd, o de lo contrario no funcionará.



Como podemos observar, los ladrillos de la pared salen demasiado alargados, y el suelo demasiado distorsionado, por el hecho de que estamos agrandando las texturas para acomodarlas al tamaño de los objetos.

Pero, ¿y si repetimos las texturas? Por ejemplo, podríamos repetir la textura de la en 3 divisiones verticales, y la del suelo 36 veces (6 horizontales x 6 verticales).

Para ello, debemos especificar que queremos repetir las texturas con las siguientes llamadas a función:

glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);

glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);

La primera permite que las texturas se repitan en horizontal, y la segunda, en vertical. Con este cambio, podríamos mapear las texturas con coordenadas mayores a 1, de manera que las texturas se repetirían, de esta manera:

glBindTexture(GL_TEXTURE_2D,texture_floor);

glBegin(GL_QUADS);

glTexCoord2d(0.0,0.0);

glVertex3f(-6.0,0.0,-6.0);

glTexCoord2d(0.0,6.0);

glVertex3f(-6.0,0.0,6.0);

glTexCoord2d(6.0,6.0);

glVertex3f(6.0,0.0,6.0);

glTexCoord2d(6.0,0.0);

glVertex3f(6.0,0.0,-6.0);

glEnd();

glBindTexture(GL_TEXTURE_2D,texture_wall);

glBegin(GL_QUADS);

glTexCoord2d(0.0,0.0);

glVertex3f(-6.0,4.0,-6.0);

glTexCoord2d(0.0,1.0);

glVertex3f(-6.0,0.0,-6.0);

glTexCoord2d(3.0,1.0);

glVertex3f(6.0,0.0,-6.0);

glTexCoord2d(3.0,0.0);

glVertex3f(6.0,4.0,-6.0);

glEnd();

La textura de la pared ha sido mapeada entre (0,0) y (3,1), para repetirla 3 veces horizontalmente, y la textura del suelo entre (0,0) y (6,6). El resultado es sensiblemente distinto: