Sutherland-Hodgman-Clipping für Physik-Engines

Ausschnitt ist der Prozess der Bestimmung, welcher Raumbereich sich in einem anderen Raumbereich befindet. Dies ist besonders wichtig in Studienbereichen wie Computergrafik und Physiksimulation. Wenn Sie zum Beispiel eine Welt auf den Bildschirm rendern, ist es am besten zu wissen, was auf dem Bildschirm angezeigt wird, bevor Sie Daten verarbeiten. Dadurch können viele überflüssige Daten ignoriert werden, während nur das verarbeitet und dargestellt wird, was der Benutzer sieht.

In der physikalischen Simulation werden häufig starre Körper gefunden interpenetrierend - das heißt, zwei separate Objekte überlappen sich. Das ist schlecht. Interpenetration ist etwas, das in der realen Welt niemals vorkommt, aber ein Problem, das bei der Kollisionserkennung behandelt werden muss. Oft wird eine Form gegen eine andere gekürzt, um festzustellen, welche Merkmale sich tatsächlich berühren. Von hier aus kann eine Kollisionsreaktion ausgelöst werden.

Fortgeschrittene Funktionen von Game-Engines können mit einer Art Clipping implementiert werden. Auftriebssimulation, Kameraansichtsebenen; Sprödbruch, Kontakterzeugung mit dem Seperating Axis Test; All diese Dinge (und vieles mehr) können mit Clipping-Algorithmen erreicht werden. In der Tat war ein solcher Ausschnitt in meinem vorherigen Tutorial zum Aufbau einer Physik-Engine für starre Körper notwendig.


Voraussetzungen

Dieser Artikel setzt einige voraus, die Sie gut verstehen:

  • Grundlegende lineare Algebra
  • Einfache 3D-Mathematik

Die oben genannten Forschungsbereiche können aus einer Vielzahl von Ressourcen im Internet (z. B. Wolfires Leitfaden zur linearen Algebra oder Khan Academy) erlernt werden - und sie sind nicht zu schwer zu lernen!


Ebenen teilen

Bei vielen komplexen Abschneideroutinen wird eine einzige Art von Vorgang verwendet: das Teilen einer Form mit einer einzigen Ebene. Wenn Sie eine Form durch eine Ebene teilen, nehmen Sie eine Form und schneiden durch eine feste Ebene in zwei Teile.

Es ist wichtig zu wissen, dass das Teilen einer Form mit einer Ebene ein grundlegendes Werkzeug in dieser Clipping-Routine ist.

Sehen Sie sich Davide Cervones hervorragende Animationen an, um dies deutlich zu demonstrieren:


Von Davide P. Cervone. Mit Erlaubnis verwendet.

Sutherland Hodgman-Ausschnitt

Sutherland Hodgman-Clipping ist meine Lieblings-Clipping-Routine, da sie für Clipping-Line-Loops gegen Flugzeuge geeignet ist. Mit diesem Algorithmus kann unter Berücksichtigung einer Liste von Eingabescheitelpunkten und einer Liste von Ebenen eine Ausgabe neuer Scheitelpunkte berechnet werden, die rein innerhalb der Menge von Ebenen existieren.

Die Termebene kann verwendet werden, um sowohl auf eine Ebene in 3D als auch auf 2D zu verweisen. Eine 2D-Ebene kann auch als a bezeichnet werden Linie.

Wir können uns die Liste der Scheitelpunkte als einzelnes Polygon vorstellen. Wir können uns die Liste der Ebenen als eine einzige Linie vorstellen. Die Visualisierung in zwei Dimensionen könnte wie folgt aussehen:

Beim Sutherland Hodgman-Clipping ist das Polygon auf der rechten Seite die gewünschte Form, während das rote Polygon auf der linken Seite die Eingabeform ist und noch abgeschnitten werden muss. Die blaue Linie repräsentiert eine Aufteilungsebene in zwei Dimensionen. Es können beliebig viele Teilungsebenen verwendet werden. Im obigen Beispiel wird nur eine einzelne Aufteilungsebene verwendet. Wenn viele Teilungsebenen verwendet werden, erfolgt das Beschneiden zu einer bestimmten Zeit in einer Ebene, wodurch immer mehr Originalform weggeschnitten wird, um eine neue auszugeben:

Seiten eines Flugzeugs

Lassen Sie uns der Einfachheit halber in zwei Dimensionen arbeiten. Wenn Sie ein Polygon gegen eine Ebene teilen, ist es wichtig zu wissen, auf welcher Seite der Ebene sich ein bestimmter Punkt befindet.


Oben ist der am häufigsten verwendete Weg, um die Entfernung von einem Punkt zu einer Ebene zu ermitteln. Beachten Sie, dass das Punktsymbol in der obigen Gleichung das Punktprodukt darstellt.

Die Entfernung muss nicht explizit berechnet werden. der normale Vektor n muss nicht normalisiert werden (dh es muss nicht genau eine Einheit lang sein). Es muss lediglich das Vorzeichen des Entfernungsergebnisses geprüft werden d. Ob n ist dann nicht normalisiert d wird in Einheiten der Länge von sein n. Ob n ist dann normalisiert d wird in Einheiten sein, die den Weltraumeinheiten entsprechen. Dies ist wichtig zu erkennen, da die Berechnung durchgeführt werden kann, um zu sehen, ob d ist positiv oder negativ. Positiv d bezeichnet den Punkt p ist auf der Vorderseite des Flugzeugs. Negativ bedeutet, dass es auf der Rückseite ist.

Was genau meinen wir mit der Vorder- und Rückseite eines Flugzeugs? Nun, es hängt wirklich davon ab, was Sie als Vorder- und Rückseite definieren. Normalerweise bedeutet "vorne", dass es sich auf der gleichen Seite der Ebene befindet wie die Normalebene.

Der Algorithmus

Lasst uns direkt in den Algorithmus eintauchen. Machen Sie einen schnellen Scan über den Pseudocode:

 Polygon SutherlandHodgman (const Polygon StartPolygon, Plane [] clippingPlanes) Polygonausgabe = StartPolygon für jede EbenenclippingPlane in ClippingPlanes input = Ausgabe output.Clear () Vec2 startingPoint = input.Last () für jeden Vec2 endPoint in input, wenn startPoint und endPoint in eingegeben werden vor clippingPlane out.push (endPoint) oder else, wenn StartingPoint vor und endPoint hinter clippingPlane out.push (Kreuzung (clippingPlane, startingPoint, endPoint)) andernfalls wenn startingPoint und endPoint hinter clippingPlane out.push (Kreuzung (clippingPlane, startingPoint, endPoint) ) out.push (endPoint) endPoint = Startausgabe von returnPoint

Der Algorithmus nimmt ein Eingabepolygon und einige Schnittebenen und gibt ein neues Polygon aus. Die Idee ist, jedes Liniensegment des Eingabepolygons jeweils gegen eine Beschneidungsebene zu schneiden. Dieses Bild von Wikimedia Commons zeigt dies sehr gut:


Sutherland-Hodgman-Ausschnittsalgorithmus, von Wojciech Mula.

Für jede Ausschnittsoperation gibt es nur wenige Fälle, in denen Scheitelpunkte ausgegeben werden können, und kann wie folgt umrissen werden:

 // InFront = plane.Distance (Punkt)> 0.0f // Hinter = plane.Distance (Punkt) < 0.0f Vec2 p1, p2; ClipPlane plane; case p1 InFront and p2 InFront push p2 case p1 InFront and p2 Behind push intersection case p1 Behind and p2 InFront push intersection push p2

Der beste Weg, diesen Algorithmus zu verstehen, besteht darin, eine 2D-Form auf ein Blatt Papier zu zeichnen und eine gerade Linie durch die Form zu zeichnen. Führen Sie dann eine Schleife entlang der Scheitelpunkte des Polygons durch, führen Sie den Sutherland-Hodgman-Algorithmus durch und zeichnen Sie die Ergebnisse. Dadurch wird eine Intuition darüber aufgebaut, wie der Algorithmus gerade nacheinander entlang jeder Linie läuft, und stellt dabei sicher, dass alle Scheitelpunkte hinter der Ebene bleiben.

Probieren Sie nach Ihrem eigenen Bleistift- und Papierlauf diese großartige Webseite aus. Es gibt einige großartige Visuals und ein Java-Applet (oben auf der Seite), mit dem der Benutzer Teile des tatsächlich ausgeführten Algorithmus sehen kann!

Schnittberechnung

Die Berechnung des Schnittpunkts einer Ebene bei zwei Punkten ist sehr einfach. Die Idee ist, die Entfernungsberechnung zu verwenden, um die Entfernung jedes Punktes zu einer Ebene zu ermitteln. Bei diesen Abständen kann dann ein Schnittpunkt berechnet werden, indem die lineare Interpolation verwendet wird.

Spitze: Die lineare Interpolation ist ein äußerst nützliches Konzept, das in jeder Grafiksoftware und in der physikalischen Simulationssoftware Anwendung findet. Lineare Interpolation kann umgangssprachlich als bezeichnet werden lerp. Alles kann linear von einer Ausgangsposition bis zur Endposition interpoliert werden, solange die Bewegungsgleichung ist linear.

In der Regel die Formel zur linearen Interpolation aus Start zu Ende mit Kreuzung Alpha ist:

 Lerp (Anfang, Ende, Alpha) Rückkehr Anfang * (1 - Alpha) + Ende * Alpha
Spitze: Im obigen Beispiel, Alpha ist das, was man nennt Interpolant. In den linearen Interpolationsberechnungen werden Interpolanten verwendet, um Positionen zwischen Start- und Endpunkt zu berechnen.

Wenn man das weiß, können die Abstände von der Ebene zu jedem Punkt als Werte verwendet werden, von denen eine berechnet werden soll Alpha Interpolant. Die Variablen t1 und t2 kann die Abstände zur Ebene von darstellen p1 und p2 beziehungsweise.

In dieser Hinsicht ist der Schnittpunkt einfach a Lerp vom Startpunkt bis zum Endpunkt bei gegebener Schnittzeit.

Erweiterung auf 3D

Dieser Algorithmus kann leicht in den dreidimensionalen Raum erweitert und dort ausgeführt werden. (Dieser Algorithmus kann in jeder höheren Dimension ausgeführt werden, was auch immer das bedeutet.) In praktischen Anwendungen ist dieser Algorithmus jedoch normalerweise nie tatsächlich in 3D durchgeführt. Durch die Verwendung intelligenter inverser Transformationen kann das Problem des Abschneidens eines 3D-Polyeders gegen eine Ebene (in bestimmten Szenarien) zu einer 2D-Polygon-zu-Polygon-Abschneideroutine vereinfacht werden. Dies ermöglicht eine signifikante rechnerische Optimierung.

Wenn Sie den Bullet Physics-Quellcode studieren, wird in ähnlicher Weise festgestellt, dass das Beschneiden tatsächlich in einem eindimensionalen Raum erfolgt, obwohl tatsächlich ein vollständiges 3D-Beschneiden ausgeführt wird. Dies liegt an der Möglichkeit, ein gültiges Interpolationsmittel zu berechnen, wenn nur eine einzige Dimension des Problemraums verwendet wird.

Zum Beispiel, wenn man die Kreuzungszeit von kennt x Werte eines einfachen Problems, kann diese Kreuzungszeit als Interpolant verwendet werden, um eine Lerp auf einem dreidimensionalen Punkt.

Schauen wir uns das etwas genauer an. Schauen Sie sich das folgende Diagramm an:

Angenommen, die gelbe Linie ist der Boden an einer y-Position von 0. Nehmen Sie nun an, die Startposition ist auf 10, und die Endposition y ist bei -1. Wir berechnen einen gültigen Interpolanten- und Schnittpunkt entlang der y-Koordinate:

 // alpha = 0,9 / 10,0f Gleitkomma alpha = 0,9f // finalY = 0,0f Gleitkomma finalY = Lerp (y1, y2, alpha);

Das obige kann als "90% des Weges von 10 nach -1" gelesen werden, was Null sein würde. Diese Interpolante kann auf beliebige Datentypen angewendet werden, einschließlich eines zweidimensionalen Vektors:

 // alpha = 9.0f / 10.0f Float alpha = 0.9f Vec2 finalPosition = Lerp (p1, p2, alpha);

Der obige Code würde tatsächlich die richtige x-Koordinate für die Zeit des Aufpralls berechnen, ohne sogar Operationen mit der x-Koordinate auszuführen, um die Interpolante zu bestimmen. Diese Idee, Interpolanten zu berechnen und auf höhere Dimensionen anzuwenden, als sie berechnet wurden, ist ein guter Weg, um Code zu optimieren.

Numerische Robustheit

Es gibt einige Probleme, die bei einer naiven Implementierung dieser Abschneidroutine bestehen bleiben können. Bei jeder Berechnung des Schnittpunktes schleicht sich der numerische Fehler in die Berechnung ein. Dies stellt ein Hauptproblem dar, wenn ein Polygon mit einer Ebene geteilt wird, während die Ausgabe erzeugt wird beide Seiten des Flugzeugs. Um eine Ausgabe für beide Seiten einer Teilungsebene zu erzeugen, muss der ursprüngliche Sutherland-Hodgman-Algorithmus geringfügig modifiziert werden. Hier treten Probleme auf.

Wenn ein Schnittpunkt berechnet wird, schleicht sich der numerische Fehler ein. Dies ist ein Problem, da der Schnittpunkt aus dem Punkt berechnet wird EIN darauf hinweisen B wird etwas anders sein als die Berechnung vom Punkt B darauf hinweisen EIN. Um solche Probleme zu vermeiden, muss die Schnittmenge konsistent berechnet werden. Dies vermeidet ein schreckliches T-Kreuzung Problem. Es ist in Ordnung, wenn es einen numerischen Fehler gibt, solange der Fehler konsistent ist.

T-Junction Ausgabe: Eine Lücke zwischen zwei Maschen, die bewirkt, dass durch die Dreieck-Rasterung eine sichtbare Lücke zwischen drei Dreiecken entsteht. In der Regel durch schlechte Handhabung von Epsilon während der Gleitkommaberechnung verursacht. Mögliche Lösungen: konsistente Vertex-Transformationen; Scheitelpunktschweißnachbearbeitung.

Ein anderes Problem besteht darin, zu bestimmen, ob sich ein Punkt auf einer Seite einer oder einer anderen Ebene befindet. Aus einer ganzen Reihe von Gründen sollten dicke Flugzeuge geprüft werden. Die Idee ist, eine Ebene eher als eine dicke Ebene zu behandeln, als eine Ebene mit unendlich geringer Breite. Dies ermöglicht einen zusätzlichen Fall innerhalb der Sutherland-Hodgman-Routine: die im Flugzeug Fall.

 Float D = Ebene Abstand (Punkt); // EPSILON ist eine numerisch signifikante und visuell unbedeutende Zahl. Vielleicht 0.00001f. bool OnPlane (Float D) return D> -EPSILON und D < EPSILON; 

Wenn auf der Beschneideebene ein Punkt gefunden wird, drücken Sie einfach den Endpunkt. Dadurch werden insgesamt 3 Fälle gezählt. Weitere Informationen zu EPSILON, siehe hier.


Referenzen und Quellcode

Die beste Referenz für diesen Abschneidealgorithmus finden Sie im Christer Ericson-Buch zur Erkennung von Kollisionen in Echtzeit (alias Orange Book). Sie finden dieses Buch in so ziemlich jedem Spielstudio, das es gibt.

Neben der Beschaffung von viel Geld für ein Buch gibt es ein paar kostenlose Ressourcen:

  • Leicht modifizierte Version von Sutherland-Hodgman für 2D-Clipping in einer Physik-Engine
  • Rosetta-Codebeispiele (nicht der beste Code, aber eine gute Referenz)
  • Visualisierung des Algorithmus und des Pseudocodes

Fazit

Sutherland-Hodgman-Clipping ist eine großartige Möglichkeit, Clipping-Effekte sowohl im 2D- als auch im 3D-Raum durchzuführen. Diese Art von Routine kann angewendet werden, um viele verschiedene Probleme zu lösen, von denen einige Probleme in eher fortgeschrittenen Untersuchungsbereichen anwendbar sind. Wie immer können Sie gerne Fragen in den Kommentaren unten stellen!