Lo costruirò in pochi strati in modo da poter vedere come si riunisce.
Inizia creando un nuovo shader in Unity scegliendo Create --> Shader --> Unlit
nel menu Risorse o nel menu contestuale del tasto destro del mouse nella finestra Progetto.
Nel blocco superiore aggiungeremo un _ScrollSpeeds
parametro per controllare la velocità con cui la trama si sposta in ciascuna direzione:
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_ScrollSpeeds ("Scroll Speeds", vector) = (-5, -20, 0, 0)
}
Ciò espone una nuova proprietà float a 4 componenti nella finestra di ispezione dei materiali, con il nome descrittivo "Velocità di scorrimento" (simile all'aggiunta di una public
o una Serialized
variabile a uno MonoBehaviour
script)
Successivamente useremo questa variabile nello shader Vertex per spostare le coordinate della trama ( o.uv
), aggiungendo solo due righe allo shader predefinito:
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;
}
Sbattilo su un quad (con una bella trama giraffa libera di Kenney ) e otterrai:
Per far scorrere la trama verso l'esterno in un anello, potremmo semplicemente usare una mesh suddivisa come una ragnatela, con le coordinate uv v in aumento dal centro verso l'esterno. Ma questo darà alcuni artefatti a forma di lama da solo. Mostrerò invece come possiamo calcolare i nostri UV per frammento.
Questo è un po 'più costoso, sia per le operazioni di trigma e lunghezza (che sono più costose della matematica di base) sia perché non è così efficiente prevedere e memorizzare nella cache i dati delle trame nell'hardware quando si calcolano texcoords per frammento, rispetto al solo interpolarli tra i vertici. Ma per un piccolo effetto speciale come questo, non è eccessivo.
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;
}
Questo ci dà qualcosa del genere (qui ho aumentato i parametri di piastrellatura nel materiale, quindi è più chiaro cosa sta succedendo: avvolgere solo una singola ripetizione della piastrella attorno al cerchio sembra distorto e strano)
Infine, per tingere la trama con un gradiente di scorrimento, possiamo semplicemente aggiungere il gradiente come seconda trama e moltiplicarli insieme.
Per prima cosa aggiungiamo il nuovo parametro di trama in alto:
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_TintTex("Tint Texture", 2D) = "white" {}
_ScrollSpeeds ("Scroll Speeds", vector) = (-5.0, -20.0, 0, 0)
}
E dichiaralo nel nostro blocco CGPROGRAM in modo che lo shader CG possa vederlo:
sampler2D _MainTex;
float4 _MainTex_ST;
// Declare our second texture sampler and its Scale/Translate values
sampler2D _TintTex;
float4 _TintTex_ST;
float4 _ScrollSpeeds;
Quindi aggiorna il nostro shader di frammenti per utilizzare entrambe le trame:
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;
}
E ora la nostra giraffa diventa davvero trippy:
Con una selezione leggermente più artistica di trame e velocità di scorrimento, questo può creare un effetto abbastanza simile a quello mostrato nella domanda.
Potresti notare due piccoli artefatti con la versione che ho mostrato sopra:
Le facce vicino al centro del cerchio diventano allungate / magre / appuntite, quindi mentre si muovono verso l'esterno vengono schiacciate / larghe.
Questa distorsione si verifica perché abbiamo un numero fisso di facce attorno al perimetro, ma la circonferenza che stanno estendendosi si allarga all'aumentare del raggio, mentre la loro altezza rimane invariata.
Possiamo risolvere questo problema rimappando la componente verticale del campione di trama per seguire una curva logaritmica, quindi le ripetizioni della trama sono più separate man mano che il raggio aumenta e si avvicinano verso il centro. (In realtà, questo ci dà un regresso infinito di giraffe sempre più piccole ...)
C'è una fila di uno o due pixel sfocati lungo la metà sinistra del quadrato.
Ciò accade perché la GPU esamina due coordinate di esempio di trama adiacenti per capire quale filtro utilizzare. Quando i campioni sono ravvicinati, indica che la trama viene visualizzata grande / chiusa e mostra il livello di mip più dettagliato. Quando i campioni sono distanti, si suppone che dobbiamo mostrare la trama con un piccolo zoom o molto lontano, e campiona da una mipmap più piccola / più sfocata per garantire che non ci siano artefatti scintillanti di aliasing.
Il problema è qui, siamo al punto di avvolgimento in coordinate polari, da -180 a 180 gradi. Quindi in realtà stiamo campionando da punti molto simili nel nostro spazio texture ripetuto, anche se le loro coordinate numeriche li fanno sembrare distanti. Quindi possiamo fornire i nostri vettori di gradiente di campionamento per correggere questo.
Ecco una versione con queste correzioni:
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
variabile integrata che puoi aggiungere alle tue coordinate di trama nello shader (vertice) per ottenere un effetto di scorrimento sporco a buon mercato. L'effetto esagono sarebbe anche abbastanza semplice. Se modifichi la tua domanda per evidenziare solo un effetto e chiedi "come dovrei implementarlo in uno shader Unity", probabilmente possiamo aiutarti. Assicurati di specificare se lo shader deve rispondere all'illuminazione / all'ombra, poiché ciò ha un impatto sul modo in cui verrebbe scritto.