In diesem Tutorial geht es um
interaktive Karten und deren Erstellung in Unity mithilfe von Shadern.
Dieser Effekt kann als Grundlage für komplexere Techniken wie holographische Projektionen oder sogar einen Sandtisch aus dem Film "Black Panther" dienen.
Eine Inspiration für dieses Tutorial ist der von
Baran Kahyaoglu veröffentlichte Tweet, der ein Beispiel dafür zeigt, was er für
Mapbox erstellt .
Die Szene (ohne Karte) wurde aus der Unity Visual Effect Graph-Raumschiff-Demo (siehe unten) entnommen, die hier heruntergeladen
werden kann .
Teil 1. Scheitelpunktversatz
Anatomie der Wirkung
Das erste, was Sie sofort bemerken können, ist, dass geografische Karten
flach sind : Wenn sie als Texturen verwendet werden, fehlt ihnen die Dreidimensionalität, die ein reales 3D-Modell des entsprechenden Kartenbereichs haben würde.
Sie können diese Lösung anwenden: Erstellen Sie ein 3D-Modell des Bereichs, der im Spiel benötigt wird, und wenden Sie dann eine Textur von der Karte darauf an. Dies wird zur Lösung des Problems beitragen, nimmt jedoch viel Zeit in Anspruch und ermöglicht es nicht, den Effekt des „Scrollens“ aus dem Video Baran Kahyaoglu zu realisieren.
Offensichtlich ist ein technischerer Ansatz am besten. Glücklicherweise können Shader verwendet werden, um die Geometrie eines 3D-Modells zu ändern. Mit ihrer Hilfe können Sie jedes Flugzeug in Täler und Berge der Region verwandeln, die wir brauchen.
In diesem Tutorial verwenden wir eine Karte
von Chillot , Chilli, berühmt für seine charakteristischen Hügel. Das Bild unten zeigt die Textur des Bereichs, der auf einem runden Netz aufgetragen ist.
Obwohl wir Hügel und Berge sehen, sind sie immer noch völlig flach. Dies zerstört die Illusion des Realismus.
Extrudieren von Normalen
Der erste Schritt zur Verwendung von Shadern zum Ändern der Geometrie ist eine Technik, die als
normale Extrusion bezeichnet wird . Sie benötigt
einen Scheitelpunktmodifikator : eine Funktion, mit der einzelne Scheitelpunkte eines 3D-Modells bearbeitet werden können.
Die Art und Weise, wie der Vertex-Modifikator verwendet wird, hängt von der Art des verwendeten Shaders ab. In diesem Tutorial ändern wir den
Surface Standard Shader - einen der Shader-Typen, die Sie in Unity erstellen können.
Es gibt viele Möglichkeiten, die Eckpunkte eines 3D-Modells zu bearbeiten. Eine der ersten Methoden, die in den meisten Vertex-Shader-Tutorials beschrieben werden, ist das
Extrudieren von Normalen . Es besteht darin, jeden Scheitelpunkt nach außen zu drücken (zu
extrudieren ), wodurch das 3D-Modell ein aufgeblähteres Aussehen erhält. "Außerhalb" bedeutet, dass sich jeder Scheitelpunkt entlang der Richtung der Normalen bewegt.
Bei glatten Oberflächen funktioniert dies sehr gut, aber bei Modellen mit schlechten Scheitelpunktverbindungen kann diese Methode seltsame Artefakte erzeugen. Dieser Effekt wird in einem meiner ersten Tutorials
ausführlich erläutert:
Eine sanfte Einführung in Shader , in der ich zeigte, wie
ein 3D-Modell
extrudiert und
eingedrungen wird .
Das Hinzufügen von extrudierten Normalen zu einem Oberflächen-Shader ist sehr einfach. Jeder Surface Shader verfügt über eine
#pragma
, mit der zusätzliche Informationen und Befehle übertragen werden. Ein solcher Befehl ist
vert
, was bedeutet, dass die
vert
Funktion verwendet wird, um jeden Scheitelpunkt des 3D-Modells zu verarbeiten.
Der bearbeitete Shader lautet wie folgt:
#pragma surface surf Standard fullforwardshadows addshadow vertex:vert ... float _Amount; ... void vert(inout appdata_base v) { v.vertex.xyz += v.normal * _Amount; }
Da wir die Position der Scheitelpunkte ändern, müssen wir auch
addshadow
wenn das Modell Schatten korrekt auf sich selbst werfen soll.
Was ist appdata_base?Wie Sie sehen können, haben wir eine Funktion des Vertices-Modifikators (
vert
) hinzugefügt, der als Parameter eine
Struktur namens
appdata_base
. Diese Struktur speichert Informationen zu jedem einzelnen Scheitelpunkt des 3D-Modells. Es enthält nicht nur
die Scheitelpunktposition (
v.vertex
), sondern auch andere Felder, z. B.
die normale Richtung (
v.normal
) und
Texturinformationen, die dem Scheitelpunkt (
v.texcoord
) zugeordnet sind.
In einigen Fällen reicht dies nicht aus, und wir benötigen möglicherweise andere Eigenschaften, z. B. die
Scheitelpunktfarbe (
v.color
) und die
Tangentenrichtung (
v.tangent
). Vertex-Modifikatoren können mithilfe einer Vielzahl anderer
appdata_tan
werden, einschließlich
appdata_tan
und
appdata_full
, die auf Kosten eines geringen Leistungsaufwands mehr Informationen bereitstellen. Weitere
appdata
zu
appdata
(und ihren Varianten) finden Sie im
Unity3D-Wiki .
Wie werden Werte von vert zurückgegeben?Die Top-Funktion hat keinen Rückgabewert. Wenn Sie mit der C # -Sprache vertraut sind, sollten Sie wissen, dass Strukturen als Wert übergeben werden. Wenn sich v.vertex
ändert v.vertex
wirkt sich dies nur auf die Kopie von v
, deren Umfang durch den Hauptteil der Funktion begrenzt ist.
v
auch als inout
deklariert, was bedeutet, dass es sowohl für die Eingabe als auch für die Ausgabe verwendet wird. Alle Änderungen, die Sie vornehmen, ändern die Variable selbst, die wir an vert
. Die Schlüsselwörter inout
und out
sehr häufig in der Computergrafik verwendet und können grob mit ref
und out
in C # korreliert werden.
Extrudieren von Normalen mit Texturen
Der oben verwendete Code funktioniert korrekt, ist jedoch weit von dem gewünschten Effekt entfernt. Der Grund ist, dass wir nicht alle Eckpunkte um den gleichen Betrag extrudieren möchten. Wir möchten, dass die Oberfläche des 3D-Modells mit den Tälern und Bergen der entsprechenden geografischen Region übereinstimmt. Zuerst müssen wir irgendwie Informationen darüber speichern und abrufen, wie viel jeder Punkt auf der Karte angehoben wird. Wir möchten, dass das Extrudieren von der Textur beeinflusst wird, in der die Höhen der Landschaft codiert sind. Solche Texturen werden oft als
Höhenkarten bezeichnet , aber je nach Kontext werden sie auch als
Tiefenkarten bezeichnet . Nachdem wir Informationen über die Höhen erhalten haben, können wir die Extrusion der Ebene basierend auf der Höhenkarte ändern. Wie in der Abbildung gezeigt, können wir so das Anheben und Absenken von Bereichen steuern.
Es ist ganz einfach, ein Satellitenbild des gewünschten geografischen Gebiets und eine zugehörige Höhenkarte zu finden. Unten finden Sie die Satellitenkarte des Mars (oben) und die Höhenkarte (unten), die in diesem Tutorial verwendet wurden:
Ich habe in einer anderen Reihe von Tutorials mit dem Titel
"3D-Fotos von Facebook von innen: Parallaxen-Shader" [
Übersetzung in Habré] ausführlich über das Konzept der Tiefenkarte gesprochen.
In diesem Tutorial wird davon ausgegangen, dass die Höhenkarte als Bild in Graustufen gespeichert ist, wobei Schwarzweiß niedrigeren und höheren Höhen entspricht. Wir brauchen diese Werte auch, um
linear zu skalieren,
dh der Farbunterschied, beispielsweise bei
0.1
entspricht einem Höhenunterschied zwischen
0
und
0.1
oder zwischen
0.9
und
1.0
. Bei Tiefenkarten ist dies nicht immer der Fall, da viele von ihnen Tiefeninformationen auf einer
logarithmischen Skala speichern.
Zum Abtasten einer Textur werden zwei Informationselemente benötigt: die Textur selbst und die
UV-Koordinaten des Punktes, den wir abtasten möchten. Auf Letzteres kann über das in der Struktur
texcoord
gespeicherte Feld
appdata_base
werden. Dies ist die UV-Koordinate, die dem aktuell verarbeiteten Scheitelpunkt zugeordnet ist. Die
tex2D
in einer
Oberflächenfunktion erfolgt mit
tex2D
. Wenn wir uns jedoch in einer
, ist
tex2Dlod
erforderlich.
Im folgenden
_HeightMap
eine Textur namens
_HeightMap
verwendet, um den für jeden Scheitelpunkt durchgeführten Extrusionswert zu ändern:
sampler2D _HeightMap; ... void vert(inout appdata_base v) { fixed height = tex2Dlod(_HeightMap, float4(v.texcoord.xy, 0, 0)).r; vertex.xyz += v.normal * height * _Amount; }
Warum kann tex2D nicht als Scheitelpunktfunktion verwendet werden?
Wenn Sie sich den Code ansehen, den Unity für Standard Surface Shader generiert, werden Sie feststellen, dass er bereits ein Beispiel für das Beispiel von Texturen enthält. Insbesondere wird die
_MainTex
(
_MainTex
) in einer
Oberflächenfunktion (genannt
surf
) unter Verwendung der integrierten
tex2D
Funktion
tex2D
.
Tatsächlich wird
tex2D
verwendet, um Pixel aus einer Textur
tex2D
, unabhängig davon, was darin gespeichert ist, Farbe oder Höhe. Möglicherweise stellen Sie jedoch fest, dass
tex2D
nicht in einer Scheitelpunktfunktion verwendet werden kann.
Der Grund ist, dass
tex2D
nicht nur Pixel aus der Textur liest. Sie entscheidet auch, welche Version der Textur verwendet werden soll, abhängig von der Entfernung zur Kamera. Diese Technik wird als
Mipmapping bezeichnet : Sie ermöglicht kleinere Versionen einer einzelnen Textur, die automatisch in unterschiedlichen Entfernungen verwendet werden können.
In der Oberflächenfunktion weiß der Shader bereits, welche
MIP-Textur verwendet werden soll. Diese Informationen sind möglicherweise noch nicht in der Scheitelpunktfunktion verfügbar, und daher kann
tex2D
nicht mit voller
tex2D
verwendet werden. Im Gegensatz dazu kann der
tex2Dlod
Funktion zwei zusätzliche Parameter übergeben werden, die in diesem Tutorial einen Nullwert haben können.
Das Ergebnis ist in den folgenden Bildern deutlich sichtbar.
In diesem Fall kann eine leichte Vereinfachung vorgenommen werden. Der zuvor überprüfte Code kann mit jeder Geometrie verwendet werden. Wir können jedoch davon ausgehen, dass die Oberfläche absolut flach ist. Tatsächlich möchten wir diesen Effekt wirklich auf das Flugzeug anwenden.
Daher können Sie
v.normal
entfernen und durch
float3(0, 1, 0)
ersetzen:
void vert(inout appdata_base v) { float3 normal = float3(0, 1, 0); fixed height = tex2Dlod(_HeightMap, float4(v.texcoord.xy, 0, 0)).r; vertex.xyz += normal * height * _Amount; }
Wir könnten dies tun, weil alle Koordinaten in
appdata_base
im
appdata_base
gespeichert sind,
appdata_base
sie werden relativ zum Zentrum und zur Ausrichtung des 3D-Modells festgelegt. Übergang, Drehung und Skalierung mit
Transformation in Unity ändern Position, Drehung und Skalierung des Objekts, haben jedoch keinen Einfluss auf das ursprüngliche 3D-Modell.
Teil 2. Bildlaufeffekt
Alles, was wir oben gemacht haben, funktioniert ziemlich gut. Bevor wir
getVertex
, extrahieren wir den Code, der zur Berechnung der neuen Scheitelpunkthöhe erforderlich ist, in eine separate Funktion
getVertex
:
float4 getVertex(float4 vertex, float2 texcoord) { float3 normal = float3(0, 1, 0); fixed height = tex2Dlod(_HeightMap, float4(texcoord, 0, 0)).r; vertex.xyz += normal * height * _Amount; return vertex; }
Dann hat die gesamte Funktion
vert
die Form:
void vert(inout appdata_base v) { vertex = getVertex(v.vertex, v.texcoord.xy); }
Wir haben dies getan, weil wir unten die Höhe mehrerer Punkte berechnen müssen. Aufgrund der Tatsache, dass diese Funktionalität in einer eigenen Funktion ausgeführt wird, wird der Code viel einfacher.
UV-Koordinatenberechnung
Dies führt uns jedoch zu einem anderen Problem. Die Funktion
getVertex
hängt nicht nur von der Position des aktuellen Scheitelpunkts (v.vertex) ab, sondern auch von seinen UV-Koordinaten (
v.texcoord
).
Wenn wir den Scheitelpunkthöhenversatz berechnen möchten, den die
vert
Funktion derzeit verarbeitet, sind beide Datenelemente in der Struktur
appdata_base
verfügbar. Was passiert jedoch, wenn wir die Position eines benachbarten Punkts abtasten müssen? In diesem Fall können wir die xyz-Position im
Modellraum kennen , haben jedoch keinen Zugriff auf die UV-Koordinaten.
Dies bedeutet, dass das vorhandene System den Höhenversatz nur für den aktuellen Scheitelpunkt berechnen kann. Eine solche Einschränkung wird es uns nicht ermöglichen, weiterzumachen, daher müssen wir eine Lösung finden.
Am einfachsten ist es, die UV-Koordinaten eines 3D-Objekts zu berechnen und die Position seines Scheitelpunkts zu kennen. Dies ist eine sehr schwierige Aufgabe, und es gibt verschiedene Techniken, um sie zu lösen (eine der beliebtesten ist die
triplanare Projektion ). In diesem speziellen Fall müssen wir UV jedoch nicht an die Geometrie anpassen. Wenn wir davon ausgehen, dass der Shader immer auf das flache Netz angewendet wird, wird die Aufgabe trivial.
Wir können
UV-Koordinaten (unteres Bild) aus den
Positionen der Eckpunkte (oberes Bild) berechnen, da beide linear auf einem flachen Netz überlagert sind.
Dies bedeutet, dass wir zur Lösung unseres Problems die
Komponenten XZ der Scheitelpunktposition in die entsprechenden
UV-Koordinaten umwandeln müssen.
Diese Prozedur wird als
lineare Interpolation bezeichnet . Es wird auf meiner Website ausführlich besprochen (zum Beispiel:
Die Geheimnisse der Farbinterpolation ).
In den meisten Fällen liegen die UV-Werte im Bereich von
vorher
;; Im Gegensatz dazu sind die Koordinaten jedes Scheitelpunkts möglicherweise unbegrenzt. Aus mathematischer Sicht benötigen wir für die Umstellung von XZ auf UV nur deren Grenzwerte:
- ,
- ,
- ,
- ,
die unten gezeigt werden:
Diese Werte variieren je nach verwendetem Netz. Auf der Unity-Ebene liegen die
UV-Koordinaten im Bereich von
vorher
und die
Koordinaten der Eckpunkte liegen im Bereich von
vorher
.
Die Gleichungen zur Umwandlung von XZ in UV sind:
(1)

Wie werden sie angezeigt?Wenn Sie mit dem Konzept der linearen Interpolation nicht vertraut sind, können diese Gleichungen ziemlich einschüchternd wirken.
Sie werden jedoch ganz einfach angezeigt. Schauen wir uns nur ein Beispiel an.
. Wir haben zwei Intervalle: eines hat Werte von
vorher
ein anderer aus
vorher
. Eingehende Daten für die Koordinate
ist die Koordinate des aktuellen Scheitelpunkts, der verarbeitet wird, und die Ausgabe ist die Koordinate
wird verwendet, um die Textur abzutasten.
Wir müssen die Eigenschaften der Proportionalität zwischen beibehalten
und sein Intervall und
und sein Intervall. Zum Beispiel wenn
zählt dann 25% seines Intervalls
wird auch 25% seines Intervalls ausmachen.
All dies ist in der folgenden Abbildung dargestellt:
Daraus können wir schließen, dass der Anteil des roten Segments in Bezug auf das Rosa dem Anteil zwischen dem blauen Segment und dem blauen Segment entsprechen sollte:
(2)
Jetzt können wir die oben gezeigte Gleichung transformieren, um zu erhalten
::
und diese Gleichung hat genau die gleiche Form wie oben gezeigt (1).
Diese Gleichungen können wie folgt im Code implementiert werden:
float2 _VertexMin; float2 _VertexMax; float2 _UVMin; float2 _UVMax; float2 vertexToUV(float4 vertex) { return (vertex.xz - _VertexMin) / (_VertexMax - _VertexMin) * (_UVMax - _UVMin) + _UVMin; }
Jetzt können wir die Funktion
getVertex
, ohne
v.texcoord
:
float4 getVertex(float4 vertex) { float3 normal = float3(0, 1, 0); float2 texcoord = vertexToUV(vertex); fixed height = tex2Dlod(_HeightMap, float4(texcoord, 0, 0)).r; vertex.xyz += normal * height * _Amount; return vertex; }
Dann nimmt die gesamte Funktion
vert
die Form an:
void vert(inout appdata_base v) { v.vertex = getVertex(v.vertex); }
Bildlaufeffekt
Dank des von uns geschriebenen Codes wird die gesamte Karte auf dem Netz angezeigt. Wenn wir die Anzeige verbessern wollen, müssen wir Änderungen vornehmen.
Lassen Sie uns den Code etwas formalisieren. Erstens müssen wir möglicherweise einen separaten Teil der Karte vergrößern, anstatt ihn als Ganzes zu betrachten.
Dieser Bereich kann durch zwei Werte definiert werden: seine Größe (
_CropSize
) und seine Position auf der Karte (
_CropOffset
), gemessen im
Scheitelpunktraum (von
_VertexMin
bis
_VertexMax
).
Nachdem wir diese beiden Werte erhalten haben, können wir wieder die lineare Interpolation verwenden, sodass
getVertex
nicht für die aktuelle Position der Oberseite des 3D-Modells
getVertex
, sondern für den skalierten und übertragenen Punkt.
Relevanter Code:
void vert(inout appdata_base v) { float2 croppedMin = _CropOffset; float2 croppedMax = croppedMin + _CropSize;
Wenn wir scrollen möchten, reicht es aus,
_CropOffset
über das Skript zu aktualisieren. Aus diesem Grund wird der Kürzungsbereich verschoben und tatsächlich durch die Landschaft gescrollt.
public class MoveMap : MonoBehaviour { public Material Material; public Vector2 Speed; public Vector2 Offset; private int CropOffsetID; void Start () { CropOffsetID = Shader.PropertyToID("_CropOffset"); } void Update () { Material.SetVector(CropOffsetID, Speed * Time.time + Offset); } }
Damit dies funktioniert, ist es sehr wichtig, den
Umbruchmodus aller Texturen auf
Wiederholen einzustellen. Wenn dies nicht getan wird, können wir die Textur nicht schleifen.
Für den Zoom / Zoom-Effekt reicht es aus, nur
_CropSize
ändern.
Teil 3. Geländeschattierung
Flache Schattierung
Der gesamte Code, den wir geschrieben haben, funktioniert, hat aber ein ernstes Problem. Das Modell zu beschatten ist irgendwie seltsam. Die Oberfläche ist richtig gekrümmt, reagiert aber auf Licht, als wäre es flach.
Dies ist in den folgenden Bildern sehr deutlich zu sehen. Das obere Bild zeigt einen vorhandenen Shader. Der untere Teil zeigt, wie es tatsächlich funktioniert.
Die Lösung dieses Problems kann eine große Herausforderung sein. Aber zuerst müssen wir herausfinden, was der Fehler ist.
Der normale Extrusionsvorgang hat die allgemeine Geometrie der ursprünglich verwendeten Ebene geändert. Unity hat jedoch nur die Position der Scheitelpunkte geändert, nicht jedoch ihre normalen Richtungen.
Die Richtung der Scheitelpunktnormalen ist, wie der Name schon sagt, ein Einheitslängenvektor (
Richtung ), der senkrecht zur Oberfläche anzeigt.
Normalen sind notwendig, weil sie eine wichtige Rolle bei der Schattierung eines 3D-Modells spielen. Sie werden von allen Oberflächen-Shadern verwendet, um zu berechnen, wie Licht von jedem Dreieck des 3D-Modells reflektiert werden soll. Normalerweise ist dies notwendig, um die Dreidimensionalität des Modells zu verbessern. Beispielsweise wird Licht von einer ebenen Oberfläche reflektiert, genau wie es von einer gekrümmten Oberfläche reflektiert wird. Dieser Trick wird häufig verwendet, um Low-Poly-Oberflächen glatter aussehen zu lassen als sie tatsächlich sind (siehe unten).
In unserem Fall passiert jedoch das Gegenteil. Die Geometrie ist gekrümmt und glatt, aber da alle Normalen nach oben gerichtet sind, wird das Licht vom Modell reflektiert, als wäre es flach (siehe unten):
Weitere Informationen zur Rolle von Normalen bei der Objektschattierung finden Sie im Artikel über
Normal Mapping (Bump Mapping) , in dem identische Zylinder trotz des gleichen 3D-Modells aufgrund unterschiedlicher Methoden zur Berechnung von Scheitelpunktnormalen sehr unterschiedlich aussehen (siehe unten).
Leider verfügen weder Unity noch die Sprache zum Erstellen von Shadern über eine integrierte Lösung zum automatischen Neuberechnen von Normalen. Dies bedeutet, dass Sie sie abhängig von der lokalen Geometrie des 3D-Modells manuell ändern müssen.
Normale Berechnung
Die einzige Möglichkeit, das Schattierungsproblem zu beheben, besteht darin, die Normalen basierend auf der Oberflächengeometrie manuell zu berechnen. Eine ähnliche Aufgabe wurde in einem Beitrag von
Vertex Displacement - Melting Shader Part 1 besprochen, in dem das Schmelzen von 3D-Modellen in
Cone Wars simuliert
wurde .
Obwohl der fertige Code in 3D-Koordinaten arbeiten muss, beschränken wir die Aufgabe vorerst auf nur zwei Dimensionen. Stellen Sie sich vor, Sie müssen die
Richtung der Normalen berechnen, die dem Punkt auf der 2D-Kurve entspricht (der große blaue Pfeil im folgenden Diagramm).
Aus geometrischer Sicht ist die
Richtung der Normalen (großer blauer Pfeil) ein Vektor senkrecht zu der
Tangente, die durch den für uns interessanten Punkt verläuft (eine dünne blaue Linie).
Die Tangente kann als Linie dargestellt werden, die sich auf der Krümmung des Modells befindet.
Ein Tangentenvektor ist ein
Einheitsvektor , der auf einer Tangente liegt.
Dies bedeutet, dass Sie zur Berechnung der Normalen zwei Schritte ausführen müssen: Suchen Sie zuerst die
Tangente an den gewünschten Punkt. Berechnen Sie dann den Vektor senkrecht dazu (dies ist die notwendige
Richtung der Normalen ).
Tangentenberechnung
Um die
Normalität zu erhalten, müssen wir zuerst die
Tangente berechnen. Sie kann angenähert werden, indem ein Punkt in der Nähe abgetastet und daraus eine Linie in der Nähe des Scheitelpunkts erstellt wird. Je kleiner die Linie, desto genauer ist der Wert.
Drei Schritte sind erforderlich:
- Stufe 1. Bewegen Sie eine kleine Menge auf einer ebenen Fläche
- Schritt 2. Berechnen Sie die Höhe des neuen Punktes.
- Schritt 3. Verwenden Sie die Höhe des aktuellen Punkts, um die Tangente zu berechnen
All dies ist im Bild unten zu sehen:
Damit dies funktioniert, müssen wir die Höhe von zwei Punkten berechnen, nicht von einem. Zum Glück wissen wir bereits, wie das geht. Im vorherigen Teil des Tutorials haben wir eine Funktion erstellt, die die Höhe einer Landschaft basierend auf einem Netzpunkt abtastet. Wir haben es
getVertex
.
Wir können den neuen Scheitelpunktwert am aktuellen Punkt und dann an zwei anderen nehmen. Einer ist für die Tangente, der andere für die Tangente an zwei Punkten. Mit ihrer Hilfe bekommen wir das Normale. Wenn das ursprüngliche Netz, mit dem der Effekt erstellt wurde, flach ist (und in unserem Fall auch), benötigen wir keinen Zugriff auf
v.normal
und können einfach
float3(0, 0, 1)
für Tangente bzw. Tangente an zwei Punkte
float3(0, 0, 1)
und
float3(1, 0, 0)
. Wenn wir dasselbe tun wollten, aber zum Beispiel für eine Kugel, wäre es viel schwieriger, zwei geeignete Punkte für die Berechnung der Tangente und der Tangente an zwei Punkte zu finden.
Vektorgrafiken
Nachdem wir die geeigneten Tangenten- und Tangentenvektoren für zwei Punkte erhalten haben, können wir die Normalen unter Verwendung einer Operation berechnen, die als
Vektorprodukt bezeichnet wird . Es gibt viele Definitionen und Erklärungen für eine Vektorarbeit und deren Funktionsweise.
Ein Vektorprodukt empfängt zwei Vektoren und gibt einen neuen zurück. Wenn zwei Anfangsvektoren Einheit waren (ihre Länge ist gleich Eins) und sie sich in einem Winkel von 90 befinden, befindet sich der resultierende Vektor bei 90 Grad relativ zu beiden.
Dies kann zunächst verwirrend sein, kann jedoch grafisch wie folgt dargestellt werden: Das Vektorprodukt zweier Achsen erzeugt eine dritte. Also
aber auch
, usw.
Wenn wir einen ausreichend kleinen Schritt machen (im Code ist dies
offset
), befinden sich die Vektoren der Tangente und der Tangente an zwei Punkte in einem Winkel von 90 Grad.
Zusammen mit dem Normalenvektor bilden sie drei senkrechte Achsen, die entlang der Oberfläche des Modells ausgerichtet sind.Wenn wir das wissen, können wir den gesamten notwendigen Code schreiben, um den normalen Vektor zu berechnen und zu aktualisieren. void vert(inout appdata_base v) { float3 bitangent = float3(1, 0, 0); float3 tangent = float3(0, 0, 1); float offset = 0.01; float4 vertexBitangent = getVertex(v.vertex + float4(bitangent * offset, 0) ); float4 vertex = getVertex(v.vertex); float4 vertexTangent = getVertex(v.vertex + float4(tangent * offset, 0) ); float3 newBitangent = (vertexBitangent - vertex).xyz; float3 newTangent = (vertexTangent - vertex).xyz; v.normal = cross(newTangent, newBitangent); v.vertex.y = vertex.y; }
Alles zusammenfügen
Jetzt, da alles funktioniert, können wir den Scroll-Effekt zurückgeben. void vert(inout appdata_base v) {
Und damit ist unser Effekt endlich abgeschlossen.Wohin als nächstes gehen
Dieses Tutorial kann zur Grundlage für komplexere Effekte werden, beispielsweise für holographische Projektionen oder sogar für eine Kopie des Sandtisches aus dem Film "Black Panther".Einheitspaket
Das vollständige Paket für dieses Tutorial kann auf Patreon heruntergeladen werden. Es enthält alle Elemente , die zum Spielen des beschriebenen Effekts erforderlich sind.