La #refracción es un efecto que ocurre cuando la luz atraviesa un medio, sea aire caliente o agua y esta se desvía del camino, muy distinto de la #reflexión, que simplemente la luz no logra penetrar el medio, pero rebota sobre el.
Algunos ejemplos de refracción es cuando ves un objeto a través de un cristal con relieves o a través de agua en movimiento o las oleadas del calor sobre el asfalto, esta distorsión en el asfalto también se le conoce como #fatamorgana.
En videojuegos este efecto es usado para darle mayor realismo a algunos materiales, es un efecto algo costoso y unity tiene algunos shaders que aplican lo mismo, como el efecto ojo de pez o el efecto whirl similar al efecto molinete de photoshop, pero estos efectos que vienen por defecto en unity usan matemática dura para calcularlo, por eso cuando abres el script es muy difícil de leer.
Yo al haber hecho el tutorial de #aberracióncromática me di cuenta que al sumar valores al las coordenadas uv, desplazaba la imagen y al multiplicar escalaba. En esta ocasión vamos a hacer el mismo #shader que me fué censurado hace un año, van a ver que es un efecto relativamente simple.
Vamos a crear un nuevo archivo Shader y vamos a agregarle unos cuantas propiedades
Un vector que usaremos para manipular las coordenadas uv
Un flotante que permitirá escalar la UV de la textura
La textura que vá a distorsionar lo que vemos, un normalmap
Un color, no es necesario, pero este script, está pensado para cuando la cámara se sumerge bajo el agua, le de un tono azulado, así que puede prescindir de él si lo desea.
Y por último la textura que será enviada desde la camara.
Properties
{
_SpeedStrength("Speed (XY), Strength (ZW)", Vector) = (1, 1, 1, 1)
_RefractTexTiling("Refraction Tilefac", Float) = 1
_RefractTex("Refraction (RG), Colormask (B)", 2D) = "bump" {}
_Color("Color (RGB)", Color) = (1, 1, 1, 1)
_MainTex("Base (RGB) DON`T TOUCH IT! :)", RECT) = "white" {}
}
Luego el subshader y el pase que incluirá el vert_img, que es la función de vértice proporcionada por unity para efectos de postprocesado.
las variables respectivas a los parámetros usando uniform
SubShader
{
Pass
{ZTest Always Cull Off ZWrite Off
Fog{ Mode off }
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
uniform sampler2D _MainTex;
uniform sampler2D _RefractTex;
uniform float4 _SpeedStrength;
uniform float _RefractTexTiling;
uniform float4 _Color;
Por último pasamos a explicar que hace en este caso la función frag. En esta función tomamos como parámetro la estructura v2f_img que nos devuelve la función de vértice por defecto de unity el vert_img, como verán más adelante no es necesario declarar ninguna función vert_img o el struct v2f_img, porque ya vienen dentro del include UnityCG.cginc.
El truco de este shader depende enteramente de las coordenadas uv, la variable refrtc es la escala que se produce al multiplicar la coordenada con el valor flotante _RefractTexTiling.
ahora hay que aclarar algo, unity tiene internamente una variable _Time como un vector, donde cada componente del vector tiene un tiempo diferente, siendo x el más lento y w el más rápido, se usa similar al time delta time.
Para que la imagen se pueda desplazar a cierta velocidad hay que multiplicar el _Time por el vector _SpeedStrength.xy y el resultado de esta sumarlo a la coordenada uv que modificamos en la variable refrtc.
refrtc + _SpeedStrength.xy*_Time.x
la variable refract toma el valor los colores desde la imagen que introducimos como variable, y las coordenadas uv que modificamos. Algo que hay que aclarar es que los valores de los colores de las imágenes van de 0 a 1, pero nosotros necesitamos que para el desplazamiento vaya de -1 a 1, basta con multiplicar por 2 y restar 1.
refract.rg*2.0 - 1.0
Todo lo que hemos hecho hasta ahora es usar la textura normal para crear los datos de desplazamiento. Note que no estoy usando UnpackNormal, por el interés nunca fue usar la normal en si, sino los colores como datos, el componente rojo va de 0 a 1, pero con la multiplicacion ahora va desde -1 a 1 en X, lo mismo para el componente verde, quedando G = -1 a 1 en Y.
Ahora debemos usar estos datos para desplazar las coordenadas UV de la imagen que se nos envía desde la camara, aqui usamos los componentes ZW del vector de velocidad multiplicados por los componentes RG de la imagen de refracción que hicimos y el resultado se lo sumamos a las coordenadas UV de la imagen enviada desde la cámara.
i.uv + refract.rg*_SpeedStrength.zw
El siguiente paso es opcional y es incorporar el color al resultado de la imagen, quizás se pudo haber hecho multiplicando el color directamente, pero como en mi script estaba combinándolo con otra textura me quedó así.
lerp(original, original*_Color, refract.b);
Una función lerp que hace transición entre la imagen original y la original modificada, siendo regulada nuevamente por el componente B de la textura de refracción. El resto no es necesario comentarlo.
float4 frag(v2f_img i) : COLOR
{
float2 refrtc = i.uv*_RefractTexTiling;
float4 refract = tex2D(_RefractTex, refrtc + _SpeedStrength.xy*_Time.x);
refract.rg = refract.rg*2.0 - 1.0;
float4 original = tex2D(_MainTex, i.uv + refract.rg*_SpeedStrength.zw);
float4 output = lerp(original, original*_Color, refract.b);
output.a = original.a;
return output;
}
ENDCG
}
Aqui les dejo el script completo.
Shader "Image FX/Refraction"
{
Properties
{
_SpeedStrength("Speed (XY), Strength (ZW)", Vector) = (1, 1, 1, 1)
_RefractTexTiling("Refraction Tilefac", Float) = 1
_RefractTex("Refraction (RG), Colormask (B)", 2D) = "bump" {}
_Color("Color (RGB)", Color) = (1, 1, 1, 1)
_MainTex("Base (RGB) DON`T TOUCH IT! :)", RECT) = "white" {}
}
SubShader
{
Pass
{
ZTest Always Cull Off ZWrite Off
Fog{ Mode off }
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
uniform sampler2D _MainTex;
uniform sampler2D _RefractTex;
uniform float4 _SpeedStrength;
uniform float _RefractTexTiling;
uniform float4 _Color;
float4 frag(v2f_img i) : COLOR
{
float2 refrtc = i.uv*_RefractTexTiling;
float4 refract = tex2D(_RefractTex, refrtc + _SpeedStrength.xy*_Time.x);
refract.rg = refract.rg*2.0 - 1.0;
float4 original = tex2D(_MainTex, i.uv + refract.rg*_SpeedStrength.zw);
float4 output = lerp(original, original*_Color, refract.b);
output.a = original.a;
return output;
}
ENDCG
}
}
Fallback off
}
Espero pronto hacer un Blog que use estos efectos que he estado publicando y los combine para hacer otros mas complejos.
Esto es todo por ahora, nos vemos en un siguiente blog.
Comments