Wenn Sie mit Polygon-Assets arbeiten, können Sie jeweils nur ein Objekt zeichnen (wenn Sie Techniken wie Stapeln und Instanziieren nicht berücksichtigen). Wenn Sie jedoch Entfernungsfelder mit einem Vorzeichen (signierte Entfernungsfelder, SDF) verwenden, sind wir nicht darauf beschränkt. Wenn zwei Positionen dieselbe Koordinate haben, geben die vorzeichenbehafteten Abstandsfunktionen denselben Wert zurück, und in einer Berechnung können wir mehrere Zahlen erhalten. Um zu verstehen, wie der zum Generieren von vorzeichenbehafteten Abstandsfeldern verwendete Raum transformiert wird, empfehlen
wir Ihnen, herauszufinden , wie
Sie mithilfe der vorzeichenbehafteten Abstandsfunktionen Formen erstellen und
SDF-Formen kombinieren .
Konfiguration
In diesem Tutorial ändere ich die Paarung zwischen dem Quadrat und dem Kreis, aber Sie können sie für jede andere Form verwenden. Dies ähnelt der Konfiguration für das
vorherige Lernprogramm .
Hierbei ist es wichtig, dass der modifizierbare Teil vor der Verwendung von Positionen zum Generieren von Zahlen liegt.
Shader "Tutorial/036_SDF_Space_Manpulation/Type"{ Properties{ _InsideColor("Inside Color", Color) = (.5, 0, 0, 1) _OutsideColor("Outside Color", Color) = (0, .5, 0, 1) _LineDistance("Mayor Line Distance", Range(0, 2)) = 1 _LineThickness("Mayor Line Thickness", Range(0, 0.1)) = 0.05 [IntRange]_SubLines("Lines between major lines", Range(1, 10)) = 4 _SubLineThickness("Thickness of inbetween lines", Range(0, 0.05)) = 0.01 } SubShader{
Und die Funktion 2D_SDF.cginc, die sich im selben Ordner wie der Shader befindet, den wir erweitern werden, sieht zunächst so aus:
#ifndef SDF_2D #define SDF_2D
Wiederholung des Raumes
Spiegelreflexion
Eine der einfachsten Operationen ist das Spiegeln der Welt um eine Achse. Um es um die y-Achse zu spiegeln, nehmen wir den absoluten Wert der x-Komponente unserer Position. Somit sind die Koordinaten rechts und links von der Achse gleich.
(-1, 1)
wird zu
(1, 1)
und befindet sich innerhalb eines Kreises, wobei
(1, 1)
als Koordinatenursprung und mit einem Radius größer als 0 verwendet wird.
Meistens sieht der Code, der diese Funktion verwendet, ungefähr so aus wie
position = mirror(position);
so können wir es ein bisschen vereinfachen. Wir werden das Positionsargument einfach als inout deklarieren. Wenn Sie also in das Argument schreiben, ändert sich auch die Variable, die wir an die Funktion übergeben. Der Rückgabewert kann dann vom Typ void sein, da wir den Rückgabewert immer noch nicht verwenden.
Es ist schon ziemlich gut gelaufen, aber auf diese Weise erhalten wir nur eine Achse zum Spiegeln. Wir können die Funktion erweitern, indem wir den Raum drehen, wie wir es beim Drehen der Figuren getan haben. Zuerst müssen Sie den Raum drehen, dann spiegeln und dann zurückdrehen. Auf diese Weise können wir eine Spiegelung in Bezug auf jeden Winkel durchführen. Das Gleiche ist möglich, wenn Speicherplatz übertragen und nach dem Spiegeln eine umgekehrte Übertragung durchgeführt wird. (Wenn Sie beide Operationen ausführen, vergessen Sie vor dem Spiegeln nicht, zuerst die Übertragung durchzuführen und dann zu drehen. Danach geht die Runde zuerst.)
Zellen
Wenn Sie wissen, wie die
Rauschgenerierung funktioniert, dann verstehen Sie, dass wir bei der prozeduralen Generierung häufig die Position wiederholen und kleine Zellen erhalten, die im Wesentlichen gleich sind und sich nur in unbedeutenden Parametern unterscheiden. Dasselbe können wir für Entfernungsfelder tun.
Da die
fmod
Funktion (und die Verwendung von% zum Teilen mit dem Rest) den Rest und nicht die Definition des Rests ergibt, müssen wir einen Trick verwenden. Zunächst nehmen wir den Rest der Ganzzahldivision durch die Funktion fmod. Für positive Zahlen ist dies genau das, was wir brauchen, und für negative Zahlen ist dies das Ergebnis, das wir abzüglich der Periode benötigen. Sie können dies beheben, indem Sie einen Punkt hinzufügen und den Rest der Division erneut übernehmen. Das Hinzufügen einer Periode ergibt das gewünschte Ergebnis für negative Eingabewerte, und für positive Eingabewerte ist der Wert eine Periode höher. Der zweite Rest der Division wird mit den Werten für negative Eingabewerte nichts anfangen, da sie bereits im Bereich von 0 bis zur Periode liegen und für positive Eingabewerte im Wesentlichen eine Periode subtrahiert wird.

Das Problem mit den Zellen ist, dass wir die Kontinuität verlieren, für die wir Distanzfelder lieben. Dies ist nicht schlecht, wenn sich die Formen nur in der Mitte der Zellen befinden. In dem oben gezeigten Beispiel kann dies jedoch zu erheblichen Artefakten führen, die vermieden werden sollten, wenn Distanzfelder für eine Vielzahl von Aufgaben verwendet werden, bei denen Distanzfelder normalerweise angewendet werden können.
Es gibt eine Lösung, die nicht in jedem Fall funktioniert, aber wenn sie funktioniert, ist es wunderbar: jede andere Zelle zu spiegeln. Dafür benötigen wir einen Pixelzellenindex, aber wir haben immer noch keinen Rückgabewert in der Funktion, sodass wir ihn einfach verwenden können, um den Zellenindex zurückzugeben.
Um den Zellenindex zu berechnen, teilen wir die Position durch die Periode. Somit ist 0-1 die erste Zelle, 1-2 die zweite und so weiter ... und wir können sie leicht diskretisieren. Um den Index der Zelle zu erhalten, runden wir den Wert einfach ab und geben das Ergebnis zurück. Wichtig ist, dass wir den Index der Zelle berechnen, bevor wir uns mit dem Rest teilen, um die Zellen zu wiederholen. Andernfalls würden wir überall den Index 0 erhalten, da die Position den Zeitraum nicht überschreiten kann.
Mit diesen Informationen können wir Zellen umdrehen. Um zu verstehen, ob umgedreht werden soll oder nicht, teilen wir den Zellenindex Modulo 2. Das Ergebnis dieser Operation ist abwechselnd 0 und 1 oder -1 pro zweite Zelle. Um die Änderung dauerhafter zu machen, nehmen wir den absoluten Wert und erhalten einen Wert, der zwischen 0 und 1 wechselt.
Um diesen Wert zum Umschalten zwischen einer normalen und einer umgedrehten Position zu verwenden, benötigen wir eine Funktion, die nichts für den Wert 0 tut und die Position von der Periode subtrahiert, in der das Umdrehen 1 ist. Das heißt, wir führen eine lineare Interpolation von der normalen zur umgedrehten Position unter Verwendung der Umkehrvariablen durch . Da die Flip-Variable ein 2d-Vektor ist, werden ihre Komponenten einzeln gespiegelt.
Radialzellen
Ein weiteres großartiges Merkmal ist die Wiederholung des Raums in einem radialen Muster.
Um diesen Effekt zu erzielen, berechnen wir zunächst die radiale Position. Dazu codieren wir den Winkel relativ zum Mittelpunkt der x-Achse und den Abstand vom Mittelpunkt entlang der y-Achse.
float2 radialPosition = float2(atan2(position.x, position.y), length(position));
Dann wiederholen wir die Ecke. Da die Übertragung der Anzahl der Wiederholungen viel einfacher ist als der Winkel jedes Stücks, berechnen wir zunächst die Größe jedes Stücks. Der gesamte Kreis ist 2 * pi. Um den richtigen Teil zu erhalten, teilen wir 2 * pi durch die Zellengröße.
const float PI = 3.14159; float cellSize = PI * 2 / cells;
Mit diesen Informationen können wir die x-Komponente der radialen Position jeder cellSize-Einheit wiederholen. Wir führen Wiederholungen durch Division mit dem Rest durch, daher erhalten wir nach wie vor Probleme mit negativen Zahlen, die mit Hilfe von zwei Funktionen der Division mit dem Rest beseitigt werden können.
radialPosition.x = fmod(fmod(radialPosition.x, cellSize) + cellSize, cellSize);
Dann müssen Sie die neue Position wieder auf die üblichen xy-Koordinaten verschieben. Hier verwenden wir die Sincos-Funktion mit der x-Komponente der radialen Position als Winkel, um den Sinus in die x-Koordinate der Position und den Cosinus in die y-Koordinate zu schreiben. Mit diesem Schritt erhalten wir eine normalisierte Position. Um die richtige Richtung von der Mitte zu erhalten, müssen Sie sie mit der Komponente y der radialen Position multiplizieren, dh der Länge.
Dann können wir auch den Zellindex und die Spiegelung hinzufügen, wie wir es bei normalen Zellen getan haben.
Es ist notwendig, den Zellenindex nach der Berechnung der radialen Position zu berechnen, aber bevor der Rest von der Teilung erhalten wird. Wir erhalten es, indem wir die x-Komponente der radialen Position teilen und das Ergebnis abrunden. In diesem Fall kann der Index auch negativ sein, und dies ist ein Problem, wenn die Anzahl der Zellen ungerade ist. Zum Beispiel erhalten wir mit 3 Zellen 1 Zelle mit einem Index von 0, 1 Zelle mit einem Index von -1 und 2 Halbzellen mit den Indizes 1 und -2. Um dieses Problem zu umgehen, addieren wir die Anzahl der Zellen zu der Variablen, die die Variable abgerundet hat, und dividieren sie durch die Größe der Zelle mit dem Rest.
Um dies widerzuspiegeln, müssen die Koordinaten im Bogenmaß angegeben werden. Um eine Neuberechnung der Radialkoordinaten außerhalb der Funktion zu vermeiden, fügen wir ihr eine Option mit dem Argument bool hinzu. Normalerweise ist in Shadern eine Verzweigung (wenn Konstrukte) nicht erwünscht, aber in diesem Fall gehen alle Pixel auf dem Bildschirm denselben Pfad, sodass dies normal ist.
Die Spiegelung sollte erfolgen, nachdem die Radialkoordinate geloopt wurde, aber bevor sie wieder in ihre normale Position konvertiert wurde. Wir finden heraus, ob wir die aktuelle Zelle umdrehen müssen, indem wir den Zellenindex durch 2 mit dem Rest teilen. Normalerweise sollte dies Nullen und Einsen ergeben, aber in meinem Fall erscheinen mehrere Zweien, was seltsam ist, und dennoch können wir damit umgehen. Um Zweien zu eliminieren, subtrahieren wir einfach 1 von der Flip-Variablen und nehmen dann den absoluten Wert. Somit werden Nullen und Zweien zu Einsen, und Einheiten werden nach Bedarf zu Nullen, und zwar nur in umgekehrter Reihenfolge.
Da Nullen und Einsen in der falschen Reihenfolge sind, führen wir eine lineare Interpolation von der verkehrten Version zur verkehrten Version durch und nicht wie zuvor umgekehrt. Um die Koordinate umzudrehen, subtrahieren wir einfach die Position von der Zellengröße.
Schwankender Raum
Aber um den Raum zu ändern, ist es nicht notwendig, ihn zu wiederholen. Im Tutorial zu den Grundlagen haben wir es beispielsweise gedreht, verschoben und skaliert. Sie können auch Folgendes tun: Bewegen Sie jede Achse mit einer Sinuswelle auf der Basis der anderen. Dadurch werden die Abstände der vorzeichenbehafteten Abstandsfunktion weniger genau, aber bis sie zu stark schwingen, ist alles in Ordnung.
Zuerst berechnen wir die Größe der Positionsänderung, indem wir die x- und y-Komponenten umdrehen und sie dann mit der Wobbelfrequenz multiplizieren. Dann nehmen wir den Sinus von diesem Wert und multiplizieren ihn mit der Menge an Wackeln, die wir hinzufügen möchten. Danach addieren wir einfach diesen Wobbelfaktor zur Position und wenden das Ergebnis erneut auf die Position an.
Wir können dieses Winken auch animieren, indem wir seine Position ändern, das Winken an der versetzten Position anwenden und den Raum zurückgeben. Damit die Gleitkommazahlen nicht zu groß werden, dividiere ich mit dem Rest pi * 2 durch die Wobbelfrequenz, dies korreliert mit dem Wobble (die Sinuskurve wiederholt alle pi * 2-Einheiten), sodass Sprünge und zu große Offsets vermieden werden.
Quellcode
2D SDF Bibliothek
#ifndef SDF_2D #define SDF_2D
Grundlegender Demo-Shader
Shader "Tutorial/036_SDF_Space_Manpulation/Mirror"{ Properties{ _InsideColor("Inside Color", Color) = (.5, 0, 0, 1) _OutsideColor("Outside Color", Color) = (0, .5, 0, 1) _LineDistance("Mayor Line Distance", Range(0, 2)) = 1 _LineThickness("Mayor Line Thickness", Range(0, 0.1)) = 0.05 [IntRange]_SubLines("Lines between major lines", Range(1, 10)) = 4 _SubLineThickness("Thickness of inbetween lines", Range(0, 0.05)) = 0.01 } SubShader{
Jetzt kennen Sie alle Grundlagen der Vorzeichenentfernungsfunktionen, an die ich mich erinnern konnte. Im nächsten Tutorial werde ich versuchen, etwas Interessantes mit ihnen zu machen.