TexGen era una función que permitía cubrir con una textura cubica una malla, pero que actualmente ya no existe, hay shaders antiguos que usaba esta característica, pero ahora es un problema no poder actualizarlos a implementaciones del presente.
Unity desde versiones antiguas a la 5, unity tenía una mezcla muy extraña entre vértices y fragmentos y shaders de función fija, estos shader de función fija pretendía usar características comunes que funcionaban en común en OpenGL y en HLSL y que buscaba ser compatible para todo tipo de dispositivos, pero francamente se veía terrible y en el ámbito de código era una mezcla terrible combinar HLSL y Fixed Functions.
Con el tiempo y poco a poco unity ha ido desechando más las Fixed Functions o el mal llamado ShaderLab, ahora actualmente quedan algunas propiedades o funciones de mezcla o de orden de renderizado o el nombre de pases, mucha parte de esas funciones fijas se convirtieron en #macros, otras simplemente ya no están y en esta ocasión trataremos la función desaparecida #TexGen
¿Qué es TexGen?
TexGen era una propiedad que se declaraba a un #cubemap y al darle el valor por defecto, había varias opciones de comportamiento y luego simplemente al hacer uso de la imagen, pues esta se comportaba tal como lo habíamos elegido desde las propiedades.
Creo que simplemente desapareció, porque al parecer no era tan difícil su implementación, pues casi todo se basa en manipular las coordenadas uv del objeto, esta vez explicaremos que sucede con cada uno y quizás tratar de adivinar en que casos aplicarlos mejor.
Realmente no es un secreto, pues me apoyaré en la documentación al respecto, este blog es una profundización del blog anterior y el preámbulo para crear el shader de océano o un gran cuerpo de agua.
TexGen.cginc y Shader principal
Aclaro que este archivo es un poco inútil, pero al menos tenerlo a la mano ayuda a recordar como implementarlo, va a ser muy rara la ocasión donde apliquemos estos enfoques porque normalmente usaremos texturas cúbicas, cuando las aplicaciones dadas aquí, la mayoría será para texturas 2D normales y corrientes.
Comenzaremos a llenar por funciones el archivo cginc con 3 funciones para texturas 2D y luego más adelante 2 funciones para cubemaps. Por cierto si no sabes como hacer un archivo cginc ve a verlo en el siguiente enlace.
Proyección lineal.
La proyección lineal es igual que usar la misma posición del vértice como coordenada uv, este sería la función más inútil, pero es útil para simular la ilusión de unos reflejos de coche con una textura, sería la opción más barata en rendimiento para simular reflejos falsos.
fixed3 TexGenObjectLinear(fixed4 position) {
return position.xyz;
}
fixed3 TexGenObjectLinear(fixed3 position) {
return position;
}
Esta será la funcion de vertice:
Input vert(vertInput v) {
Input o;
o.pos = UnityObjectToClipPos(v.pos);
o.uv = TexGenObjectLinear(o.pos);
return o;
}
Esta será la función de fragmentos:
fixed4 frag(Input IN):COLOR {
// Albedo comes from a texture tinted by color
fixed4 c = tex2D(_MainTex, IN.uv.xy);
return c;
}
Proyección de la vista.
En este caso es otra función casi inútil, en esta simplemente se usa la matriz de proyección de la vista para las coordenadas uv, el efecto es casi el mismo, con la tendencia que la textura trata de quedar fijada a la vista de la cámara, similar a lo que ocurre con efectos de imagen.
//En el archivo cginc
fixed3 TexGenEyeLinear(fixed4 position) {
return UnityObjectToViewPos(position);
}
Input vert(vertInput v) {
Input o;
o.uv = TexGenEyeLinear(v.pos);
return o;
}
Proyección de mapa esférico.
Este es quizás la única función útil de las proyecciones 2D, esta permite usar una imagen 2D de un mapa esférico, también es una forma barata de simular reflejos en un objeto, el defecto limitante es que solo se ve bien en esferas.
Si quieres hacer una textura de estas es muy fácil, es solo tomar una imagen 360 grados de cualquier ambiente y aplicarle coordenadas polares en Photoshop, sin embargo también funciona bien con la imagen de 360 grados directamente, pero su reflejo se verá de cabeza.
La razón de que funcione es porque esta función lo único que hace es aplicar coordenadas polares a la inversa, es decir si usábamos en Photoshop una imagen rectangular para obtener una imagen esférica, este shader lo que hace es tomar la imagen esférica para convertirla en un rectángulo nuevamente y si le introduces una imagen rectangular, otra vez obtendrás una esfera, pero en el sentido contrario, no como lo obtendrías de Photoshop.
fixed2 TexGenSphereMap(fixed4 position, fixed3 normal) {
fixed3 viewDir = normalize(ObjSpaceViewDir(position));
fixed3 r = reflect(viewDir, normal);
r = UnityObjectToViewPos(r);
r.z += 1;
fixed m = 2 * length(r);
return r.xy / m + 0.5;
}
Input vert(vertInput v) {
Input o;
o.pos = UnityObjectToClipPos(v.pos);
o.uv = TexGenSphereMap(o.pos,v.normal);
return o;
}
A continuación vamos con las proyecciones para cubemaps.
Tex Cube Normal.
Las dos últimas funciones harán uso de cubemaps, por lo tanto hay que hacer algunos cambios en el shader principal.
Esta función es similar a texturizar a usar una función tex2D, pero usando un cubemap, su uso sería quizás para reflejos para un objeto estático, no se me ocurre para que pudiera ser útil.
fixed3 TexGenCubeNormal(fixed3 normal) {
return mul((float3x3)UNITY_MATRIX_IT_MV, normal);
}
En la estructura de entrada cambiaremos el float2 de las coordenadas uv que normalmente se usa para la función tex2D, por float3 para la función texCUBE
struct Input {
float3 uv : TEXCOORD0;
float4 pos : SV_POSITION;
};
Input vert(vertInput v) {
Input o;
o.pos = UnityObjectToClipPos(v.pos);
o.uv = TexGenCubeNormal(v.normal);
return o;
}
fixed4 frag(Input IN):COLOR {
// Albedo comes from a texture tinted by color
fixed4 c = texCUBE(_MainCube, IN.uv);
return c;
}
Tex Cubemap
Esta proyección seria tal como unity usa los cubemaps, pero normalmente lo que usamos es una macro para este propósito.
fixed3 TexGenCubemap(fixed4 position, fixed3 normal) {
fixed3 uv;
fixed3 viewDir = normalize(ObjSpaceViewDir(position));
uv = reflect(-viewDir, normal);
uv = UnityObjectToViewPos(uv);
return uv;
}
Conclusión.
Este blog ha repasado los tipos de reflejo que existe en videojuegos excepto el #raytracing, con esto complementamos el blog anterior donde hacíamos uso de los reflejos aprovechando el componente #reflectionProbe para simular reflejos en tiempo real.
Hemos aprendido a que no es necesario disponer de una textura cubica para obtener reflejos simulados, simplemente basta con usar la matriz correcta, también es posible usar una textura cubica como una textura 2D común.
Archivo cginc
#ifndef TEXGEN
#define TEXGEN
//Texgen for TexCUBE
fixed3 TexGenCubemap(fixed4 position, fixed3 normal) {
fixed3 uv;
fixed3 viewDir = normalize(ObjSpaceViewDir(position));
uv = reflect(-viewDir, normal);
uv = UnityObjectToViewPos(uv);
return uv;
}
fixed3 TexGenCubeNormal(fixed3 normal) {
return mul((float3x3)UNITY_MATRIX_IT_MV, normal);
}
//Texgen for Tex2D
fixed3 TexGenObjectLinear(fixed4 position) {
return position.xyz;
}
fixed3 TexGenObjectLinear(fixed3 position) {
return position;
}
fixed3 TexGenEyeLinear(fixed4 position) {
return UnityObjectToViewPos(position);
}
fixed2 TexGenSphereMap(fixed4 position, fixed3 normal) {
fixed3 viewDir = normalize(ObjSpaceViewDir(position));
fixed3 r = reflect(viewDir, normal);
r = UnityObjectToViewPos(r);
r.z += 1;
fixed m = 2 * length(r);
return r.xy / m + 0.5;
}
//Utils
fixed3 LocalPositionInSurf(fixed3 worldPos) {
return worldPos - mul(unity_ObjectToWorld, float4(0, 0, 0, 1)).xyz;
}
#endif
Shader
Shader "Custom/FirstCubemap-VertFrag" {
Properties {
_MainCube ("Albedo (RGB)", Cube) = "white" {}
_MainTex("tex", 2D) = "white" {}
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader{
Tags { "RenderType" = "Opaque" }
LOD 200
Pass{
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma fragment frag
#pragma vertex vert
#include "UnityCG.cginc"
#include "TexGen.cginc"
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
samplerCUBE _MainCube;
sampler2D _MainTex;
struct Input {
float3 uv : TEXCOORD0;
float4 pos : SV_POSITION;
};
struct vertInput {
float4 pos: POSITION;
float3 normal : NORMAL;
};
Input vert(vertInput v) {
Input o;
o.pos = UnityObjectToClipPos(v.pos);
o.uv = TexGenCubemap(o.pos,v.normal);
return o;
}
fixed4 frag(Input IN):COLOR {
// Albedo comes from a texture tinted by color
fixed4 c = texCUBE(_MainCube, IN.uv);
return c;
}
ENDCG
}
}
FallBack "Diffuse"
}
Esto ha sido todo por ahora, aun no soy un experto en reflejos y en un futuro puede que aplique un efecto de imagen sin comprometerme. Lo importante es que llegados a este punto ya usted esté habilitado a entender la base de como funciona esta técnica de reflejos.
No siendo mas me despido, nos vemos en el siguiente Blog.
Comments