jueves, 27 de mayo de 2010

Bump Mapping

Después de tirarme una semana bastante agobiada implementando y entendiendo estoy, voy a hacer mi propio tutorial ahora que he conseguido que funcione y lo entiendo :P. Y antes de empezar os enseño el resultado para que veáis como mola :D.


Imaginaos un escenario de un juego en tres dimensiones con muchos elementos, y cada elemento con sus detalles. Cada uno de los objetos de este escenario, en su geometría, tendrá todos los detalles representados como triángulos. Por lo que todo el escenario podría tener miles de millones de triángulos para dibujar. Cuando nos moviésemos por este escenario, todo iría muy lento porque habría que pintar en la pantalla todos los triángulos, asignarles color y textura, iluminación, ... En resumen que sería imposible jugar de una fluida.

Una solución sería disminuir la complejidad geométrica de los objetos, es decir, hacer que tengan menos triángulos. El problema de esta solución es que cuantos menos triángulos dibujes menos detalles se verán en la escena, pero todo iría mucho más fluido ...

En este caso se puede aplicar una textura, de manera que se dé una apariencia realista al objeto. Aunque aplicando sólo la textura el objeto no se va a ver igual que si dibujásemos toda la geometría, se verá todo un poco plano ...

Pero ¿y si pudiésemos simular un objeto complejo con una geometría simple y una textura? Justo esto es lo que hace el bump mapping.

El bump mapping lo que hace es modificar la iluminación en cada uno de los puntos del objeto, de tal manera que el objeto parezca mucho más complejo aunque no lo sea :P.

Para modificar la iluminación en cada punto del objeto habrá que tener en cuenta la posición de la luz, la normal cada punto del objeto y la normal en cada punto de la textura. Como veis nos estamos moviendo en tres sistemas diferentes, en el mundo, en el espacio del objeto y en el espacio de textura (tangent space), respectivamente. Por lo que habrá que pasar de uno a otro para poder aplicar las perturbaciones en las normales del objeto.

Más o menos os voy a definir por pasos lo que habría que hacer y como hacerlo en cada caso:
  1. Generar el mapa de normales de la textura. En mi caso, no lo he generado en el programa sino que haciendo uso del programa Crazybump he generado una imagen con el mapa de normales de la textura que vaya a usarse.

    Este mapa de normales también se podría generar de forma automática de la siguiente manera:

    Si tenemos una imagen de (w, h) pixeles, y tenemos el heighmap de la textura, básicamente la textura en blanco y negro podemos calcular el mapa de normales de la siguiente manera:

    t = (1, 0, heighmap[i+1][j] - heigmap[i-1][j]);
    b = (0, 1, heighmap[i][j+1] - heigmap[i][j-1]);
    n = t x b (producto vectorial de t por b)


    n estará en el rango [-1, 1], así que para poder codificarlo como imagen habrá que pasarlo al rango [0, 1] de la siguiente manera:

    n_compress = n/2 + 0.5;

    Pero por eficiencia decidí crearla fuera del programa.

    Este mapa de normales, el rango de valores variará de [-1,1]. Pero como se almacena en una imagen este rango irá de [0,1]. Por lo que cuando se vayan a usar los valores del mapa de normales habrá que descomprimirlos de la siguiente manera:

    n = 2*(n_compress-0.5);

  2. Pasar de coordenadas de mundo a coordenadas de objeto es algo fácil, ya que ambos mundo son mundos en 3D. El problema viene al pasar del espacio del objeto al espacio de textura, ya que es pasar de 3D a 2D. Para realizar el cambio de uno a otro será necesario crear la matriz TBN y su inversa. En la siguiente imagen vemos un esquema de como sería el espacio de textura:


    donde S es la tangente en el punto en la dirección del eje X, T es la tangente en el punto en la dirección del eje Y, y N indica el eje Z, que sale hacia fuera. Cada vértice de los triángulos que forman el objeto le corresponde su propio espacio de textura. De esta forma, si ponemos los ejes en forma de matriz obtendremos la matriz TBN (Tangent Binormal Normal) (que corresponden a los ejes S, T y N, respectivamente). La forma de calcular esta matriz es:

    Tenemos un triángulo con vértices V1, V2, V3 y coordenadas de textura asociadas a esos vértices C1, C2, C3.

    C2C1T = C2.x - C1.x;
    C2C1B = C2.y - C1.y;
    C3C1T = C3.x - C1.x;
    C3C1B = C3.y - C1.y;
    V2V1 = V2 - V1;
    V3V1 = V3 - V1;

    T= (C3C1B * V2V1 - C2C1B * V3V1)/(C2C1T*C3C1B - C3C1T*C2C1B);
    B= (C3C1T * V2V1 - C2C1T * V3V1)/(C2C1T*C3C1B - C3C1T*C2C1B);
    N = TxB (producto vectorial de T por B)


    Para calcular la inversa de la matriz TBN se haría:

    A1 = BxN;(producto vectorial de B por N)
    A2 = NxT;(producto vectorial de N por T)
    A3 = TxB;(producto vectorial de T por B)
    denominador = ((T.x*B.y*N.z - T.z*B.y*N.x) + (B.x*N.y*T.z - B.z*N.y*T.x) + (N.x*T.y*B.z - N.z*T.y*B.x));
    Tinv = (A1.x, -A1.y, A1.z) / denominador;
    Binv = (-A2.x, A2.y, -A2.z) / denominador;
    Ninv = (A3.x, -A3.y, A3.z) / denominador;


  3. Calcular el nuevo color para cada uno de los puntos de la imagen de la siguiente manera:

    I = D1*Dm*clamp(L·N, 0, 1)

    donde D1 es el color difuso de la luz,
    Dm es el color difuso del material del objeto,
    L es el vector que va desde un punto de la superficie a la luz,
    N es la normal de la superficie en un punto.
Esto no va todo implementado en openGL sino que para agilizar se ha implementado en GPU (tarjeta gráfica) usando el lenguaje de programación CG. Si queréis saber algo más de CG lo suyo es que lo miréis en la página de nvidia.

Habrá que generar dos programitas uno para los vértices y otro para los fragmentos (puntos que pertenecen al objeto).

En el programa de vértices lo que haremos es transformar la posición de la luz al espacio de textura.

vlight = vlightPosition - position.xyz; //así pasamos la luz al espacio del objeto
vlight.xyz = mul(TBNMatrix, vlight);

El programa para los fragmentos lo que hace es calcular el color en cada punto.

vlight = normalize(vlight);
vNormal = 2.0f * (tex2D(normalTexture, normalCoords).rgb - 0.5); //descomprimo el valoor del normal map
colorOUT.rgb = fLightDiffuseColor * tex2D(baseTexture, texCoords).rgb * saturate(dot(vlight, vNormal)); //calculamos el color final, sturate = clamp

Lo que sí os voy a decir es como añadir a vuestro programa en opengGL el vertex y el fragment shaders:
  1. Inicialización

    bool esfera::InitCG(){
    // cg context

    g_context = cgCreateContext();


    // Find the best matching profile for the fragment shader

    g_fragmentProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT);

    cgGLSetOptimalOptions(g_fragmentProfile);

    if (g_fragmentProfile == CG_PROFILE_UNKNOWN){
    return false;

    }


    // Find the best matching profile for the vertex shader

    g_vertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX);

    cgGLSetOptimalOptions(g_vertexProfile);


    if (g_vertexProfile == CG_PROFILE_UNKNOWN) {
    return false;

    }


    // Create the fragment program.

    g_fragmentProgram = cgCreateProgramFromFile(g_context, CG_SOURCE, "FragmentShader.cg", g_fragmentProfile, "main", 0);


    if (!g_fragmentProgram)
    return false;


    // Load the fragment program

    cgGLLoadProgram(g_fragmentProgram);


    // Create the vertex program

    g_vertexProgram = cgCreateProgramFromFile(g_context, CG_SOURCE, "VertexShader.cg", g_vertexProfile, "main", 0);


    if (!g_vertexProgram)
    return false;


    // Load the vertex program

    cgGLLoadProgram(g_vertexProgram);


    // Get the parameters which we can pass to the vertex and fragment shaders.

    g_modelViewMatrix = cgGetNamedParameter(g_vertexProgram, "modelViewProjMatrix");

    g_lightPosition = cgGetNamedParameter(g_vertexProgram, "vLightPosition");

    g_lightDiffuseColor = cgGetNamedParameter(g_fragmentProgram, "fLightDiffuseColor");


    return true;

    }


  2. Uso

    // Enable the vertex and fragment profiles and bind the vertex and fragment programs
    cgGLEnableProfile(g_vertexProfile);

    cgGLEnableProfile(g_fragmentProfile);


    cgGLBindProgram(g_vertexProgram);

    cgGLBindProgram(g_fragmentProgram);


    // Set the "modelViewProjMatrix" parameter in the vertex shader to the current concatenated

    // modelview and projection matrix

    cgGLSetStateMatrixParameter(g_modelViewMatrix,
    CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);


    // Set the light position parameter in the vertex shader

    cgGLSetParameter3f(g_lightPosition, pos[0], pos[1], pos[2]);


    // Set the diffuse of the light in the fragment shader

    cgGLSetParameter3f(g_lightDiffuseColor, color_difusa[0], color_difusa[1], color_difusa[2]);


    //codigo para habilitar las texturas


    //codigo para dibujar el objeto


    cgGLDisableProfile(g_vertexProfile);

    cgGLDisableProfile(g_fragmentProfile);


    glActiveTextureARB(GL_TEXTURE0_ARB);
    glDisable(GL_TEXTURE_2D);


    glActiveTextureARB(GL_TEXTURE1_ARB);

    glDisable(GL_TEXTURE_2D);


  3. Destrucción de los parámetros

    void esfera::DestroyCG(){
    // Destroying the CG context automatically destroys all attached CG programs

    cgDestroyContext(g_context);

    }


El código lo podeis descargar de aquí.

El programa está hecho en linux, más concretamente en Kubuntu 9.10. Para poder compilar el programa y que todo funcione hace falta que instaleis lo siguiente:
  • CG toolkit, que se puede descargar aquí.
    También se puede instalar mediante apt-get de la siguiente forma:
    sudo apt-get install nvidia-cg-toolkit

  • Tener instalado openGL. Si teneis una tarjeta gráfica nvidia al instalar los drivers instala todo lo necesario para usar openGL. Si no teneis tarjeta gráfica o no sabeis como instalar openGL, podeis usar MESA que es un emulador.

  • Tener instalado la librería gráfica para el manejo de ventanas glut:
    sudo apt-get install freeglut3 freeglut3-dbg freeglut3-dev ftgl-dev gle-doc glut-doc glutg3 glutg3-dev

  • Tener instalado las librerías para manipulación de imágenes SDL:
    apt-get install libsdl1.2debian apt-get install libsdl1.2-dev apt-get install libsdl-image1.2 apt-get install libsdl-image1.2-dev apt-get install libsdl-mixer1.2 apt-get install libsdl-mixer1.2-dev apt-get install libsdl-ttf2.0-0 apt-get install libsdl-ttf2.0-dev
Fuentes:

El nokia 5230 ya es mío :D


La semana pasada, después de pensármelo bastante de ver todo tipo de móviles y de ofertas, me compré mi móvil nuevo. Realmente ha sido un poco por capricho, y porque ya me había cansado del otro ... Y bueno el cambio ha sido a bastante mejor en muchos aspectos, por ahora la diferencia entre el Sony Ericsson W580i y el Nokia 5230 es bastante grande. Empezando por que uno es de los que se deslizan y el otro táctil, las pantallas no deberían de ser comparables, pero la verdad es un gustazo la mega pantalla del nokia :P. La cámara tiene los mismo megapíxeles, 2, pero el nokia viene con el sistema operativo simbian, lo que significa que se le podrán instalar cositas :D. Y bueno lo del gps integrado también le da un punto a favor.

En resumen, que estoy contentísima con el móvil nuevo. Ahora sólo me queda trastearlo y comprarle una funda antigolpes, que a veces parece que tengo las manos de mantequilla :P.


lunes, 17 de mayo de 2010

How to create a succesful Blog :)

Admito que mi blog no es especialmente exitoso, de hecho diría que poca gente lo lee, pero bueno aquí está y yo le hecho de vez en cuando mi ratillo. De hecho diría que el problema es que escribo de cuando en cuando ... Pero ese no es el caso :P.

Esta entrada viene porque me ha hecho mucha gracia la nueva viñeta de XKCD y bueno habla de como crear un blog con éxito xD. Os lo dejo por aquí aunque os invito a que entréis en la página y veáis los comentarios del autor, que suelen ser lo mejor :)


jueves, 6 de mayo de 2010

Escaneándome ;)

Justo la semana pasada acabé las clases del máster, y durante estos días que he estado sin clase me he dedicado a intentar instalar las cosillas que me hacían falta para ahora hacer los trabajos durante este mes :).

Uno de los módulos que hemos tenido justo este último mes ha sido el de Digitalización 3D. Las primeras clases fueron con Roberto Scopigno, un tiillo bastante famoso porque fue el que digitalizó el David de Miguel Angel :P, y bueno su grupo de investigación es el creador de Meshlab, un software para tratamiento de mallas 3D bastante sencillo de usar :).

En las siguientes clases con los profesores de la ETSIIT, Pedro y Javi, pues nos dedicamos a escanear cosillas y procesar los datos para obtener modelos 3D correctos de los objetos :). En uno de esos escaneos pues nos escaneamos a nosotros mismos, no enteros pero si la cara :D. Justo acabo de poder instalar el Geomagic y así poder ver y moverme un poco por lo que hicimos aquellos días :). Las imágenes que os son mi cara escaneada ^^, no están procesadas sino que son capturas de los 3 escaneos que hicimos y un merge de las 3, así que no están muy bien y vereis cosas raras que salen de mi frente xD.



martes, 4 de mayo de 2010

Felicidades Peque!

Hoy es un día especial, porque Carlos cumple años ... sólo 21. Y bueno desde aquí tb quiero felicitarlo :)


Felicidades amor!!! Te quiero :)

Parque Warner ^^

El sábado pasado para celebrar el cumple de Carlos subimos unos pocos a Madrid al parque de la Warner, para echar un diilla allí :). Al principio, como suele pasar, se apuntó todo el mundo, pero al final fuimos sólo 4, Carlos, René, Ali y yo :). Y pa que más que no lo pasamos estupendamente :).

Como lo que pretendíamos era subir y bajar en el mismo día, el sábado tocó madrugar bastante, como si fuese a ir a trabajar ... Me recogieron en mi casita y ahí empezó el viaje rumbo a Madrid. El viaje a Madrid fue bastante tranquilo y no se me hizo mu largo, supongo que como salimos bastante temprano ...

Pero a las 4 horas o así aterrizamos en el parque y que guay :). Pa mi era la segunda vez que iba, aunque como si fuese la primera que con mis amigas no me pude montar en casi ninguna montaña rusa ...



Lo primero que hicimos fue explorar :), hicimos unas cuantas fotillos en la fuente de la entrada, con la limosina negra :D. Con forme íbamos encontrando algo que nos gustase pues ahí que nos parábamos a echarnos una foto ^^.








Por la mañana hicimos ronda de montañas rusas en la sección de los superheroes :D, primero al Superman, luego a la de Batman (en esta pillé un mareo considerable la verdad ...), y la caída libre que estuvo chulísima ^^.







Desde allí fuimos al Old West Territory ;), y bueno dimos una vuelta y nos perdimos porque no encontrábamos la montaña rusa xD. Pero cuando la encontramos estuvo chulísima ^^, sin loopings pero con un montón de caídas :D.






Por esa zona ya empezamos a ver gente con sus bocadillos, y nosotros pensamos que tb podríamos comer dentro del parque aunque nos dijeran que no se podía ... :P.

Desde allí fuimos a Cartoon Village, pero un poco de paso la verdad, nos montamos en una nave y en el coche de los Picapiedra :D, y nos hicimos un par de fotillos por allí. Esa era más la zona de los niños chiquitillos.








Fuimos hacia allí porque queríamos ir a la última montaña rusa que quedaba, pero al final pasamos de montarnos ... justo nos habíamos comido unas patatillas y eso de que me dejaran colgada de la seguridad como que no me molaba mucho ... Así que como era hora de comer pues salimos fuera a por los bocadillos de Rene y Ali, y nos quedamos comiendo fuera :).


Por la tarde fue un poco más relajado. Estuvimos por Cartoon Village dando una vuelta, viendo la casa de la Abuelita, la de Bugs Bunny, y las cosillas que había por allí :).





Fuimos a una de las tiendas en Cartoon Village para que Rene y Ali se compraran unos chubasqueros. En la tienda nos encontramos con el Marvin, el marcianito :P.


Y después ... nos montamos en los rápidos, y que gracia, no podíamos parar de reírnos jejeje. No nos mojamos mucho, al menos Ali y yo no, aunque yo al principio vi una mega ola que iba pa Rene y Carlos jejeje.


Ya que estábamos mojaillos pensamos en montarnos en el resto de atracciones de agua. Yo ya pasé de más agua aunque ellos si que se montaron. Yo me quedé fuera haciéndoles fotos en las barcas :D. Se montaron en dos más, una que parece que te mojabas menos, por como salieron y en otra en la que salieron chorreando. Carlos que era el que no tenía chubasquero acabó mojado entero ... así está ahora resfriado :S.






Después de que se mojaran fuimos a montarnos al Superman otra vez, bueno otras dos veces más xD. Y que genial, probamos diferentes sitios, los últimos y casi los primeros. Y al final vas ultra rápido, casi no me daba tiempo a asimilar que es lo que me pasaba xD. Estuvo muy chulo!! Fue la atracción que más me gustó :D.


Estábamos un poquillo mareaillos y fuimos a algo "menos mareante", al hotel encantado, que resultó ser un mareo mayor jejeje. Y de allí a ver el simulador de Batman, que hicimos una cola gigantesca :S, y la verdad fue un poco cacotilla ...

Para terminar la tarde nos fuimos a unos de los barecillos a tomarnos un cafelillo para espabilarnos que en poco rato teníamos que estar saliendo para Granada otra vez ... Y fue un rato bastante agradable, sentaillos en una mesa con nuestro cafes en la terracilla :).

Antes de irnos pasamos por la tienda de regalos y compramos algunas cosillas, bueno yo compré una cosa, una taza gigante de Bugs Bunny :), y Carlos se compró otra más chiquitilla :P.

En el viaje de vuelta estaba hecha pedazos, no podía con mi alma jejeje, no sé como aguanté la mayor parte del viaje despierta :P.

En resumen, el día estuvo genial, me lo pasé super bien y acabé cansaisima :). Y bueno en verano habrá que repetir algo parecido pero a Port Aventura ;).