Je vais construire cela en quelques couches afin que vous puissiez voir comment cela se combine.
Commencez par créer un nouveau shader dans Unity en choisissant Create --> Shader --> Unlit
dans le menu Actifs ou dans le menu contextuel du clic droit de la fenêtre Projet.
Dans le bloc supérieur, nous ajouterons un _ScrollSpeeds
paramètre pour contrôler la vitesse à laquelle la texture se déplace dans chaque direction:
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_ScrollSpeeds ("Scroll Speeds", vector) = (-5, -20, 0, 0)
}
Cela expose une nouvelle propriété flottante à 4 composants dans l'inspecteur des matériaux, avec le nom convivial "Vitesse de défilement" (similaire à l'ajout d'une public
ou d' une Serialized
variable à un MonoBehaviour
script)
Ensuite, nous utiliserons cette variable dans le shader Vertex pour décaler les coordonnées de texture ( o.uv
), en ajoutant seulement deux lignes au shader par défaut:
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;
}
Giflez cela sur un quad (avec une belle texture de girafe gratuite par Kenney ) et vous obtenez:
Pour faire défiler la texture vers l'extérieur dans un anneau, nous pourrions simplement utiliser un maillage subdivisé comme une toile d'araignée, avec la coordonnée uv v augmentant du centre vers l'extérieur. Mais cela donnera à lui seul des artefacts en forme de lame de scie. Au lieu de cela, je vais montrer comment nous pouvons calculer nos UV par fragment.
C'est un peu plus cher, à la fois en raison des opérations de trig et de longueur (qui sont plus chères que les mathématiques de base) et parce qu'il n'est pas aussi efficace de prévoir et de mettre en cache les données de texture dans le matériel lors du calcul des texcoords par fragment, par rapport à simplement les interpoler entre les sommets. Mais pour un petit effet spécial comme celui-ci, ce n'est pas excessif.
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;
}
Cela nous donne quelque chose comme ça (ici, j'ai augmenté les paramètres de mosaïque dans le matériau afin qu'il soit plus clair ce qui se passe - envelopper une seule répétition de la tuile autour du cercle semble déformé et bizarre)
Enfin, pour teinter la texture par un dégradé de défilement, il suffit d'ajouter le dégradé comme deuxième texture et de les multiplier ensemble.
Nous ajoutons d'abord le nouveau paramètre de texture en haut:
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_TintTex("Tint Texture", 2D) = "white" {}
_ScrollSpeeds ("Scroll Speeds", vector) = (-5.0, -20.0, 0, 0)
}
Et déclarez-le dans notre bloc CGPROGRAM pour que le shader CG puisse le voir:
sampler2D _MainTex;
float4 _MainTex_ST;
// Declare our second texture sampler and its Scale/Translate values
sampler2D _TintTex;
float4 _TintTex_ST;
float4 _ScrollSpeeds;
Mettez ensuite à jour notre fragment shader pour utiliser les deux textures:
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;
}
Et maintenant, notre girafe devient vraiment trippante:
Avec une sélection légèrement plus artistique de textures et de taux de défilement, cela peut créer un effet assez similaire à celui montré dans la question.
Vous remarquerez peut-être deux petits artefacts avec la version que j'ai montrée ci-dessus:
Les visages près du centre du cercle deviennent étirés / maigres / pointus, puis lorsqu'ils se déplacent vers l'extérieur, ils sont écrasés / larges.
Cette distorsion se produit parce que nous avons un nombre fixe de faces autour du périmètre, mais la circonférence qu'elles couvrent s'élargit à mesure que le rayon augmente, tandis que leur hauteur reste la même.
Nous pouvons résoudre ce problème en remappant la composante verticale de l'échantillon de texture pour suivre une courbe logarithmique, de sorte que les répétitions de la texture sont plus éloignées lorsque le rayon augmente et plus proches les unes des autres vers le centre. (En fait, cela nous donne une régression infinie de girafes de plus en plus petites ...)
Il y a une rangée d'un ou deux pixels flous le long du milieu à gauche du quad.
Cela se produit car le GPU examine deux coordonnées d'échantillon de texture adjacentes pour déterminer le filtrage à utiliser. Lorsque les échantillons sont proches les uns des autres, il indique que la texture est affichée grande / proche et montre le niveau de mip le plus détaillé. Lorsque les échantillons sont éloignés, il suppose que nous devons montrer la texture à un zoom minuscule ou loin, et il échantillonne à partir d'un mipmap plus petit / plus flou pour nous assurer que nous n'obtenons pas d'artefacts de crénelage scintillants.
Le problème est ici, nous sommes au point de bouclage en coordonnées polaires, de -180 à 180 degrés. Nous échantillonnons donc à partir de points très similaires dans notre espace de texture répétitif, même si leurs coordonnées numériques leur donnent l'impression d'être éloignées. Nous pouvons donc fournir nos propres vecteurs de gradient d'échantillonnage pour corriger cela.
Voici une version avec ces corrections:
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 intégrée que vous pouvez ajouter à vos coordonnées de texture dans le (vertex) shader pour obtenir un effet de défilement très bon marché. L'effet hexagonal serait également assez simple. Si vous modifiez votre question pour mettre en évidence un seul effet et demandez "comment pourrais-je implémenter cela dans un shader Unity", nous pouvons probablement vous aider. Assurez-vous de spécifier si le shader doit répondre à l'éclairage / à l'ombre, car cela a un impact sur la façon dont il serait écrit.