martes, 1 de julio de 2014

Embellece tu juego 2D - Point Light

La técnica de normal mapping del post anterior queda muy bien, pero nos falta mejorar un poco el punto de luz creado para iluminar la textura. De momento el punto de luz ilumina toda la textura al completo pero es probable que en nuestro motor gráfico necesitemos definir un punto de luz con un rango finito. Para ello, vamos a añadirle la atenuación.


Las modificaciones al shader son sencillas, básicamente, lo que vamos a hacer, es establecer un radio al punto de luz y después mirar si la distancia entre la luz y el pixel es mayor o menor que el radio del punto de luz.

Cuando se calcula el color final se tiene en cuenta si el radio de la luz alcanza el pixel.

//posición en pantalla de la luz, eje Z = 400 asumiendo que la textura esta en Z = 0

float3 LightPos = float3(0,0,400);

//radio del punto de luz

float LightRadius = 800.0f;

//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);

//calculamos la distancia a partir del vector de direccion sin normalizar

 float lightDis = length(LightDirection);

//normalizamos el verctor por que nos interesa la direccion no la magnitud

LightDirection= normalize(LightDirection);


//calculamos la atenuacion, 1 si radio > distancia, 0 en caso contrario

float A= step(LightRadius, lightDis);


 //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), si la atenuacion es 1 se aplica la luz incidente, si es 0 no    se aplica.

tex.rgb *= ((AmbientLight * AmbientLightColor)  + (A * (D*LightColor)));

Y ya podemos ver como el punto de luz tiene un radio finito:


Pero quedaría más realista si hacemos que la luz pierda intensidad de forma mas gradual, por lo que modificamos el shader para que en vez de valores 0 y 1, la atenuación coja valores intermedios de forma suave:

Sustituimos la línea
float A= step(LightRadius, lightDis);
por
A = saturate(1.0 - smoothstep(500, LightRadius, lightDis));
que empieza a crear un degradado suave entre 0 y 1 usando una interpolacion polinomica de Hermite a partir de 500 con respecto a 700 que es el radio de la luz.

Y nos queda una atenuación de luz más real y bonita:



No hay comentarios:

Publicar un comentario