Animierte Texturen für Modelle; Wie schreibe ich einen Shader?


9

Ich habe eine Rocket League gesehen und festgestellt, dass es animierte Abziehbilder und Räder gibt.

Bild.

Ich möchte etwas Ähnliches wie die Effekte im obigen Bild implementieren.

Wie würde ich einen Unity Shader schreiben, um den Radeffekt zu erzielen?

Ich weiß nicht viel über Shader, aber kann ich den Standard-Unity-Shader bearbeiten, um den Animationseffekt auszuführen?


3
Shader sind hier definitiv die Lösung, selbst für etwas so Einfaches wie eine scrollende Textur. Unity verfügt über eine integrierte _TimeVariable, die Sie Ihren Texturkoordinaten im (Scheitelpunkt-) Shader hinzufügen können, um einen spottbilligen Bildlaufeffekt zu erzielen. Der Sechseckeffekt wäre auch ziemlich einfach. Wenn Sie Ihre Frage bearbeiten, um nur einen Effekt hervorzuheben, und fragen: "Wie würde ich dies in einem Unity-Shader implementieren?", Können wir Ihnen wahrscheinlich helfen. Stellen Sie sicher, dass Sie angeben, ob der Shader auf Beleuchtung / Schatten reagieren muss, da dies Auswirkungen auf die Schreibweise hat.
DMGregory

Danke für die Information. Haben Sie meine Frage geändert, ist das ein bisschen besser? Ich weiß nicht wirklich, was Licht und Schatten sind, also habe ich das vorerst weggelassen, bis ich ein besseres Verständnis von Shaders habe. Ich denke, ich würde sie wollen, aber ich könnte einfach Teile vom Standard-Shader kopieren, oder?
JacketPotatoeFan

3
Ich werde es später in einer Antwort etwas später erweitern (obwohl Sie mich gerne schlagen können, wenn jemand jetzt antworten möchte!) - aber Sie würden normalerweise einen Unity-Shader schreiben, der Beleuchtung als "Surface Shader" verwendet. während eine, die keine Beleuchtung benötigt, oft einfacher als "Unlit Shader" zu schreiben ist. Es sieht für mich so aus, als ob diese Räder nicht beleuchtet sind (die leichten Schatten am Rand sehen aus wie bei SSAO), also würde ich für den Anfang die Beleuchtung aus dem Bild herauslassen. Können Sie klarstellen: Möchten Sie genau das hier gezeigte geometrische Muster oder die Möglichkeit, beliebige Texturen nach außen zu scrollen und sie so zu regenbogenfarben?
DMGregory

Vielen Dank, alle Informationen, die Sie mir geben können, um loszulegen, wären großartig. Ich bewundere wirklich Leute, die Shader schreiben können. Ich habe ein paar Artikel für Unity Shaders durchgesehen und ja, sehr verwirrend. Ich denke, einfach durch Texturen (oder Textur-UVs?) Zu scrollen wäre gut genug und mit der Fähigkeit zu tönen.
JacketPotatoeFan

Antworten:


13

Ich werde dies in ein paar Schichten aufbauen, damit Sie sehen können, wie es zusammenkommt.

Beginnen Sie mit dem Erstellen eines neuen Shaders in Unity, indem Sie Create --> Shader --> Unlitentweder das Menü "Assets" oder das Kontextmenü mit der rechten Maustaste im Projektfenster auswählen .

Im oberen Block fügen wir einen _ScrollSpeedsParameter hinzu, um zu steuern, wie schnell sich die Textur in jede Richtung bewegt:

Properties
{
    _MainTex ("Texture", 2D) = "white" {}
    _ScrollSpeeds ("Scroll Speeds", vector) = (-5, -20, 0, 0)
}

Dadurch wird eine neue 4-Komponenten-Float-Eigenschaft im Materialinspektor mit dem Anzeigenamen "Bildlaufgeschwindigkeiten" verfügbar gemacht (ähnlich wie beim Hinzufügen einer publicoder einer SerializedVariablen zu einem MonoBehaviourSkript).

Als Nächstes verwenden wir diese Variable im Vertex-Shader, um die Texturkoordinaten ( o.uv) zu verschieben, indem wir dem Standard-Shader nur zwei Zeilen hinzufügen:

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;
}

Schlagen Sie das auf ein Quad (mit einer schönen freien Giraffentextur von Kenney ) und Sie erhalten:

Ein Quad mit einer sich wiederholenden Giraffe, die darüber rollt.

Um die Textur in einem Ring nach außen zu scrollen, könnten wir einfach ein Netz verwenden, das wie ein Spinnennetz unterteilt ist, wobei die UV-V-Koordinate von der Mitte nach außen zunimmt. Aber das wird einige sägeblattförmige Artefakte für sich ergeben. Stattdessen werde ich zeigen, wie wir unsere UVs pro Fragment berechnen können.

Dies ist etwas kostspieliger, sowohl aufgrund der Trigger- und Längenoperationen (die teurer sind als die grundlegende Mathematik) als auch, weil es nicht so effizient ist, Texturdaten in der Hardware vorherzusagen und zwischenzuspeichern, wenn Texcoords pro Fragment berechnet werden, als sie nur zu interpolieren zwischen Eckpunkten. Aber für einen kleinen Spezialeffekt wie diesen ist es nicht übertrieben.

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;
}

Das gibt uns ungefähr so ​​etwas (hier habe ich die Kachelparameter im Material erhöht, damit klarer wird, was passiert - nur eine einzige Wiederholung der Kachel um den Kreis zu wickeln sieht verzerrt und seltsam aus)

Fliesengiraffen, die von der Mitte eines Quad nach außen strahlen

Um die Textur durch einen Bildlaufverlauf zu tönen, können wir den Verlauf einfach als zweite Textur hinzufügen und miteinander multiplizieren.

Zuerst fügen wir den neuen Texturparameter oben hinzu:

Properties
{
    _MainTex ("Texture", 2D) = "white" {}
    _TintTex("Tint Texture", 2D) = "white" {}
    _ScrollSpeeds ("Scroll Speeds", vector) = (-5.0, -20.0, 0, 0)
}

Und deklarieren Sie es in unserem CGPROGRAM-Block, damit der CG-Shader es sehen kann:

sampler2D _MainTex;
float4 _MainTex_ST;

// Declare our second texture sampler and its Scale/Translate values
sampler2D _TintTex;
float4 _TintTex_ST;

float4 _ScrollSpeeds;

Aktualisieren Sie dann unseren Fragment-Shader, um beide Texturen zu verwenden:

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;
}

Und jetzt wird unsere Giraffe richtig trippig:

weit weg, Mann ....

Mit einer etwas künstlerischeren Auswahl von Texturen und Bildlaufraten kann dies einen Effekt erzeugen, der dem in der Frage gezeigten sehr ähnlich ist.


Möglicherweise stellen Sie bei der oben gezeigten Version zwei kleine Artefakte fest:

  • Die Gesichter in der Nähe des Kreismittelpunkts werden gestreckt / dünn / spitz, und wenn sie sich nach außen bewegen, werden sie gequetscht / breit.

    Diese Verzerrung tritt auf, weil wir eine feste Anzahl von Flächen um den Umfang haben, aber der Umfang, den sie überspannen, wird größer, wenn der Radius zunimmt, während ihre Höhe gleich bleibt.

    Wir können dies beheben, indem wir die vertikale Komponente der Texturprobe neu zuordnen, um einer logarithmischen Kurve zu folgen, sodass Wiederholungen der Textur mit zunehmendem Radius weiter auseinander liegen und näher an der Mitte liegen. (Tatsächlich gibt uns dies einen unendlichen Rückschritt von immer kleineren Giraffen ...)

  • Es gibt eine Reihe von ein oder zwei verschwommenen Pixeln in der Mitte links des Quad.

    Dies liegt daran, dass die GPU zwei benachbarte Texturbeispielkoordinaten betrachtet, um herauszufinden, welche Filterung verwendet werden soll. Wenn die Samples nahe beieinander liegen, wird die Textur groß / nah angezeigt und der detaillierteste Mip-Level angezeigt. Wenn die Samples weit voneinander entfernt sind, müssen wir die Textur vermutlich mit einem winzigen Zoom oder weit entfernt anzeigen und sie werden von einer kleineren / unschärferen Mipmap abgetastet, um sicherzustellen, dass wir keine funkelnden Aliasing-Artefakte erhalten.

    Das Problem ist hier, wir sind am Umlaufpunkt in Polarkoordinaten von -180 bis 180 Grad. Wir probieren also tatsächlich von sehr ähnlichen Punkten in unserem sich wiederholenden Texturraum aus, selbst wenn ihre numerischen Koordinaten sie so aussehen lassen, als wären sie weit voneinander entfernt. Daher können wir unsere eigenen Abtastgradientenvektoren bereitstellen, um dies zu korrigieren.

Hier ist eine Version mit diesen Korrekturen:

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;
}

2
Ein paar kleine Verbesserungen, die man vornehmen kann: 1) Die Verwendung einer logarithmischen Kurve für die v-Richtung hilft der Textur, ihre Form beim Scrollen nach außen beizubehalten (anstatt sich auf einen Punkt in der Mitte zu verengen, erhalten Sie nur eine unendliche Kette kleinerer Kopien bis Der Mip verschmiert es.) 2) Berechnen Sie Ihre eigenen tex2DgradTexturverläufe und korrigieren Sie das Filterartefakt, bei dem sich der Winkel von -pi nach + pi dreht (das ist die dünne Linie unscharfer Pixel auf der linken Seite). Hier ist das
ausgebesserte

Erstaunlich, danke auch für den Zusammenbruch. Ich habe das Gefühl, dass Shader etwas sind, das ich möglicherweise nie schreiben kann (insbesondere Dinge wie das, was Sie für den "Polar" getan haben, das mathematische Zeug bedeutet mir einfach nichts, da ich nur sehr grundlegende mathematische Fähigkeiten habe).
JacketPotatoeFan

1
Solche Sachen würden normalerweise nur nachschlagen. ;) Ich mache es gerade genug, dass es von der Tastatur rollt. Versuchen Sie jedoch, mit verschiedenen mathematischen Funktionen herumzuspielen, und sehen Sie sich die Ergebnisse an. Mit einem solchen Tool kann ich mir vorstellen, was die Mathematik geometrisch macht. (Nicht speziell mit Shadern, sondern mit einem alten WinAmp-Visualisierer namens WhiteCap ...) Selbst wenn die Shader-Mathematik schrecklich schief geht, ist es oft seltsam cool, sie anzusehen. : D
DMGregory
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.