Obwohl Netze die einfachste und vielseitigste Möglichkeit zum Rendern sind, gibt es andere Optionen fĂŒr die Darstellung von Formen in 2D und 3D. Eine hĂ€ufig verwendete Methode sind SDF (Signed Distance Fields). Signierte Entfernungsfelder bieten eine kostengĂŒnstigere Strahlverfolgung, ermöglichen einen reibungslosen Ablauf verschiedener Formen und sparen Texturen mit niedriger Auflösung fĂŒr qualitativ hochwertige Bilder.
Wir werden zunÀchst das Vorzeichen der Distanzfelder mit zweidimensionalen Funktionen erzeugen, spÀter jedoch weiterhin in 3D. Ich werde die Koordinaten des Weltraums verwenden, damit wir so wenig wie möglich von Skalierung und UV-Koordinaten abhÀngig sind. Wenn Sie also nicht verstehen, wie dies funktioniert, lesen Sie dieses
Tutorial auf einer flachen Ăberlagerung , in der erklĂ€rt wird, was passiert.
Vorbereitung der Stiftung
Wir werden die Eigenschaften vorĂŒbergehend vom Basis-Flat-Overlay-Shader wegwerfen, da wir uns vorerst um die technische Basis kĂŒmmern. Dann schreiben wir die Position des Scheitelpunkts in der Welt direkt in die Fragmentstruktur und werden sie nicht zuerst in UV konvertieren. In der letzten Phase der Vorbereitung werden wir eine neue Funktion schreiben, die die Szene berechnet und die Entfernung zur nĂ€chsten OberflĂ€che zurĂŒckgibt. Dann rufen wir die Funktionen auf und verwenden das Ergebnis als Farbe.
Shader "Tutorial/034_2D_SDF_Basics"{ SubShader{
Ich werde alle Funktionen fĂŒr die signierten Distanzfelder in eine separate Datei schreiben, damit wir sie wiederholt verwenden können. Dazu erstelle ich eine neue Datei. Wir werden nichts Böses hinzufĂŒgen, dann setzen wir es und schlieĂen den bedingten Include-Schutz ab, wobei wir zuerst prĂŒfen, ob die PrĂ€prozessorvariable gesetzt ist. Wenn es noch nicht definiert ist, definieren wir es und vervollstĂ€ndigen das bedingte if-Konstrukt nach den Funktionen, die wir einschlieĂen möchten. Dies hat den Vorteil, dass der Shader beschĂ€digt wird, wenn wir die Datei zweimal hinzufĂŒgen (z. B. wenn wir zwei verschiedene Dateien hinzufĂŒgen, von denen jede die Funktionen hat, die wir benötigen, und beide dieselbe Datei hinzufĂŒgen). Wenn Sie sicher sind, dass dies niemals passieren wird, können Sie diese PrĂŒfung nicht durchfĂŒhren.
Wenn sich die Include-Datei im selben Ordner wie der Haupt-Shader befindet, können wir sie einfach mit dem Pragma-Konstrukt einschlieĂen.
Wir sehen also nur eine schwarze OberflÀche auf der gerenderten OberflÀche, die bereit ist, die Entfernung mit einem Zeichen darauf anzuzeigen.
Kreis
Die einfachste Funktion des vorzeichenbehafteten Distanzfeldes ist die Kreisfunktion. Die Funktion erhÀlt nur die Position der Probe und den Radius des Kreises. Wir beginnen mit der LÀnge des Probenpositionsvektors. Wir erhalten also einen Punkt an der Position (0, 0), der einem Kreis mit einem Radius von 0 Àhnelt.
float circle(float2 samplePosition, float radius){ return length(samplePosition); }
AnschlieĂend können Sie die Kreisfunktion in der Szenenfunktion aufrufen und die zurĂŒckgegebene Entfernung zurĂŒckgeben.
float scene(float2 position) { float sceneDistance = circle(position, 2); return sceneDistance; }
Dann addieren wir den Radius zu den Berechnungen. Ein wichtiger Aspekt der vorzeichenbehafteten Abstandsfunktionen ist, dass wir innerhalb des Objekts einen negativen Abstand zur OberflĂ€che erhalten (dies bedeutet das Wort vorzeichenbehaftet im Feld vorzeichenbehaftete Entfernung). Um den Kreis auf einen Radius zu vergröĂern, subtrahieren wir einfach den Radius von der LĂ€nge. Somit bewegt sich die OberflĂ€che, die sich ĂŒberall dort befindet, wo die Funktion 0 zurĂŒckgibt, nach auĂen. Das, was fĂŒr einen Kreis mit einer GröĂe von 0 in zwei Einheiten des Abstands von der OberflĂ€che liegt, ist nur eine Einheit von einem Kreis mit einem Radius von 1 und eine Einheit innerhalb des Kreises (der Wert ist -1) fĂŒr einen Kreis mit einem Radius von 3;
float circle(float2 samplePosition, float radius){ return length(samplePosition) - radius; }
Jetzt können wir den Kreis nur noch von der Mitte aus verschieben. Um dies zu beheben, können Sie der Kreisfunktion ein neues Argument hinzufĂŒgen, um den Abstand zwischen der Probenposition und dem Mittelpunkt des Kreises zu berechnen, und den Radius von diesem Wert subtrahieren, um einen Kreis zu definieren. Sie können den Ursprung auch neu definieren, indem Sie den Raum des Abtastpunkts verschieben und dann einen Kreis in diesem Raum erhalten. Die zweite Option sieht viel komplizierter aus, aber da das Verschieben von Objekten eine Operation ist, die wir fĂŒr alle Figuren verwenden möchten, ist sie viel universeller, und deshalb werde ich sie erklĂ€ren.
Umzug
"Transformation des Raumes eines Punktes" - klingt viel schlimmer als es tatsĂ€chlich ist. Dies bedeutet, dass wir den Punkt an die Funktion ĂŒbergeben und die Funktion ihn Ă€ndert, damit wir ihn auch in Zukunft verwenden können. Bei einer Ăbertragung subtrahieren wir einfach den Versatz vom Punkt. Die Position wird subtrahiert, wenn wir die Formen in die positive Richtung verschieben möchten, da sich die Formen, die wir im Raum rendern, in die entgegengesetzte Richtung zur Bewegung des Raums bewegen.
Wenn wir zum Beispiel eine Kugel in Position
(3, 4)
zeichnen möchten, mĂŒssen wir den Raum so Ă€ndern, dass
(3, 4)
zu
(0, 0)
, und dafĂŒr mĂŒssen wir
(3, 4)
subtrahieren. Wenn wir nun eine Kugel um einen
neuen Ursprungspunkt zeichnen, ist dies ein
alter Punkt
(3, 4)
.
float scene(float2 position) { float2 circlePosition = translate(position, float2(3, 2)); float sceneDistance = circle(circlePosition, 2); return sceneDistance; }
Rechteck
Eine andere einfache Form ist ein Rechteck. ZunĂ€chst betrachten wir die Komponenten separat. Zuerst erhalten wir den Abstand vom Zentrum, wobei wir den absoluten Wert nehmen. Dann subtrahieren wir Ă€hnlich wie bei einem Kreis die HĂ€lfte der GröĂe (die im Wesentlichen dem Radius eines Rechtecks ââĂ€hnelt). Um nur zu zeigen, wie die Ergebnisse aussehen werden, geben wir vorerst nur eine Komponente zurĂŒck.
float rectangle(float2 samplePosition, float2 halfSize){ float2 componentWiseEdgeDistance = abs(samplePosition) - halfSize; return componentWiseEdgeDistance.x; }
Jetzt können wir eine billige Version des Rechtecks ââerhalten, indem wir einfach die gröĂte Komponente 2 zurĂŒckgeben. Dies funktioniert in vielen FĂ€llen, aber nicht korrekt, da nicht der richtige Abstand um die Ecken angezeigt wird.
Die korrekten Werte fĂŒr das Rechteck auĂerhalb der Figur können erhalten werden, indem zuerst das Maximum zwischen den AbstĂ€nden zu den Kanten und 0 und dann seine LĂ€nge genommen werden.
Wenn wir den Abstand von unten nicht auf 0 begrenzen, berechnen wir einfach den Abstand zu den Ecken (wobei edgeDistances
(0, 0)
), aber die Koordinaten zwischen den Ecken fallen nicht unter 0, sodass die gesamte Kante verwendet wird. Dies hat den Nachteil, dass 0 als Abstand von der Kante fĂŒr das gesamte Innere der Figur verwendet wird.
Um den Abstand 0 fĂŒr den gesamten inneren Teil zu korrigieren, mĂŒssen Sie den Innenabstand einfach mit der billigen Rechteckformel (unter Verwendung des Maximalwerts aus der x- und y-Komponente) generieren und dann sicherstellen, dass er niemals 0 ĂŒberschreitet, und den Minimalwert daraus auf 0 setzen. Dann addieren wir den externen Abstand, der niemals kleiner als 0 ist, und den inneren Abstand, der niemals 0 ĂŒberschreitet, und erhalten die fertige Distanzfunktion.
float rectangle(float2 samplePosition, float2 halfSize){ float2 componentWiseEdgeDistance = abs(samplePosition) - halfSize; float outsideDistance = length(max(componentWiseEdgeDistance, 0)); float insideDistance = min(max(componentWiseEdgeDistance.x, componentWiseEdgeDistance.y), 0); return outsideDistance + insideDistance; }
Da wir die Ăbertragungsfunktion zuvor in universeller Form aufgezeichnet haben, können wir sie jetzt auch verwenden, um ihre Mitte an einen beliebigen Ort zu verschieben.
float scene(float2 position) { float2 circlePosition = translate(position, float2(1, 0)); float sceneDistance = rectangle(circlePosition, float2(1, 2)); return sceneDistance; }
Drehen Sie sich
Das Drehen von Formen Ă€hnelt dem Bewegen. Bevor wir den Abstand zur Figur berechnen, drehen wir die Koordinaten in die entgegengesetzte Richtung. Um das VerstĂ€ndnis der Rotationen so weit wie möglich zu vereinfachen, multiplizieren wir die Rotation mit 2 * pi, um den Winkel im BogenmaĂ zu erhalten. Daher ĂŒbergeben wir eine Rotation an die Funktion, wobei 0,25 eine viertel Umdrehung, 0,5 eine halbe Umdrehung und 1 eine vollstĂ€ndige Umdrehung ist (Sie können Konvertierungen anders durchfĂŒhren, wenn es Ihnen natĂŒrlicher erscheint). Wir kehren auch die Drehung um, da wir die Position aus dem gleichen Grund wie beim Bewegen in die entgegengesetzte Richtung zur Drehung der Figur drehen mĂŒssen.
Um die gedrehten Koordinaten zu berechnen, berechnen wir zuerst den Sinus und den Cosinus basierend auf dem Winkel. Hlsl hat eine Sincos-Funktion, die diese beiden Werte schneller berechnet als wenn sie separat berechnet werden.
Wenn wir einen neuen Vektor fĂŒr die Komponente x konstruieren, nehmen wir die ursprĂŒngliche Komponente x multipliziert mit dem Kosinus und die Komponente y multipliziert mit dem Sinus. Dies kann leicht in Erinnerung bleiben, wenn Sie sich daran erinnern, dass der Cosinus von 0 1 ist, und wenn er um 0 gedreht wird, soll die Komponente x des neuen Vektors genau dieselbe sein wie zuvor (dh mit 1 multiplizieren). Die Komponente y, die zuvor nach oben zeigte und keinen Beitrag zur Komponente x leistete, dreht sich nach rechts, und ihre Werte beginnen bei 0 und werden zunĂ€chst gröĂer, dh ihre Bewegung wird vollstĂ€ndig durch einen Sinus beschrieben.
FĂŒr die Komponente y des neuen Vektors multiplizieren wir den Kosinus mit der Komponente y des alten Vektors und subtrahieren den Sinus multipliziert mit der alten Komponente x. Um zu verstehen, warum wir den Sinus, multipliziert mit der Komponente x, subtrahieren, anstatt ihn zu addieren, stellen Sie sich am besten vor, wie sich der Vektor
(1, 0)
im Uhrzeigersinn Àndert. Die y-Komponente des Ergebnisses beginnt bei 0 und wird dann kleiner als 0. Dies ist das Gegenteil des Verhaltens des Sinus, daher Àndern wir das Vorzeichen.
float2 rotate(float2 samplePosition, float rotation){ const float PI = 3.14159; float angle = rotation * PI * 2 * -1; float sine, cosine; sincos(angle, sine, cosine); return float2(cosine * samplePosition.x + sine * samplePosition.y, cosine * samplePosition.y - sine * samplePosition.x); }
Nachdem wir die Rotationsmethode geschrieben haben, können wir sie in Kombination mit der Ăbertragung verwenden, um die Figur zu bewegen und zu drehen.
float scene(float2 position) { float2 circlePosition = position; circlePosition = rotate(circlePosition, _Time.y); circlePosition = translate(circlePosition, float2(2, 0)); float sceneDistance = rectangle(circlePosition, float2(1, 2)); return sceneDistance; }
In diesem Fall drehen wir zuerst das Objekt um die Mitte der gesamten Szene, sodass die Drehung auch die Ăbertragung beeinflusst. Um eine Figur relativ zu ihrer eigenen Mitte zu drehen, mĂŒssen Sie sie zuerst verschieben und dann drehen. Aufgrund dieser zum Zeitpunkt der Drehung geĂ€nderten Reihenfolge wird der Mittelpunkt der Figur zum Mittelpunkt des Koordinatensystems.
float scene(float2 position) { float2 circlePosition = position; circlePosition = translate(circlePosition, float2(2, 0)); circlePosition = rotate(circlePosition, _Time.y); float sceneDistance = rectangle(circlePosition, float2(1, 2)); return sceneDistance; }
Skalieren
Die Skalierung funktioniert Ă€hnlich wie andere Arten, Formen zu transformieren. Wir teilen die Koordinaten durch Skalierung, rendern die Figur im Raum mit einer reduzierten Skalierung und im Basiskoordinatensystem werden sie gröĂer.
float2 scale(float2 samplePosition, float scale){ return samplePosition / scale; }
float scene(float2 position) { float2 circlePosition = position; circlePosition = translate(circlePosition, float2(0, 0)); circlePosition = rotate(circlePosition, .125); float pulseScale = 1 + 0.5*sin(_Time.y * 3.14); circlePosition = scale(circlePosition, pulseScale); float sceneDistance = rectangle(circlePosition, float2(1, 2)); return sceneDistance; }
Obwohl dies die Skalierung korrekt durchfĂŒhrt, skaliert auch der Abstand. Der Hauptvorteil des vorzeichenbehafteten Entfernungsfelds besteht darin, dass wir immer die Entfernung zur nĂ€chsten OberflĂ€che kennen, das Herauszoomen diese Eigenschaft jedoch vollstĂ€ndig zerstört. Dies kann leicht behoben werden, indem das aus der Vorzeichenentfernungsfunktion (in unserem Fall das
rectangle
) erhaltene Distanzfeld mit der Skala multipliziert wird. Aus dem gleichen Grund können wir nicht leicht ungleichmĂ€Ăig skalieren (mit unterschiedlichen Skalierungen fĂŒr die x- und y-Achse).
float scene(float2 position) { float2 circlePosition = position; circlePosition = translate(circlePosition, float2(0, 0)); circlePosition = rotate(circlePosition, .125); float pulseScale = 1 + 0.5*sin(_Time.y * 3.14); circlePosition = scale(circlePosition, pulseScale); float sceneDistance = rectangle(circlePosition, float2(1, 2)) * pulseScale; return sceneDistance; }
Visualisierung
Signierte Entfernungsfelder können fĂŒr eine Vielzahl von Dingen verwendet werden, z. B. zum Erstellen von Schatten, Rendern von 3D-Szenen, Physik und Rendern von Text. Wir möchten jedoch noch nicht tief in die KomplexitĂ€t eintauchen, daher werde ich nur zwei Techniken ihrer Visualisierung erlĂ€utern. Die erste ist eine klare Form mit Antialiasing, die zweite ist das Rendern von Linien in AbhĂ€ngigkeit von der Entfernung.
Klare Form
Diese Methode Ă€hnelt der beim Rendern von Text hĂ€ufig verwendeten Methode und erstellt eine klare Form. Wenn wir ein Distanzfeld nicht aus einer Funktion erzeugen, sondern aus einer Textur lesen möchten, können wir Texturen mit einer viel niedrigeren Auflösung als ĂŒblich verwenden und gute Ergebnisse erzielen. TextMesh Pro verwendet diese Technik zum Rendern von Text.
Um diese Technik anzuwenden, nutzen wir die Tatsache, dass die Daten in den Entfernungsfeldern signiert sind und wir den Grenzwert kennen. Wir beginnen mit der Berechnung, wie weit sich das Distanzfeld zum nÀchsten Pixel Àndert. Dies sollte der gleiche Wert sein wie die LÀnge der KoordinatenÀnderung, aber es ist einfacher und zuverlÀssiger, die Entfernung mit einem Vorzeichen zu berechnen.
Nachdem wir die EntfernungsÀnderung erhalten haben, können wir einen
reibungslosen Schritt von der halben EntfernungsĂ€nderung zu minus / plus der halben EntfernungsĂ€nderung machen. Dies fĂŒhrt ein einfaches Abschneiden um etwa 0 durch, jedoch mit GlĂ€ttung. Dann können Sie diesen geglĂ€tteten Wert fĂŒr jeden benötigten BinĂ€rwert verwenden. In diesem Beispiel werde ich den Shader in einen Transparenz-Shader Ă€ndern und ihn fĂŒr den Alphakanal verwenden. Ich mache einen glatten Schritt von einem positiven zu einem negativen Wert, weil wir wollen, dass der negative Wert des Distanzfeldes sichtbar ist. Wenn Sie nicht genau verstehen, wie Transparenz-Rendering hier funktioniert, empfehlen wir Ihnen,
mein Tutorial zum Transparenz-Rendering zu lesen.
fixed4 frag(v2f i) : SV_TARGET{ float dist = scene(i.worldPos.xz); float distanceChange = fwidth(dist) * 0.5; float antialiasedCutoff = smoothstep(distanceChange, -distanceChange, dist); fixed4 col = fixed4(_Color, antialiasedCutoff); return col; }
Höhenlinien
Eine andere ĂŒbliche Technik zum Visualisieren von Entfernungsfeldern besteht darin, Entfernungen als Linien anzuzeigen. In unserer Implementierung werde ich ein paar dicke Linien und ein paar dĂŒnne Linien dazwischen hinzufĂŒgen. Ich werde auch die Innen- und AuĂenseite der Figur in verschiedenen Farben bemalen, damit Sie sehen können, wo sich das Objekt befindet.
Wir beginnen mit der Darstellung des Unterschieds zwischen der Innenseite und der AuĂenseite der Figur. Die Farben können im Material angepasst werden, daher werden neue Eigenschaften sowie Shader-Variablen fĂŒr die Innen- und AuĂenfarben der Figur hinzugefĂŒgt.
Properties{ _InsideColor("Inside Color", Color) = (.5, 0, 0, 1) _OutsideColor("Outside Color", Color) = (0, .5, 0, 1) }
Dann ĂŒberprĂŒfen wir im Fragment-Shader, wo sich das Pixel befindet, was wir rendern, indem wir den Abstand mit dem Vorzeichen mit 0 mit der
step
. Wir verwenden diese Variable, um von der inneren zur Ă€uĂeren Farbe zu interpolieren und sie auf dem Bildschirm zu rendern.
fixed4 frag(v2f i) : SV_TARGET{ float dist = scene(i.worldPos.xz); fixed4 col = lerp(_InsideColor, _OutsideColor, step(0, dist)); return col; }
Um Linien zu rendern, mĂŒssen wir zunĂ€chst angeben, wie oft und wie dick Linien gerendert werden sollen, und die Eigenschaften und die entsprechenden Shader-Variablen festlegen.
Um die Linien zu rendern, berechnen wir zunĂ€chst die AbstandsĂ€nderung, damit wir sie spĂ€ter zum GlĂ€tten verwenden können. Wir haben es auch bereits durch 2 geteilt, weil wir spĂ€ter die HĂ€lfte davon addieren und die HĂ€lfte davon subtrahieren, um den Ănderungsabstand von 1 Pixel abzudecken.
float distanceChange = fwidth(dist) * 0.5;
Dann nehmen wir die Distanz und transformieren sie so, dass sie sich an sich wiederholenden Punkten genauso verhÀlt. Dazu teilen wir es zuerst durch den Abstand zwischen den Linien, wÀhrend wir nicht bei jedem ersten Schritt vollstÀndige Zahlen erhalten, sondern vollstÀndige Zahlen nur auf der Grundlage des von uns festgelegten Abstands.
Dann addieren wir 0,5 zur Zahl, nehmen den Bruchteil und subtrahieren erneut 0,5. Der Bruchteil und die Subtraktion werden hier benötigt, damit die Linie im sich wiederholenden Muster durch Null geht. Wir addieren 0,5, um den Bruchteil zu erhalten, um eine weitere Subtraktion von 0,5 zu neutralisieren - der Versatz fĂŒhrt dazu, dass die Werte, bei denen der Graph 0 ist, bei 0, 1, 2 usw. liegen und nicht bei 0,5, 1,5, usw.
Die letzten Schritte zum Konvertieren des Werts - wir nehmen den absoluten Wert und multiplizieren ihn erneut mit dem Abstand zwischen den Linien. Durch den absoluten Wert bleiben die Bereiche vor und nach den Punkten der Linie gleich, was das Erstellen von Ausschnitten fĂŒr die Linien erleichtert. Die letzte Operation, bei der wir den Wert erneut mit dem Abstand zwischen den Linien multiplizieren, wird benötigt, um die Division am Anfang der Gleichung zu neutralisieren. Dank dessen ist die Ănderung des Werts wieder dieselbe wie zu Beginn, und die zuvor berechnete Ănderung des Abstands ist immer noch korrekt.
float majorLineDistance = abs(frac(dist / _LineDistance + 0.5) - 0.5) * _LineDistance;
Nachdem wir den Abstand zu den Linien basierend auf dem Abstand zur Figur berechnet haben, können wir die Linien zeichnen. Wir machen einen reibungslosen Schritt von Liniendicke minus der HÀlfte der AbstandsÀnderung zu Liniendicke plus der HÀlfte der AbstandsÀnderung und verwenden den gerade berechneten Linienabstand als Vergleichswert. Nachdem wir diesen Wert berechnet haben, multiplizieren wir ihn mit der Farbe, um schwarze Linien zu erstellen (Sie können auch zu einer anderen Farbe wechseln, wenn Sie mehrfarbige Linien benötigen).
fixed4 frag(v2f i) : SV_TARGET{ float dist = scene(i.worldPos.xz); fixed4 col = lerp(_InsideColor, _OutsideColor, step(0, dist)); float distanceChange = fwidth(dist) * 0.5; float majorLineDistance = abs(frac(dist / _LineDistance + 0.5) - 0.5) * _LineDistance; float majorLines = smoothstep(_LineThickness - distanceChange, _LineThickness + distanceChange, majorLineDistance); return col * majorLines; }
Wir implementieren dĂŒnne Linien zwischen dicken Linien auf die gleiche Weise - wir fĂŒgen eine Eigenschaft hinzu, die bestimmt, wie viele dĂŒnne Linien zwischen dicken sein sollen, und dann machen wir das, was wir mit dicken Linien gemacht haben, aber aufgrund des Abstands zwischen dĂŒnnen Linien teilen wir den Abstand zwischen dicken Linien durch die Anzahl der dĂŒnnen Linien zwischen sie. Wir werden auch die Anzahl der dĂŒnnen Linien
IntRange
, dank dessen können wir nur ganzzahlige Werte zuweisen und keine dĂŒnnen Linien erhalten, die nicht
IntRange
dicken
IntRange
. Nachdem wir dĂŒnne Linien berechnet haben, multiplizieren wir sie mit der Farbe auf die gleiche Weise wie dicke.
fixed4 frag(v2f i) : SV_TARGET{ float dist = scene(i.worldPos.xz); fixed4 col = lerp(_InsideColor, _OutsideColor, step(0, dist)); float distanceChange = fwidth(dist) * 0.5; float majorLineDistance = abs(frac(dist / _LineDistance + 0.5) - 0.5) * _LineDistance; float majorLines = smoothstep(_LineThickness - distanceChange, _LineThickness + distanceChange, majorLineDistance); float distanceBetweenSubLines = _LineDistance / _SubLines; float subLineDistance = abs(frac(dist / distanceBetweenSubLines + 0.5) - 0.5) * distanceBetweenSubLines; float subLines = smoothstep(_SubLineThickness - distanceChange, _SubLineThickness + distanceChange, subLineDistance); return col * majorLines * subLines; }
Quellcode
2D SDF-Funktionen
#ifndef SDF_2D #define SDF_2D float2 rotate(float2 samplePosition, float rotation){ const float PI = 3.14159; float angle = rotation * PI * 2 * -1; float sine, cosine; sincos(angle, sine, cosine); return float2(cosine * samplePosition.x + sine * samplePosition.y, cosine * samplePosition.y - sine * samplePosition.x); } float2 translate(float2 samplePosition, float2 offset){
Kreis Beispiel
Shader "Tutorial/034_2D_SDF_Basics/Rectangle"{ SubShader{
Rechteck Beispiel
Shader "Tutorial/034_2D_SDF_Basics/Rectangle"{ SubShader{
Cutoff
Shader "Tutorial/034_2D_SDF_Basics/Cutoff"{ Properties{ _Color("Color", Color) = (1,1,1,1) } SubShader{ Tags{ "RenderType"="Transparent" "Queue"="Transparent"} Blend SrcAlpha OneMinusSrcAlpha ZWrite Off Pass{ CGPROGRAM #include "UnityCG.cginc" #include "2D_SDF.cginc" #pragma vertex vert #pragma fragment frag struct appdata{ float4 vertex : POSITION; }; struct v2f{ float4 position : SV_POSITION; float4 worldPos : TEXCOORD0; }; fixed3 _Color; v2f vert(appdata v){ v2f o;
Entfernungslinien
Shader "Tutorial/034_2D_SDF_Basics/DistanceLines"{ 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{
Ich hoffe, ich habe es geschafft, die Grundlagen von Distanzzeichenfeldern zu erklĂ€ren, und Sie warten bereits auf einige neue Tutorials, in denen ich ĂŒber andere Verwendungsmöglichkeiten sprechen werde.