Construiré esto en unas pocas capas para que pueda ver cómo se combina.
Comience creando un nuevo sombreador en Unity eligiendo Create --> Shader --> Unlit
en el menú Activos o en el menú contextual del botón derecho en la ventana Proyecto.
En el bloque superior agregaremos un _ScrollSpeeds
parámetro para controlar qué tan rápido se mueve la textura en cada dirección:
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_ScrollSpeeds ("Scroll Speeds", vector) = (-5, -20, 0, 0)
}
Esto expone una nueva propiedad flotante de 4 componentes en el inspector de materiales, con el nombre descriptivo "Velocidades de desplazamiento" (similar a agregar una public
o Serialized
variable a un MonoBehaviour
script)
A continuación, utilizaremos esta variable en el sombreador Vertex para cambiar las coordenadas de textura ( o.uv
), agregando solo dos líneas al sombreador predeterminado:
sampler2D _MainTex;
float4 _MainTex_ST;
// Declare our new parameter here so it's visible to the CG shader
float4 _ScrollSpeeds;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
// Shift the uvs over time.
o.uv += _ScrollSpeeds * _Time.x;
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
Déle una palmada en un quad (con una hermosa textura de jirafa libre de Kenney ) y obtendrá:
Para que la textura se desplace hacia afuera en un anillo, podríamos usar una malla subdividida como una telaraña, con la coordenada uv v aumentando desde el centro hacia afuera. Pero eso dará algunos artefactos en forma de hoja de sierra por sí solo. En cambio, mostraré cómo podemos calcular nuestros UV por fragmento.
Esto es un poco más costoso, tanto por las operaciones trigonométricas y de longitud (que son más costosas que las matemáticas básicas) como porque no es tan eficiente predecir y almacenar en caché los datos de textura en el hardware al calcular texcoords por fragmento, en comparación con solo interpolarlos entre vértices Pero para un pequeño efecto especial como este, no es excesivo.
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
// Shift the UVs so (0, 0) is in the middle of the quad.
o.uv = v.uv - 0.5f;
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// Convert our texture coordinates to polar form:
float2 polar = float2(
atan2(i.uv.y, i.uv.x)/(2.0f * 3.141592653589f), // angle
length(i.uv) // radius
);
// Apply texture scale
polar *= _MainTex_ST.xy;
// Scroll the texture over time.
polar += _ScrollSpeeds.xy * _Time.x;
// Sample using the polar coordinates, instead of the original uvs.
// Here I multiply by MainTex
fixed4 col = tex2D(_MainTex, polar);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
Eso nos da algo como esto (aquí aumenté los parámetros de mosaico en el material para que sea más claro lo que está sucediendo: envolver solo una sola repetición del mosaico alrededor del círculo parece distorsionado y extraño)
Por último, para teñir la textura con un gradiente de desplazamiento, podemos agregar el gradiente como una segunda textura y multiplicarlos.
Primero agregamos el nuevo parámetro de textura en la parte superior:
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_TintTex("Tint Texture", 2D) = "white" {}
_ScrollSpeeds ("Scroll Speeds", vector) = (-5.0, -20.0, 0, 0)
}
Y declararlo en nuestro bloque CGPROGRAM para que el sombreador CG pueda verlo:
sampler2D _MainTex;
float4 _MainTex_ST;
// Declare our second texture sampler and its Scale/Translate values
sampler2D _TintTex;
float4 _TintTex_ST;
float4 _ScrollSpeeds;
Luego actualiza nuestro sombreador de fragmentos para usar ambas texturas:
fixed4 frag(v2f i) : SV_Target
{
float2 polar = float2(
atan2(i.uv.y, i.uv.x) / (2.0f * 3.141592653589f), // angle
length(i.uv) // radius
);
// Copy the polar coordinates before we scale & shift them,
// so we can scale & shift the tint texture independently.
float2 tintUVs = polar * _TintTex_ST.xy;
tintUVs += _ScrollSpeeds.zw * _Time.x;
polar *= _MainTex_ST.xy;
polar += _ScrollSpeeds.xy * _Time.x;
fixed4 col = tex2D(_MainTex, polar);
// Tint the colour by our second texture.
col *= tex2D(_TintTex, tintUVs);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
Y ahora nuestra jirafa se pone realmente loca:
Con una selección ligeramente más artística de texturas y velocidades de desplazamiento, esto puede crear un efecto bastante similar al que se muestra en la pregunta.
Puede notar dos pequeños artefactos con la versión que he mostrado arriba:
Las caras cerca del centro del círculo se estiran / flacas / puntiagudas, luego, cuando se mueven hacia el exterior, se aplastan / ensanchan.
Esta distorsión ocurre porque tenemos un número fijo de caras alrededor del perímetro, pero la circunferencia que abarcan se ensancha a medida que aumenta el radio, mientras que su altura se mantiene igual.
Podemos arreglar esto reasignando el componente vertical de la muestra de textura para seguir una curva logarítmica, de modo que las repeticiones de la textura estén más separadas a medida que aumenta el radio, y más juntas hacia el centro. (De hecho, esto nos da una regresión infinita de jirafas cada vez más pequeñas ...)
Hay una fila de uno o dos píxeles borrosos a lo largo del centro-izquierda del quad.
Esto sucede porque la GPU mira dos coordenadas de muestra de textura adyacentes para descubrir qué filtro usar. Cuando las muestras están juntas, calcula que la textura se muestra grande / cerrada y muestra el nivel de detalle más detallado. Cuando las muestras están muy separadas, adivina que debemos mostrar la textura con un pequeño zoom o muy lejos, y toma muestras de un mapa MIP más pequeño / borroso para garantizar que no se produzcan artefactos de alias brillantes.
El problema está aquí, estamos en el punto de ajuste en coordenadas polares, de -180 a 180 grados. Por lo tanto, en realidad estamos tomando muestras de puntos muy similares en nuestro espacio de textura repetitivo, incluso si sus coordenadas numéricas hacen que parezcan muy distantes. Por lo tanto, podemos proporcionar nuestros propios vectores de gradiente de muestreo para corregir esto.
Aquí hay una versión con estas correcciones:
fixed4 frag(v2f i) : SV_Target
{
float2 polar = float2(
atan2(i.uv.y, i.uv.x) / (2.0f * 3.141592653589f), // angle
log(dot(i.uv, i.uv)) * 0.5f // log-radius
);
// Check how much our texture sampling point changes between
// neighbouring pixels to the sides (ddx) and above/below (ddy)
float4 gradient = float4(ddx(polar), ddy(polar));
// If our angle wraps around between adjacent samples,
// discard one full rotation from its value and keep the fraction.
gradient.xz = frac(gradient.xz + 1.5f) - 0.5f;
// Copy the polar coordinates before we scale & shift them,
// so we can scale & shift the tint texture independently.
float2 tintUVs = polar * _TintTex_ST.xy;
tintUVs += _ScrollSpeeds.zw * _Time.x;
polar *= _MainTex_ST.xy;
polar += _ScrollSpeeds.xy * _Time.x;
// Sample with our custom gradients.
fixed4 col = tex2Dgrad(_MainTex, polar,
_MainTex_ST.xy * gradient.xy,
_MainTex_ST.xy * gradient.zw
);
// Since our tint texture has its own scale,
// its gradients also need to be scaled to match.
col *= tex2Dgrad(_TintTex, tintUVs,
_TintTex_ST.xy * gradient.xy,
_TintTex_ST.xy * gradient.zw
);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
_Time
variable incorporada que puede agregar a sus coordenadas de textura en el sombreador (vértice) para obtener un efecto de desplazamiento muy barato. El efecto del hexágono también sería bastante sencillo. Si edita su pregunta para resaltar solo un efecto y pregunta "cómo implementaría esto en un sombreador de Unity", probablemente podamos ayudarlo. Asegúrese de especificar si el sombreador necesita responder a la iluminación / sombreado, ya que eso tiene un impacto en cómo se escribiría.