lunes, 30 de junio de 2014

Embellece tu juego 2D - Normal Mapping

Muchos de los juegos 2D que muestran iluminación dinámica se basan en una técnica llamada Color Blending. Con esta técnica se puede simular la iluminación de una textura como se ve en la imagen del enlace anterior.

Para mejorar los gráficos 2D se pueden emplear técnicas de iluminación 3D que embellecen enormemente el resultado final.

La primera técnica que podemos aplicar es NormalMapping. Esta técnica define la dirección hacia la que está orientado cada pixel en la textura por lo que podemos calcular como incide la luz en él.


Un normal map es una textura con 3 canales (RGB) donde cada canal representa una dimensión (XYZ) en un mundo tridimensional. Con el normal map sabemos hacia donde "mira" el pixel.

Textura de nuestro juego:




























Normal Map:





























Para calcular la luz realizamos el dot product del pixel del normal map (que representa un vector con dirección XYZ) con la dirección (otro vector) invertida de la luz con respecto a ese pixel. ¿Y por qué invertida? Porque lo que calculamos con el dot product es el ángulo de 2 vectores partiendo desde la posición del pixel. También hay que tener en cuenta que una textura no permite valores negativos, por lo que al leer el normal map hay que transformarlo de 0...1 a 1...-1

Supongamos que [a] es el vector del pixel y que [b] es el vector de la dirección de la luz invertida. Si no estuviese invertido el vector de la dirección de la luz la flecha apuntaría hacia la base de [a] y el resultado de hacer el dot product no daría el resultado esperado para calcular la cantidad de luz que incide en el pixel:


Sólo resta escribir un pixel shader que reciba la textura de color, el normal map y la información con respecto a la fuente de luz:
//posición en pantalla de la luz, eje Z = 400 asumiendo que la textura esta en Z = 0
float3 LightPos = float3(0,0,400);
//color de la luz, blanca
float3 LightColor = float3(1,1,1);
//luz de ambiente en caso de que la luz no incida en el pixel
float AmbientLight = .3f;
//color de la luz ambiental
float3 AmbientLightColor = float3(1,1,1);

//textura
sampler TextureSampler : register(s0);
//normal map
sampler NormalSampler : register(s1);

//obtenemos el color del pixel de la textura
 float4 tex = tex2D(TextureSampler, texCoord);
//obtenemos el color del pixel del normal map y lo transformamos al rango 1...-1
float4 normal =(2.0 * (tex2D(NormalSampler, texCoord)))-1.0;

//dirección de la luz invertida (o tambien < LightPos - pixelPos > para ahorrarnos el paso de invertirlo )
 float3 LightDirection = -(pixelPos - LightPos);
//normalizamos el verctor por que nos interesa la direccion no la magnitud
LightDirection= normalize(LightDirection);
 //calculamos cuanto incide la luz en el pixel
float D = saturate(dot(normal, LightDirection));

//color final es el color del pixel multiplicado por la luz ambiental (con su color) y le sumamos la cantidad de luz que incide en el pixel (con su color)
tex.rgb *= ((AmbientLight * AmbientLightColor)  + (D*LightColor));
Y este es el resultado con la luz en la esquina superior izquierda como hemos puesto en el ejemplo de código:


No hay comentarios:

Publicar un comentario