Categories: Sonstiges

Wie Sie einen Einstieg in die Tessellierung finden.

Tessellierung ist die Vertex-Verarbeitungsphase in der OpenGL-Rendering-Pipeline, in der Patches von Vertex-Daten in kleinere Primitive unterteilt sind. Dieser Prozess wird duch zwei Shaderstufen und eine Stufe mit fester Funktion gesteuert.

Der Tessellierungsprozess ist in drei Phasen unterteilt, die einen optionalen Teil der Vertex-Verarbeitung in der Rendering-Pipeline bilden. Zwei der Stufen sind programmierbar, zwischen ihnen befindet sich eine feste Funktionsstufe. Sie werden im Folgenden in der Reihenfolge ihrer Verarbeitung beschrieben.

Im Allgemeinen beinhaltet der Prozess der Mosaikierung die Unterteilung eines Patches irgendeiner Art, dann die Berechnung neuer Node-Werte (Position, Farbe, Texturkoordinaten etc.) für jeden der durch diesen Prozess erzeugten Nodes. Jede Stufe der Tesselierungspipeline erfüllt einen Teil dieses Prozesses.

Der Tessellation Control Shader (TCS) bestimmt, wie viel tesselliert werden muss (dieser kann auch die tatsächlichen Patchdaten anpassen und zusätzliche Patchdaten in spätere Phasen einspeisen). Daher ist das TCS in erster Linie für die Sicherstellung der Kontinuität über Patches hinweg verantwortlich. Wenn Sie also zwei benachbarte Patches haben, die unterschiedliche Ebenen der Mosaikierung aufweisen müssen, müssen die TCS-Aufrufe für die verschiedenen Patches ihre Mosaikierungssteuerelemente verwenden, um sicherzustellen, dass die gemeinesamen Kanten zwischen den Patches den gleichen Grad der Mosaikierung verwenden. Ohne diesen Schutz können Lücken und Brüche in den angeblich zusammenhängenden Stellen entstehen.

Das TCS ist optional. Standard-Tessellierungswerte können verwendet werden, wenn kein TCS angegeben ist.

Der primitive Tessellierungsgenerator nimmt den Input-Patch und unterteilt ihn basierend auf Werten, die vom TCS berechnet oder als Standardwerte bereitgestellt werden.

Der Tesselllation Evaluation Shader (TES) nimmt den tessellierten Patch und berechnet die Nodewerte für jeden erzeugten Node.

Patches.

Tessellierungsstufen arbeiten mit Patches, einem primitiven Typ, der durch die Konstante GL_PATCHES bezeichnet wird. Ein Patch-Primitiv ist ein Allzweck-Primitiv, bei dem jeder n Node ein neues Patch-Primitiv ist. Die Anzahl der Nodes pro Patch kann auf Anwendungsgebiete angewendet werden, mit:

Copy to Clipboard

mit GL_PATCH_VERTICES als Ziel und einem Wert, der im halboffenen Bereich [1, GL_MAX_PATCH_VERTICES] liegt. Die maximale Anzahl der Patch-Eckpunkte ist implementierungsabhängig, wird aber nie kleiner als 32 sein.

Patch-Primitive sind immer eine Folge von einzelnen Patches. Es gibt keine „Patch Strip“ oder Ähnliches. Für einen gegebenen Vertex-Stream wird also jede Gruppe von Wertzahlen der Node ein separater Patch sein. Wenn Sie so etwas wie Dreieckstreifen machen müssen, sollten Sie Indexed Rendering verwenden, um ein ähnliches Verhalten zu erhalten, obwohl es die Anzahl der Nodes in der Indexliste nicht reduziert. Glücklicherweise sollte sich der Post-Transform-Cache mit allen Performance-Auswirkungen befassen, die mehr Indizes haben.

Tessellation Control Shader.

Der erste Schritt der Tessellierung ist der optionale Aufruf eines Tessellation Control Shader (TCS). Das TCS hat zwei Aufgaben:

  • Bestimmen Sie den Grad der Mosaikierung, den ein Primitiv haben sollte.
  • Führen Sie alle speziellen Transformationen an den Input-Patch-Daten durch.

Das TCS kann die Größe eines Patches ändern, indem es mehr Nodes pro Patch hinzufügt oder weniger bereitstellt. Ein TCS kann jedoch einen Patch nicht verwerfen (direkt, es kann dies indirekt tun), noch kann es mehrere Patches schreiben. Daher wird für jedes von der Anwendung bereitgestellte Patch ein Patch für die nächste Tessellierungsphase bereitgestellt.

Das TCS ist optional. Wenn in dem aktuellen Programm oder der aktuellen Programmpipeline kein TCS aktiv ist, werden die Patchdaten direkt von den Aufrufen des Vertex Shaders an den Schritt der primitiven Tessellierung übergeben. Die Höhe der in diesem Fall durchgeführten Tessellierung wird aus den im Kontext eingestellten Standardwerten übernommen. Diese werden durch die folgende Funktion definiert:

Copy to Clipboard

Wenn pname GL_PATCH_DEFAULT_OUTER_LEVEL ist, ist values ein 4-Element-Array von Floats, die die vier äußeren Tessellierungsebenen definieren. Wenn pname GL_PATCH_DEFAULT_INNER_LEVEL ist, ist values ein 2-Elemente-Array von Floats, die die beiden inneren Tessellierungsebenen definieren.

Diese Standardwerte entsprechen den TCS-Ausgabevariablen gl_TessLevelOuter and gl_TessLevelInner pro Patch.

Tessellation Primitive Generation.

Primitive Generation ist eine Stufe mit fester Funktion, die für die Erstellung einer Reihe neuer Primitive aus dem Eingabepatch verantwortlich ist. Diese Phase wird nur ausgeführt, wenn im aktuellen Programm oder in der aktuellen Programmpipeline ein Tessellation Evaluation Shader (TES) aktiv ist. Primitive Generation wird durch die folgenden Faktoren beeinflusst:

  • Die Tessellierungsstufen, die entweder durch das TCS oder die Standardwerte bereitgestellt werden, wie oben beschrieben.
  • Der Abstand der tessellierten Nodes, wie er durch die nachfolgende TS-Stufe definiert ist. Es kann der gleiche Abstand sein.
  • Der durch das nachfolgende TES definierte primitive Eingangstyp, der eines von Dreiecken, Vierecken oder Isolinien sein kann. Das TES kann auch die Generierung der Tessellierung als eine Reihe von Punkten anstelle von Dreiecken oder Linien erzwingen, indem es das point_mode Primitiv bereitstellt.
  • Die Primitive Generation Reihenfolge, die durch die nachfolgende TES definiert ist, die cw oder ccw sein kann. Dies, in Verbindung mit der Position der erzeugten Primitive, wird verwendet, um die Windungsordnung des Primitives zu bestimmen.

Abstrakter Patch.

Beachten Sie, dass die primitive Generierung nicht von den benutzerdefinierten Ausgaben des TCS (oder des Vertex-Shaders, wenn kein TCS aktiv ist), der Ausgabepatchgröße des TCS oder anderen TCS-Ausgaben beeinflusst wird. Der primitive Generierungsteil der Tessellierungsphase ist völlig blind für die tatsächlichen Nodekoordinaten und andere Patchdaten.

Der Zweck des primitiven Generation-Systems ist es, zu bestimmen, wie viele Nodes erzeugt werden sollen, in welcher Reihenfolge sie erzeugt werden sollen und welche Art von Primitiven daraus aufgebaut werden sollen. Die tatsächlichen Per-Vertex-Daten für diese Nodes, wie Position, Farbe usw., sind vom TES auf der Grundlage der vom primitiven Generator gelieferten Informationen zu generieren.

Aufgrund dieser Dichotomie arbeitet der primitive Generator mit einem, wie man meinen könnte, „abstrakten Patch“. Es betrachtet nicht die Patch-Ausgabe des TCS, sondern denkt nur in Bezug auf die Mosaikierung eines abstrakten Quad-, Dreiecks- oder „Isolinien“-Blocks.

Abhängig vom abstrakten Patch-Typ wertet der primitive Generator eine unterschiedliche Anzahl von Tessellierungsstufen aus und wendet verschiedene Tessellierungsalgorithmen an. Jeder erzeugte Node hat eine normalisierte Position (d.h. die Koordinaten liegen im Bereich [0, 1] innerhalb des abstrakten Patches. Diese Position besteht aus zwei oder drei Komponenten, je nach Art des Patchs. Die Koordinaten werden dem TES über die eingebaute vec3 gl_TessCoord Eingabe zur Verfügung gestellt.

Tessellation-Levels.

Die Menge der Tessellierung, die über den abstrakten Patch-Typ erfolgt, wird durch innere und äußere Tessellierungsebenen definiert. Diese werden, wie bereits erwähnt, entweder durch das TCS oder durch Kontextparameter bereitgestellt, die über den glPatchParameter angegeben sind. Sie sind ein 4-Vektor von Floats, die die „äußeren Tessellierungsebenen“ definieren, und ein 2-Vektor von Floats, die die „inneren Tessellierungsebenen“ definieren.

Die spezifische Interpretation hängt vom verwendeten abstrakten Patch-Typ ab, aber die allgemeine Idee ist folgende. In den meisten Fällen definiert jede Tessellierungsebene, in wie viele Segmente eine Kante getesselliert wird. Eine Tessellierungsebene von 4 bedeutet also, dass eine Kante zu 4 Kanten wird (4 Nodes zu 5). Die „äußeren“ Tessellierungsebenen definieren die Tessellierung für die Außenkanten des Primitives. Dies ermöglicht es, dass sich zwei oder mehr Patches richtig verbinden lassen, während sie gleichzeitig unterschiedliche Tessellierungsniveaus innerhalb des Patches aufweisen. Die inneren Tessellierungsebenen sind für die Anzahl der Tessellierungen innerhalb des abstrakten Patches.

Nicht alle abtrakten Patches verwenden die gleiche Anzahl von Werten in den Daten der äußeren/innersten Tessellierungsebenen. Beispielsweise verwenden Dreiecke nur eine innere Ebene und 3 äußere Ebenen. Der Rest wird ignoriert.

Der Patch kann verworfen werden, wenn ein äußerer Tessellierungslevel 0 oder weniger ist, aber nur für Tessellierungslevel, die der abstrakte Patch tatsächlich verwendet. Der Patch kann auch verworfen werden, wenn einer dieser Werte ein Gleitkomma-NaN ist. Ein Patch, der verworfen wird, wird nicht tesselliert, und es wird kein TES dafür aufgerufen. Es wird einfach vom System verschluckt, als ob es das nie wäre.

Dies ermöglicht es einem TCS, Patches effektiv zu entfernen, indem es 0 für eine relevante äußere Tessellierungsstufe übergibt.

Die so festgelegten Mosaikierungsstufen werden nicht direkt verwendet. Sie durchlaufen einen Klemmprozess, um die effektiven Mosaikierungsebenen zu erzeugen, die zur Mosaikierung des Primitives verwendet werden. Dieser Prozess hängt vom Abstandsparameter des TES ab.

In der folgenden Diskussion ist max die maximal zulässige Tessellierungsstufe, wie sie durch das GL_MAX_TESS_GEN_LEVEL definiert ist. Es muss mindestens 64 sein, damit Sie etwas Platz zum Spielen haben.

Der Abstand beeinflusst den effektiven Mosaikierungsgrad wie folgt:

Copy to Clipboard

Jede Mosaikierungsebene wird einzeln auf den geschlossenen Bereich eingespannt. Dann wird es auf die nächste Ganzzahl aufgerundet, um den effektiven Tessellierungsgrad zu erhalten.

Copy to Clipboard

Jede Mosaikierungsebene wird einzeln auf den geschlossenen Bereich eingespannt. Dann wird es auf die nächste gerade ganze Zahl aufgerundet, um den effektiven Mosaikierungsgrad zu erhalten.

Copy to Clipboard

Jede Mosaikierungsebene wird einzeln auf den geschlossenenen Bereich eingespannt. Dann wird es auf die nächste ungerade ganze Zahl aufgerundet, um den effektiven Tessellierungsgrad zu erhalten.

Kantenabstände bei der Mosaikierung.

An verschiedenen Stellen in der bevorstehenden Diskussion über die Tessellierung des abstrakten Patches wird es Aussagen geben, die besagen, dass eine Kante eines Primitives tesselliert werden soll. Das bedeutet, sie in eine Reihe von Segmenten nach einer bestimmten Mosaikierungsstufe zu unterteilen. Die Gesamtlänge dieser Segmente ist die Länge des ursprünglichen Segments. Die Länge der einzelnen Segmente zueinander hängt jedoch von dem im TES angegebenen Abstandsparameter und der Tessellierungsebene ab.

Wenn der Abstand auf equal_spacing gesetzt ist, dann haben alle tessellierten Segmente auf einer Kante die gleiche Länge. Da equal_spacing die Tessellierungsebenen auf die nächste Ganzzahl rundet, bedeutet dies, dass Kanten „pop“ werden, wenn die Tessellierungsebenen von einer Ganzzahl zur nächsten gehen.

Die beiden anderen Abstandsregelungen sollen ein reibungsloses Verhalten ermöglichen, wenn sich die Mosaikierungspegel ändern. Diese sind nützlich für alle Fälle, in denen die Höhe der Tessellierung auf der Fläche basiert, die von der Kamera aus betrachtet wird.

Wir müssen zuerst zwei Werte definieren:

  • n ist der oben berechnete effektive Mosaikierungsgrad für die betreffende Kante.
  • F ist der Wert, der vor dem obigen Rundungsschritt berechnet wurde. Es handelt sich also um einen potentiellen Bruchteilswert.

Wenn n gleich 1 ist, dann findet an dieser Kante keine Mosaikierung statt. Wenn n genau 2 ist, dann gibt es zwei Segmente gleicher Länge.

Andernfalls wird die Kante in zwei Sätze von Segmenten unterteilt. Ein Satz hat n – 2 Segmente, die alle die gleiche Länge haben. Der andere Satz wird 2 Segmente haben, die Längen gleich einander haben müssen, aber nicht unbedingt die Segmente in Satz eins. Die beiden Segmente werden kürzer sein als die anderen, also nennen wir diese Gruppe die „kurzen Segmente“.

Die Länge der kurzen Segmente im Verhältnis zu den anderen basiert auf (n – f). Wenn dieser Wert genau 0 ist, dann sind die kurzen Segmente gleich lang wie die anderen. Wenn sich dieser Wert 2 nähert, nähern sich die Längen der kurzen Segmente 0, 0.

Trotz des obigen Diagramms erfordert die Spezifikation nicht, dass sich die kurzen Segmente an einer bestimmten Stelle im Verhältnis zu den anderen befinden. Die Spezifikation enthält nur die folgenden Anforderungen:

  • dass die kurzen Segmente symmetrisch über die Mosaikkante gelegt werden.
  • Dass die Platzierung der kurzen Segmente invariant mit f ist, für jedes Segment, das tesselliert wird.

Beachten Sie, dass es keine Garantie dafür gibt, dass die Platzierung der kurzen Segmente nicht bei Änderungen von f springt. Das heißt, es gibt keine Garantie für die Glätte bei Änderungen von f.

Primitive Generation Order.

Beim Rendern ohne Mosaik ist die Reihenfolge der Primitive zueinander gut definiert. Das Rendern erfordert die Einrichtung eines Vertex-Streams. Dies definiert eine geordnete Abfolge von Eckpunkten. Das zum Zeitpunkt des Renderings angegebene Primitiv bestimmt genau, wie der Stream in Basis-Primitive zerlegt wird. Und so ist jedes Primitiv nach den Node geordnet, die es erzeugen.

Hinweis: Diese Reihenfolge bedeutet nicht, dass Sie den Image Load Store verwenden können, um aus dem Speicher zu lesen, der von einem Twin-Shader-Aufruf geschrieben wurde, nur weil dieser Aufruf von einem „vorherigen“ Primitiv stammt. Solche Lese-/Schreibvorgänge folgen den Regeln des inkohärenten Speicherzugriffs. Tessellierung macht die Dinge weniger klar. Während die Reihenfolge der Nodes innerhalb eines Primitivs gut definiert ist (bestimmt durcdh die Einstellungen des TES-Eingabelayouts cw oder ccw), ist die Reihenfolge der erzeugten Primitive relativ zueinander nicht definiert. Implementierungen definieren diese Reihenfolge, so dass Sie sich nicht auf eine bestimmte Reihenfolge verlassen können.

Andererseits basieren die meisten Möglichkeiten, die man überhaupt erkennen kann, auf inkohärenten Speicherzugriffen…

Tessellierende Primitive.

Dreiecke:

Der abstrakte Patch für die Dreiecksmodellierung ist natürlich ein Dreieck. Es werden nur die ersten drei äußeren Tessellierungsebenen und nur die erste innere Tessellierungsebene verwendet. Die äußeren Tessellierungsebenen gelten für die im Diagramm erstellten Kanten. Die genaue Funktionsweise der inneren Mosaikierungsebene gelten für die im Diagramm dargestellten Kanten. Die genaue Funktionsweise der inneren Mosaikierungsebene ist weniger intuitiv als es scheint.

Jeder Node, der erzeugt und an das TES gesendet wird, erhält als gl_TessCoord-Eingabe baryzentrische Koordinaten. Diese Koordinate definiert, wo sich dieser Node innerhalb des abstrakten Dreiecksfeldes befindet. Die baryzentrischen Koordinaten für die 3 Eckpunkte des abstrakten Dreiecksfeldes sind im Diagramm dargestellt. Alle anderen Nodes werden relativ zu diesen angegeben.

Um eine lineare Interpretation zwischen 3 Werten an jedem der drei Eckpunkte durchzuführen, sollten Sie Folgendes tun:

Copy to Clipboard

wobei value ein Array von (in diesem Fall vec3) Werten ist, die den drei im Diagramm dargestellten Nodes entsprechen.

Der Algorithmus für die Mosaikierung von Dreiecken basiert auf der Mosaikierung der Kanten des Dreiecks, aus denen dann Nodes und Dreiecke gebildet werden. Dies macht das Verhalten der inneren Mosaikierungsebene etwas unintuitiv, da sie nicht direkt der Anzahl der inneren Dreiecke entspricht.

Der erste Schritt entfernt den degeneriertesten Fall. Wenn alle effektiven äußeren Ebenen (wie oben berechnet) exakt 1,0 sind und die effektive innere ebenfalls 1,0 ist, dann wird nichts tesselliert, und die TES enthält 3 Nodes und ein Dreieck. Und so findet keiner der späteren Schritte statt.

Als nächstes, wenn die effektive innere Ebene 1,0 ist (und somit mindestens eine der äußeren Ebenen > 1,0 ist, sonst hätten wir die obige Bedingung erfüllt), dann wird die effektive innere Ebene neu berechnet, als ob der User 1,0 + e für die innere Ebene angegeben hätte, wobei e eine Zahl ist, die unendlich nahe Null liegt. 1,0 + e ist also ein Wert knapp über 1,0.

Praktisch bedeutet dies, dass, wenn eine Außenrandtessellierung stattfindet, die effektive Innen-Tessellierung je nach Abstand nicht weniger als 2 oder 3 beträgt. Dadurch wird sichergestellt, dass bei einer Mosaikierung immer mindestens ein Node innerhalb des Dreiecks vorhanden ist.

Der nächste Schritt in der Mosaikarbeit besteht darin, die Kanten des abstrakten Dreieckfeldes anhand der effektiven inneren Ebene zu unterteilen. Der Algorithmus beginnt damit, dass dieser die äußeren Tessellierungsstufen ignoriert. Er wendet die innere Tessellierung auf alle drei äußeren Kanten an:

Von hier aus bauen wir eine Reihe von konzentrischen „Ringen“ aus inneren Dreiecken. An jeder Ecke des äußeren Dreiecks werden die beiden benachbarten, unterteilten Nodes genommen. Aus diesen beiden wird ein Node berechnet, indem der Schnittpunkt zweier senkrechter Linien aus diesen Nodes ermittelt wird. Senkrechte Linien von den verbleibenden Eckpunkten zu den Kanten des inneren Dreiecks definieren, wo jede Kante des neuen Dreiecks unterteilt wird.

Dieser Vorgang wird wiederholt, indem mit dem neuen Ringdreieck der nächste Innenring erzeugt wird, bis eine von zwei Endbedingungen erfüllt ist. Wenn der Dreiecksring keine unterteilten Kanten (nur 3 Ecken) hat, dann stoppt der Prozess.

Wenn der Dreiecksring genau 2 unterteilte Kanten hat (nur 6 Eckpunkte insgesamt), dann ist der von ihm erzeugte „Ring“ überhaupt kein Dreieck. Ein ist ein einzelner Node.

Bei diesem Algorithmus ist die Anzahl der inneren Dreiecksringe die Hälfte der effektiven inneren Mosaikierungsstufe, abgerundet. Beachten Sie, dass, wenn die innere Ebene gerade ist, der innerste Ring ein einzelner Node ist.

Nach der Herstellung dieser Innenringe wird die Mosaikierung für das Hauptaußendreieck vollständig verworfen. Anschließend wird sie entsprechend den drei effektiven äußeren Tessellierungsebenen neu tesselliert.

Wenn alle Nodes generiert sind, müssen nun Dreiecke erzeugt werden. Die Spezifikation beschreibt einen bestimmten Algorithmus dafür, überlässt aber viele Details der Implementierung. Die Spezifikation garantiert:

  • Dass die Fläche des Dreickes, um (u, v, w)-Raum, vollständig durch die erzeugte Triangulation abgedeckt wird.
  • Dass kein Teil der Fläche des Dreiecks mehr als einmal von der erzeugten Dreiecksverkettung bedeckt wird.
  • Dass es Kanten zwischen den benachbarten Eckpunkten jedes „Rings“ von Dreiecken gibt. Das heißt, die dunklen Linien im obigen Diagramm werden durch die Angabe, dort zu sein, garantiert.
  • Wenn ein Node entsprechende Nodes auf dem Ring hat, der ihm unmittelbar vorausging, dann gibt es eine Kante zwischen diesem Node und seinen entsprechenden Nodes (Ecken von Ringdreiecken haben 2 entsprechende Nodes). Ebenso, wenn ein Node einen entsprechenden Node auf dem Ring in seinem Inneren hat, wird es eine Kante zwischen ihnen geben.

Die restlichen Kanten sind implementierungsdefiniert.

Quads.

Der abstrakte Patch enes Quad ist natürlich ein Quadrat. Alle 4 äußeren und 2 inneren Mosaikebenen werden verwendet. Die äußeren Tessellierungsebenen gelten für die vier im Diagramm erstellten Kanten. Die beiden inneren Tessellierungsebenen gelten für die im Diagramm dargestellten Kanten, aber auch für die gegenüberliegenden Kanten. Die innere Ebene 0 gilt also sowohl für die Unterkante als auch für die Oberkante. Wie das genau funktioniert, werden wir im Folgenden erläutern.

Jeder Node, der erzeugt und an das TES gesendet wird, erhält als gl_TessCoord-Eingabe eine normalisierte 2D-Koordinate, die die Position dieses Nodes innerhalb des abstrakten Patches darstellt. Die dritte Komponente von gl_TessCoord ist 0,0.

Dier Vierfach-Tessellierung funktioniert ähnlich wie die Dreieck-Tessellierung, nur mit 4 Seiten. Die Mosaikierung basiert auf der Unterteilung der Kanten und der Bildung von Nodes auf der Grundlage der unterteilten Kanten.

Der erste Schritt entfernt den degeneriertesten Fall. Wenn alle effektiven äußeren Ebenen genau 1,0 sind und die effektiven inneren Ebenen auch 1,0 sind, dann wird nichts tesselliert. Das TES erhält 2 Dreiecke und 4 Nodes (obwohl es bis zu 6 mal aufgerufen werden kann). Und so findet keiner der späteren Schritte statt.

Wenn nun eine effektive innere Ebene 1,0 ist (und somit mindestens eine der äußeren Ebenen > 1,0 ist, sonst hätten wir die obige Bedingung erfüllt), dann wird diese effektive innere Ebene neu berechnet, als ob der Benutzer 1,0 + e für die Ebene angegeben hätte, wobei e eine Zahl ist, die unendlich nahe Null liegt. 1,0 + e ist also ein Wert knapp über 1,0. Dies gilt für beide inneren Ebenen.

Dadurch wird sichergestellt, dass bei einer Mosaikierung entlang der Außenkante immer mindestens ein Node innerhalb des Quadrats vorhanden ist.

Der nächste Schritt in der Mosaikarbeit besteht darin, die Kanten des abstrakten Quad-Patches anhand der effektiven inneren Ebenen zu unterteilen. Der Algorithmus beginnt damit, dass er die äußeren Tessellierungsstufen ignoriert. Er wendet die innere Tessellierung auf alle vier äußeren Kanten an. Die innere Ebene 0 wird auf beide horizontalen Kanten angewendet, während die Ebene 1 auf die vertikalen Kanten angewendet wird.

Von hier aus bauen wir eine Reihe von konzentrischen „Ringen“ aus inneren Vierecken. An jeder Ecke des äußeren Quadrats werden die beiden benachbarten, unterteilten Nodes genommen. Aus diesen beiden wird ein Node berechnet, indem der Schnittpunkt zweier senkrechter Linien aus diesen Nodes ermittelt wird. Senkrechte Linien von den verbleibenden Eckpunkten zu den Kanten des inneren Quad definieren, wo jede Kante des neuen Quadrats unterteilt wird.

Dieser Vorgang wird wiederholt, indem mit dem neuen Ringviereck der nächste Innenring erzeugt wird, bis eine von zwei Endbedingungen erfüllt ist. Wenn einer oder beide der Innenringe nur ein Segment aufweisen, dann ist dieser Ring der letzte Ring.

Wenn einer oder beide der Innenringe genau zwei Segmente haben, dann ist der letzte Ring degeneriert. Das heißt, anstatt ein Quad zu sein, ist es entweder eine Linie (mit den Nodes von der langen Seite, um sie zu unterteilen) oder ein Punkt (wenn beide inneren Ebenen identisch sind).

Nach der Herstellung dieser Innenringe wird die Unterteilung für das Außenviereck vollständig aufgehoben. Jede Außenkante wird dann entsprechend den vier effektiven äußeren Mosaikstufen neu unterteilt.

Nachdem alle Nodes erzeugt wurden, muss nun die Fläche des Quad in Dreiecke zerlegt werden. Die Spezifikation beschreibt einen bestimmten Algorithmus dafür, überlässt aber viele Details der Implementierung. Die Spezifikation garantiert:

  • Dass die Fläche des Quad, im (u, v)-Raum, vollständig von der erzeugten Triangulation abgedeckt wird.
  • Dass kein Teil der Fläche des Quad mehr als einmal durch die erzeugte Triangulation abgedeckt wird.
  • Dass es Kanten zwischen den benachbarten Eckpunkten jedes „Rings“ von Quads gibt, d.h., die dunklen Linien im obigen Diagramm werden durch die Angabe, dort zu sein, garantiert.
  • Wenn ein Node entsprechende Nodes auf dem Ring hat, der ihm unmittelbar vorausging, dann gibt es eine Kante zwischen diesem Node und seinen entsprechenden Nodes (Ecken von Ringquads haben 2 entsprechende Nodes).

Ebenso, wenn ein Node einen entsprechenden Node auf dem Ring in seinem Inneren hat, wird es eine Kante zwischen ihnen geben. Die restlichen Kanten sind implementierungsdefiniert.

Isolinien.

Der Raum des abstrakten Patches der Isolinien ist ein Quadrat, aber der abstrakte Patch selbst ist eine Reihe von horizontalen Linien. Es werden nur die ersten beiden äußeren Tessellierungsebenen verwendet. Die inneren Tessellierungsebenen werden ignoriert. Die beiden äußeren Tessellierungsebenen gelten für die Kanten.

Jeder Node, der erzeugt und an das TES gesendet wird, erhält als gl_TessCoord-Eingabe eine normierte 2D-Koordinate. Gl_TessCoord.x repräsentiert den Abstand entlang einer der Linien, während gl_TessCoord.y angibt, für welche Linie der Nodes bestimmt ist. Die dritte Komponente von gl_TessCoord ist 0.0.

Der Isolinien-Patch wird wie folgt tesselliert. Die effektive Tessellierungsstufe für die äußere Ebene 0 wird immer so berechnet, als ob Sie equal_spacing angegeben hätten. Die vertikalen Kanten des Quadrats werden darauf aufbauend tesselliert. Jeder Node, mit Ausnahme des oberen, stellt eine einzelne Linie dar.

Die zweite effektive Tessellierungsebene gibt an, in wie viele Segmente die Linien unterteilt sind. Es verwendet die Standardregeln für den Abstand, um festzulegen, wie viele Segmente die Linien alle erhalten.

Wenn Sie eine einzelne Isolinie erzeugen wollen, sollten Sie 1 für gl_TessLevelOuter[0] übergeben. Wenn Sie jedoch mehr tesselierte Nodes benötigen, als in einem einzigen Wert der äußeren Ebene möglich ist, ist das TES perfekt in der Lage, mehrere einzelne Linien zusammenzufügen. Der Tessellator garantiert, dass der erste und letzte Punkt für jede Linie genau 0,0 und 1,0 ist.

Denken Sie daran: Der abstrakte Patch ist abstrakt, das TES kann die Nodes beliebig positionieren.

Tessellation Evaluation Shader.

Der Tessellation Evaluation Shader (TES) ist dafür verantwortlich, die vom primitiven Generator erzeugten abstrakten Koordinaten zusammen mit den Ausgaben aus dem TCS (oder Vertex Shader, wenn kein TCS verwendet wird) zu übernehmen und daraus die Ist-Werte für die Nodes zu berechnen. Hier kodieren Sie den Algorithmus, mit dem Sie tatsächlich die neuen Positionen/Normal/Texcoords etc. berechnen. Die TES ist ein obligatorischer Bestandteil der Tessellierung. Wenn keine vorhanden ist, erfolgt keine Tessellierung.

Die TES ist eher wie ein Vertex-Shader, da jeder Aufruf auf einen bestimmten Node innerhalb des tessellierten Patches wirkt (obwohl das System, wie bei einem Vertex-Shader, das TES mehr als einmal für den gleichen Node aufrufen kann, also sollte es deterministisch sein). Außerdem kann das TES keine Nodes auslesen.

Patch-Schnittstelle und Kontinuität.

Das Tessellieren von Patches, so dass es keine Lücken zwischen den Primitiven gibt, die durch benachbarte Patches erzeugt werden, ist sehr wichtig. Es ist auch nicht etwas, das einfach passiert. Da OpenGL blind dafür ist, wie die eigentliche Tessellierung funktioniert (der primitive Generator beschäftigt sich nur mit dem abstrakten Patch), gibt es spezifische Dinge, die Sie tun müssen, um eine lückenlose Tessellierung zwischen den Patches zu gewährleisten.

Regel 1: Die TES-Implementierung der Interpolation für die Nodes bezüglich der Patch-Kanten muss binär-identische Eingabewerte für diese Kanten empfangen. Das TES definiert, was die verschiedenen Patch-Daten tatsächlich bedeuten, da es die Interpolation implementiert. Daher muss Ihr TCS oder die Patchdaten pro Patch im Allgemeinen sicherstellen, dass die Daten, die das TES zur Generierung dieser Kantenpositionen verwendet, für die gemeinsame Kante auf den beiden (oder mehreren) Patches identisch sind.

Glücklicherweise bedeuten die Invarianzregeln von OpenGL, dass Shader-Berechnungen, die genau die gleiche Übereinstimmung ausführen, genau die gleiche Ergebnisse liefern. Normalerweise bedeutet dies also nur, dass die Eingänge zum TCS, die zur Interpolation entlang der Kanten beitragen, die gleichen Werte aus den Vertex-Arrays erhalten, wie sie durch den Vertex-Shader verarbeitet werden. Verschiedene Methoden der Mosaikierung und Interpolation, wie sie von Ihrem TES implementiert werden, können jedoch spezifische Anforderungen haben.

Regel 2: Die äußere Tessellierungsebene für die gemeinsamen Kanten muss binär-identisch sein. Während man einfach sicherstellen könnte, dass der „effektive Tessellierungspegel“ der Kanten identisch ist, besagt die Spezifikation, dass die Tessellierungsinvarianz nur dann gewährleistet ist, wenn die gegebenen Pegel identisch sind. Daher ist es am besten, sicher zu sein, indem man sicherstellt, dass die beiden Kanten, die miteinander verbunden werden sollen, genau die gleiche Tessellierungsebene erhalten.

Die beiden obigen Regeln stellen sicher, dass das TES binäre identische Werte für die Patchdaten zur Berechnung von gemeinsamen Kantenvertices sowie exakt übereinstimmende gl_TessCoord-Werte für diese Nodes entlang der gemeinsamen Kante erhält. Daher ist die einzige andere Regel, die befolgt werden muss:

Regel 3: Das TES muss Kantenvertices berechnen, die nur auf den binären identischen Daten basieren und die exakt gleiche Mathematik zwischen den Shader-Aufrufen entlang der gemeinsamen Kanten verwenden. OpenGLs Standard-Shader-Invarianzregeln funktionieren, solange der gleiche Codepfad für beide Aufrufe verwendet wird.

Hinweis: Wenn Sie zwei verschiedene Programme verwenden (z.B. zwei verschiedene Rendering-Aufrufe erzeugen Patches, die Kanten teilen), dann müssen Sie sicherstellen, dass die relevanten Shader-Stufen auch untereinander invariant sind. Daher müssen Sie bei Bedarf das invariante Keyword verwenden.

Trotz der scheinbaren Komplexität dieser Regeln sind sie wirklich nicht schwer zu befolgen. Regel 1 kann bei der Verwendung ungewöhnlicher Mosaikierungsalgorithmen schwer zu erfüllen sein, aber für Bezier- oder NURBS-Oberflächen, solange die Kontrollpunkte in Übereinstimmung mit diesen Oberflächenregeln kontinuierlich sind, werden diese erfüllt.

Die am schwierigsten zu erfüllende Regel 2 wäre, da sie die Berechnungen für die äußeren Tessellierungsebenen verkompliziert. Es kann nicht einfach auf der Entfernung zum gesamten Patch basieren: jeder Patch muss wissen, was die benachbarten Patches verwendet haben, damit sie alle übereinstimmen können. Im Wesentlichen müssten Sie die äußeren Tessellierungspegel für einen Patch berechnen, basierend auf dem Abstand für jede Kante zur Kamera. Oder ein ähnlicher Algorithmus, der über Patches hinweg invariant wäre.

Regel 3 verlangt einfach, dass Ihr Shader vernünftig ist, dass dieser keine bedingte Logik enthält, die bewirkt, dass verschiedene Patches mit unterschiedlicher Mathematik erzeugt werden. Wenn Sie jedoch Kontinuität zwischen einzelnen Programmen benötigen, wird das etwas schwieriger.

Vielen Dank für Ihren Besuch.

3DMaster