Criarei isso em algumas camadas para que você possa ver como isso acontece.
Comece criando um novo sombreador no Unity, escolhendo Create --> Shader --> Unlit
no menu Ativos ou no menu de contexto do botão direito do mouse na janela Projeto.
No bloco superior, adicionaremos um _ScrollSpeeds
parâmetro para controlar a rapidez com que a textura se move em cada direção:
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_ScrollSpeeds ("Scroll Speeds", vector) = (-5, -20, 0, 0)
}
Isso expõe uma nova propriedade de flutuação de 4 componentes no inspetor de materiais, com o nome amigável "Velocidades de rolagem" (semelhante à adição de uma variável public
ou Serialized
a um MonoBehaviour
script)
Em seguida, usaremos essa variável no shader Vertex para alterar as coordenadas da textura ( o.uv
), adicionando apenas duas linhas ao shader padrão:
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;
}
Coloque isso em um quadrilátero (com uma adorável textura de girafa grátis da Kenney ) e você terá:
Para fazer a textura rolar para fora em um anel, podemos usar uma malha subdividida como uma teia de aranha, com a coordenada uv v aumentando do centro para fora. Mas isso dará alguns artefatos em forma de serra por conta própria. Em vez disso, mostrarei como podemos calcular nossos UVs por fragmento.
Isso é um pouco mais caro, tanto pelas operações trig & length (que são mais caras que a matemática básica) quanto pelo fato de não ser tão eficiente prever e armazenar em cache os dados de textura no hardware ao calcular os cabos de texto por fragmento, em comparação com apenas interpolá-los entre vértices. Mas, para um pequeno efeito especial como esse, não é excessivo.
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;
}
Isso nos dá algo assim (aqui eu aumentei os parâmetros de ladrilhos no material, para ficar mais claro o que está acontecendo - envolver apenas uma única repetição do ladrilho ao redor do círculo parece distorcido e estranho)
Por fim, para tingir a textura com um gradiente de rolagem, podemos apenas adicionar o gradiente como uma segunda textura e multiplicá-los juntos.
Primeiro, adicionamos o novo parâmetro de textura na parte superior:
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_TintTex("Tint Texture", 2D) = "white" {}
_ScrollSpeeds ("Scroll Speeds", vector) = (-5.0, -20.0, 0, 0)
}
E declare-o em nosso bloco CGPROGRAM para que o shader CG possa vê-lo:
sampler2D _MainTex;
float4 _MainTex_ST;
// Declare our second texture sampler and its Scale/Translate values
sampler2D _TintTex;
float4 _TintTex_ST;
float4 _ScrollSpeeds;
Atualize nosso shader de fragmento para usar as duas 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;
}
E agora a nossa girafa fica muito trippy:
Com uma seleção um pouco mais artística de texturas e taxas de rolagem, isso pode criar um efeito bastante semelhante ao mostrado na pergunta.
Você pode notar dois pequenos artefatos com a versão que mostrei acima:
Os rostos próximos ao centro do círculo ficam esticados / magros / pontudos e, em seguida, à medida que avançam para o exterior, são esmagados / largos.
Essa distorção ocorre porque temos um número fixo de faces em torno do perímetro, mas a circunferência que elas estão ampliando fica maior à medida que o raio aumenta, enquanto a altura permanece a mesma.
Podemos corrigir isso remapeando o componente vertical da amostra de textura para seguir uma curva logarítmica, para que as repetições da textura fiquem mais afastadas à medida que o raio aumenta e se aproximando mais em direção ao centro. (De fato, isso nos dá uma regressão infinita de girafas cada vez menores ...)
Há uma fileira de um ou dois pixels embaçados ao longo do meio esquerdo do quad.
Isso acontece porque a GPU examina duas coordenadas de amostra de textura adjacentes para descobrir qual filtro usar. Quando as amostras estão próximas, ela mostra que a textura está sendo exibida grande / fechada e mostra o nível mip mais detalhado. Quando as amostras estão distantes, supõe-se que devemos mostrar a textura com um zoom minúsculo ou distante, e amostras de um mipmap menor / mais desfocado para garantir que não obtemos artefatos de aliasing brilhantes.
O problema está aqui, estamos no ponto de contorno das coordenadas polares, de -180 a 180 graus. Então, na verdade, estamos amostrando pontos muito semelhantes em nosso espaço de textura repetitivo, mesmo que suas coordenadas numéricas façam com que pareçam distantes. Portanto, podemos fornecer nossos próprios vetores de gradiente de amostra para corrigir isso.
Aqui está uma versão com estas correções:
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
variável interna que você pode adicionar às coordenadas de textura no shader (vértice) para obter um efeito de rolagem muito barato. O efeito hexagonal também seria bastante direto. Se você editar sua pergunta para destacar apenas um efeito e perguntar "como eu implementaria isso em um sombreador do Unity", provavelmente poderemos ajudá-lo. Certifique-se de especificar se o sombreador precisa responder à iluminação / sombreamento, pois isso afeta a forma como seria gravado.