Uno de los temas más complejos quizás y que aporta mayor realismo son los reflejos sobre los materiales y antes que viniera esta ola del raytracing en tiempo real, antes han existido al menos tres métodos de obtener reflejos:
Algunos solamente horneando una textura cubica sobre un material (solo permite reflejos estáticos que no se actualizan con el tiempo),
Otros tomando lo que ve la cámara y multiplicando matrices para aplicarlo sobre un render texture (Solo permite reflejos en superficies planas, pero falla en superficies circulares)
Por último hay un método similar pero que da algunos fallos y que se aplica como un efecto de postprocesado, usando una textura de normales para torcer las texturas y generar ese efecto de reflejo (si la cámara mira hacia abajo o hacia arriba, se pierde información de los reflejos, porque solo tiene en cuenta lo que la cámara ve).
En esta ocasión haremos el primer método, el de usar una textura cubica para usarlo como una textura, afortunadamente unity tiene un componente llamado #ReflectionProbe que podemos usar para crear estos reflejos en tiempo real, pudiendo adherir al objeto que necesita los reflejos o a la cámara.
Esta vez haremos un shader que perfectamente puede servir para toda clase de vidrios o para cualquier superficie transparente que sea reflectante, gracias a que unity nos facilita mucho el desarrollo, ahora solo con unas cuantas macros podemos invocar estos reflejos.
Preparación.
Cree una nueva escena y en dicha escena posicione varios objeto de distintas formas y colores.
En el centro de la escena cree una esfera, que será la que reflejará el entorno.
Vamos a crear un material nuevo al que nombraremos vidrio y se lo añadimos a la esfera que está en el centro
Crearemos un nuevo shader que lo adjuntaremos al material que hemos recién creado.
Editamos el shader con nuestro IDE favorito.
Edición del shader.
En las propiedades no hay nada del otro mundo, simplemente haremos uso de una propiedad que es la completamente opuesta a #viewDir, hablo del efecto #rim y obviamente una textura cubica, aunque no esté presente también haré uso de la textura que envía la cámara, un #GrabPass para poder ver a través de él (Este último punto no es necesario y bastaría solo con la transparencia del canal Alpha en el color resultante, pero deseaba hacerlo así para manipular la textura Grab en un futuro).
Shader "Vertex Fragment/ReflectionGrabShader"
{
Properties
{
_RimPower("rim power", Range(0.0,1.0)) = 1.0
_Reflection("Reflection", Cube) = "white"{}
}
SubShader
{
LOD 200
GrabPass{"_GrabTexture"}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _GrabTexture;
samplerCUBE _Reflection;
float _RimPower;
struct vertInput {
float4 vertex :POSITION;
float3 normal : NORMAL;
};
vert2frag vert(vertInput v)
{
vert2frag o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uvgrab = ComputeGrabScreenPos(o.vertex);
float3 viewDir = normalize(ObjSpaceViewDir(v.vertex));
float NdotV = 1 - dot(v.normal, viewDir);
o.rim = smoothstep(1 - _RimPower, 1.0, NdotV);
o.uvrefl = v.normal;
return o;
}
//Fragment function
half4 frag(vert2frag i) :COLOR
{
fixed4 col = tex2Dproj(_GrabTexture,UNITY_PROJ_COORD(i.uvgrab));
fixed4 refl = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, i.uvrefl);
fixed4 result = lerp(col, refl, i.rim);
return result;
}
ENDCG
}
}
FallBack "Diffuse"
}
Comencemos por la propiedad rim del programa de vértices, si recuerdan en el blog de modelos de iluminación, siempre usábamos la multiplicación de los mismos vectores para obtener la posición de la luz y la posición de la vista, para hallar la resultante que en este caso era la luz especular. En este caso nos interesa hallar el inverso de la posición de la vista, por ese motivo se le resta 1 al producto punto entre la normal y la dirección de la vista.
float3 viewDir = normalize(ObjSpaceViewDir(v.vertex));
float NdotV = 1 - dot(v.normal, viewDir);
o.rim = smoothstep(1 - _RimPower, 1.0, NdotV);
En este caso obtenemos valores mas claros por los bordes del objeto y los colores mas oscuros en el centro del objeto. Nota: Por si te preguntaste alguna vez como obtenían el borde o el contorno en un shader cartoon, es usando este efecto rim.
Ahora necesitamos hallar las coordenadas para la reflexión o la textura cubo que hará de reflejos, esto ni siquiera hay que hallarlo, es solo la misma normal.
o.uvRefl = v.normal;
ahora hay que hallar las coordenadas uv para el grab pass, esto es sencillo, es solo usar la macro que tiene unity para este propósito
o.uvgrab = ComputeGrabScreenPos(o.vertex);
Vamos ahora al programa de fragmentos, y vamos a hacer efectivos los reflejos. Unity tiene preparada otra macro que gestiona todo esto de forma automática
UNITY_SAMPLE_TEXCUBE(samplerCube, uvRefl)
Una variable que viene por defecto llamada unity_SpecCube0, esta variable toma por defecto el #cubemap del skybox que asignemos dentro de las opciones de lightning (aquella ventana que sirve para controlar, la niebla, los rebotes de luz, la luz ambiental), si tenemos un objeto cerca con un componente reflection probe, mezclara la textura del skybox con la textura generada por el #reflectionProbe.
fixed4 col = tex2Dproj(_GrabTexture,UNITY_PROJ_COORD(i.uvgrab));
Ahora renderizamos el grab pass, si se fija, la implementación es muy distinta a como lo hicimos en los efectos de imagen, más concretamente con el efecto #Blur de 2 #Passes, donde lo aplicábamos como si fuese una textura normal, en este caso, lo aplicamos con la función por defecto del lenguaje Tex2Dproj.
Como coordenadas uv, usamos una macro llamada UNITY_PROJ_COORD, que si has visto la corrección que le hice al #Projector de las #Causticas, allí es utilizado para proyectar el círculo que delimita las cáusticas y darle una posición en el espacio, de la misma forma, nuestra grab texture necesita una posición dentro del espacio.
Por último simplemente controlamos la interpolación con la propiedad rim que hicimos en la función de vértice.
fixed4 result = lerp(col, refl, i.rim);
Eso es todo por ahora, si guardan el resultado deberán ver algo como esto:
En el próximo blog veremos mas formas de aplicar reflejos un tanto distintos mediante texturas simples, pero que quizás pueda ayudarte en un futuro alguna vez, si tu juego no necesita reflejos precisos. No siendo mas, nos vemos en el siguiente Blog.
Comments