Universität Koblenz–Landau Fachbereich Informatik Triangle Mesh Compression Dominik Breuer Matrikelnummer 9720116 Seminar Computergraphik betreut von Prof. Dr.-Ing. H. Giesen Wintersemester 2000/2001 Vortrag vom 11. April 2001 Inhaltsverzeichnis 1 Einleitung 1.1 3D-Darstellungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2 VRML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3 3 2 Kompressionsverfahren 4 3 ,,Triangle Mesh Compression” 3.1 Connectivity Encoding . . . . . . . . . 3.1.1 Flussdiagramm Encoding . . . 3.1.2 Beispiel Encoding . . . . . . . 3.1.3 Flussdiagramm Decoding . . . 3.1.4 Beispiel Decoding . . . . . . . 3.1.5 Komprimierung der Befehlsliste 3.2 Vertex Coordinate Compression . . . . 4 Ergebnisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 5 8 9 10 11 12 12 14 2 1 Einleitung Mit dem Beginn der großen Verbreitung des Internets hat auch die Menge an multimedialen Inhalten kontinuierlich zugenommen. Anfang der 90er Jahre bestand ein Großteil der Internetseiten aus einfachen Textdarstellungen mit vereinzelten Bildern. Mit der Zeit stieg der Anteil der Bilder immer weiter an. Hinzu kamen immer häu£ger auch animierte Bilder, Soundeffekte und Scriptsprachen. Durch diese Entwicklung steigt auch der Bedarf an größerer Bandbreite zur Übertragung der Inhalte stark. Aufgrund der begrenzten Bandbreite von herkömmlichen Datenleitungen wäre ein Transport der Daten in unkomprimierter Form nur schwer möglich. Daher verwenden die im Internet verwendeten Dateiformate für Bild- und Toninformationen Komprimierungsverfahren zur Reduzierung der Datenmenge. 1.1 3D-Darstellungen Die bisher angesprochenen Bildformate dienen alle der Darstellung von 2D-Bildern. Immer häu£ger werden jedoch auch 3D-Darstellungen verwendet. Diese k önnen jedoch nicht mehr mit herkömmlichen Kompressionsverfahren komprimiert werden, da es sich bei den Bilddaten nicht mehr um Bildpunkte, sondern um Gittermodelle handelt. Diese Gittermodelle bestehen aus einzelnen Dreiecken, aus denen die 3D Struktur aufgebaut wird. An zu speichernden Daten fallen so die Koordinaten der Dreieckspunkte, sowie die verbindenden Kanten zwischen den Punkten an. Ein im Internet verwendetes Format zur Übertragung dieser Koordinaten- und Kantenlisten ist VRML. 1.2 VRML VRML ist die Abkürzung für ,,Virtual Reality Modeling Language” und dient der Beschreibung von 3D Welten. Das Format unterstützt die vollständige Beschreibung von dreidimensionalen Szenarien mit gerenderten Objekten, Lichtquellen, Ober¤ächentexturen und realistischen Effekten. Gespeichert werden die Daten in der Version VRML97 als ASCII Zeichen. Die Koordinaten- und Kantenlisten werden also in unkomprimierter Form gespeichert. Daraus resultieren, mit zunehmender Komplexität der 3D Modelle, sehr große Dateien. Einzige Möglichkeit zur Reduzierung ist das ,,Packen” in Formaten wie ,,Zip”. Doch selbst in dieser Form benötigt ein relativ einfach aufgebautes Modell, wie z.B. ein Triceratops mit 2832 Knoten, noch ein Größe von 44 KByte. Aufgrund dieser Problematik sollen in folgenden VRML Versionen die Daten nicht mehr im ASCII, sondern im Binär Format gespeichert werden. Ermöglicht wird dies durch die Verwendung von Kompressionsalgorithmen für die verwendeten Gittermodelle. 3 2 Kompressionsverfahren Wie bei anderen Kompressionsverfahren, z.B. bei Bild-, Audio- und Videokompression, kann bei der Kompression von Gittermodellen zwischen verlustbehafteten und verlustfreien Verfahren unterschieden werden. Bei den verlustbehafteten ,,Mesh Reduction” Verfahren handelt es sich allerdings nicht um Kompressionsverfahren im eigentlichen Sinne. Die Datenreduktion wird hier durch eine Reduzierung der Anzahl an Knoten und Kanten erreicht. Allerdings stellt dieser Vorgang noch keine Komprimierung dar. Daher kann ,,Mesh Reduction” als eine Mögliche Vorstufe zur verlustfreien Komprimierung angesehen werden. Die verlustfreien Verfahren sind sich in ihren wesentlichen Bestandteilen sehr ähnlich. Der Komprimierungsvorgang wird meist in zwei Teilbereiche gegliedert: • Connectivity Encoding Hierbei werden die Kanten der Dreiecke in einer bestimmten, Verfahren abhängigen, Form kodiert. Dabei wird erfasst, wie die Dreieckspunkte durch die einzelnen Kanten miteinander verbunden sind. • Vertex Coordinate Compression Der zweite Schritt dient der Komprimierung der Koordinaten der Dreieckspunkte. Diese sind erforderlich um mit Hilfe der Kanteninformationen das ursprüngliche Gittermodell zu rekonstruieren. Durch die Verbindung dieser beiden Komprimierungsschritte werden alle notwendigen Daten der Gittermodelle erfasst. Beim Dekomprimieren kann so die vollständige Struktur, ohne Verluste, wiederhergestellt werden. In den letzten Jahren wurden viele solcher Verfahren zur ,,Mesh Compression” vorgestellt. Darunter waren unter anderem die folgenden Verfahren: • Geometric Compression Through Topological Surgery [1] (1998, Gabriel Taubin - Jarek Rossignac) • Triangle Mesh Compression [2] (1998, Costa Touma - Craig Gotsman) • Real Time Compression of triangle mesh connectivity [3] (1998, Stefan Gumhold) • Edgebreaker: Compressing the incidence graph of triangle meshes [4] (1999, Jarek Rossignac) Im nächsten Abschnitt wird mit ,,Triangle Mesh Compression” eines der Verfahren näher erläutert. 4 3 ,,Triangle Mesh Compression” Das Komprimierungsverfahren ,,Triangle Mesh Compression” von Costa Touma und Craig Gotsman wurde auf der ,,Graphics Interface ’98” zum ersten mal vorgestellt. Bei diesem Verfahren handelt es sich, wie bereits zuvor beschrieben, um eine verlustfreie Komprimierung von 3D Gittermodellen. Der eigentliche Komprimierungsvorgang gliedert sich dabei in zwei Schritte: Connectivity Encoding und Vertex Coordinate Compression. 3.1 Connectivity Encoding Beim Connectivity Encoding werden alle Kanten des Gittermodells erfasst und dann in Form von speziellen ,,Befehlen” kodiert. Als ergänzenden Schritt werden die resultierenden Befehle dann noch mit herkömmlichen Komprimierungsverfahren in ihrer Größe reduziert. Zum durchlaufen der Kanten wird zu Beginn der Komprimierung ein beliebiges Dreieck des Modells ausgesucht. Einer der drei Eckpunkte dient als sogenannter ,,Focus Vertex”. Das heißt, dass alle von diesem Punkt ausgehenden Kanten, nacheinander und im entgegengesetzten Uhrzeigersinn, durchlaufen werden. Die noch nicht durchlaufenen Kanten des Focus Vertex werden als ,,Free Edge” bezeichnet. Zusätzlich werden die drei Eckpunkte des, zu Beginn, gewählten Dreiecks in die ,,Active List” übernommen. Beim weiteren Kodieren wird jeweils der Endpunkt der gerade kodierten Kante in diese Liste eingefügt. Knoten deren Kanten bereits alle kodiert wurden, werden wieder aus der Active List entfernt. Daraus ergibt sich, dass das Gittermodell durch die Liste in zwei Hälften unterteilt wird: Ein innerer Teil, der alle bereits durchlaufenen Kanten enthält, und ein äußerer Teil, der alle noch nicht besuchten Kanten enthält. Der eigentliche Kodiervorgang beschränkt sich in der Mehrzahl der einzelnen Kodierungsschritte auf das erfassen der ,,Vertex degrees” mit dem sogennaten add-Befehl. Zur vollständigen Beschreibung des Gittermodells werden jedoch insgesamt 4 Befehle benötigt: • add < degrees > Hierbei wird der am anderen Ende, der zu kodierenden Kante, liegende Knoten betrachtet. Die Anzahl der von ihm ausgehenden Kanten (degrees) wird zusammen mit dem Befehl ,,add (degrees)” gespeichert. So ergibt sich z.B. bei einem Knoten mit 6 abgehenden Kanten der Befehl ,,add 6”. • add dummy < degrees > Dieser Befehl hat im wesentlichen die selbe Funktionalität wie der normale addBefehl. Der einzige Unterschied besteht im Zusatz ,,dummy”. Verwendet wird dieser Befehl immer dann, wenn das Gittermodell nicht rundherum geschlossen ist, also z.B. Löcher in der Ober¤ äche hat. Um das Gittermodell fehlerfrei zu komprimieren wird daher ein Dummy-Knoten in das Gittermodel eingefügt. Dieser Knoten wird dann beim decodieren wieder entfernt. • split < of f set > Der split-Befehl wird verwendet, wenn der Endknoten, der als nächstes zu kodierenden Kante, bereits in der Active List enthalten ist (also bereits verarbeitet wurde). In diesem Fall wird die Active List in zwei Teile unterteilt, wobei der eine Teil zur späteren Bearbeitung auf einem Stack abgelegt wird und der andere Abschnitt direkt weiter bearbeitet wird. Dabei gibt ,,offset” die Stelle an, an der die Liste geteilt wird. Der offset-Wert entspricht der Anzahl der Knoten, die im Uhrzeigersinn entlang der Active List durchlaufen werden, bis der Endknoten der neuen Kante erreicht ist. Der 5 Endknoten selber wird dabei mitgezählt. Also ergibt z.B. der Befehl ,,split 5” das die Active List nach 5 Knoten, vom Focus Vertex ausgehend, geteilt wird. (Beispiel sh. Abb. 1) • merge < index >< of f set > Wenn der Endknoten der zu kodierenden Kante bereits kodiert wurde und nicht in der aktuellen Active List enthalten ist, muss er in einer der auf dem Stack liegenden Listen enthalten sein. In diesem Fall wird der merge-Befehl verwendet. Der Befehl enthält als Attribute einen index und einen offset. Der index enthält die Nummer, unter der die entsprechende Active List auf dem Stack liegt. Der offset hat eine ähnliche Bedeutung wie beim split-Befehl: Der Wert entspricht der Anzahl der Knoten, die im Uhrzeigersinn durchlaufen werden, bis der Endknoten der neuen Kante erreicht ist. An dieser Stelle wird die aktuelle Active List mit der, auf dem Stack abgelegten, Active List zu einer neuen Liste verbunden. (Beispiel sh. Abb. 2) Nach dem alle Kanten des Gittermodells mit diesen Befehlen kodiert wurden, erhält man eine Befehlsliste, die aus den 4 Befehlen besteht und z.B. folgende Form hat: add 6, add 5, add 4, split 5, add 4. Mit der Befehlsliste ist der erste Schritt des ,, Connectivity Encoding” beendet. Zur Verdeutlichung folgen in den nächsten 4 Abschnitten Flussdiagramme und Beispiele zum Encoding und Decoding. 6 Abbildung 1: Beispiel zum split-Befehl. Die gestrichelte Kante zwischen den beiden Knoten soll kodiert werden. Da der Endknoten innerhalb der Active List (als schwarze Linie dargestellt) liegt, werden zwei neue Active Lists erzeugt. Abbildung 2: Beispiel zum merge-Befehl. Die gestrichelte Kante zwischen den beiden Knoten soll kodiert werden. Da der Endknoten innerhalb einer anderen Active List liegt, werden die beiden Active Lists zu einer gemeinsamen Liste verbunden. 7 3.1.1 Flussdiagramm Encoding Abbildung 3: Flussdiagramm zum Encoding der ,,Mesh Connectivity”. 8 3.1.2 Beispiel Encoding Abbildung 4: Beispiel zum Encoding der ,,Mesh Connectivity”. Die dicken Linien stellen die Active List dar und die dicken Punkte den Focus Vertex. Gestrichelte Linien wurden bereits kodiert. (a) Stellt das Gittermodell dar. (b) Da das Gittermodell nicht geschlossen ist, wird ein Dummy-Knoten an den Eckpunkten eingefügt. (c) Das Ausgangsdreieck wird festgelegt. So werden auch die ersten drei Befehle gewonnen: ,,add 6, add 7, add 4”. (d) Im entgegengesetzten Uhrzeigersinn wird die nächste Kannte mit dem add-Befehl erfasst: ,,add 4”. (e) ,,add 8”. (f) ,,add 5” (Die Verbindungskante zum Dummy-Knoten wird mitgezählt). (g) ,,add 5” Damit sind alle freien Kanten des Focus Vertex bearbeitet. (h) Der Focus Vertex wird entfernt und am nächsten Knoten der Active List entsteht der neue Focus Vertex. (i) ,,add 4”. (j) ,,add 5”. (k) Der Endknoten der nächsten Kanten wurde bereits erfasst und liegt in der aktuellen Active List. Daher wird der split-Befehl angewendet. Dieser Endknoten liegt auf der Active List 5 Knoten vom Focus Vertex entfernt. So ergibt sich der Befehl: ,,split 5”. (l) Die Active List wurde in zwei Teile geteilt. Die innere Liste wird auf dem Stack abgelegt. Der Endknoten der Kante wird zum Focus Vertex der äußeren Active List. (m) ,,add 4”. (n) ,,add 4” Alle Kanten des Focus Vertex wurden bearbeitet. (o) Der neue Focus Vertex hat nur noch eine freie Kante. Die Verbindungskante zum DummyKnoten: ,,add dummy 6”. Da keine weiteren freien Kanten entlang der Active List liegen wird sie abgeschlossen. (p) Die nächste Active List wird vom Stack genommen. (q) ,,add 4”. (r) Verschieben des Focus Vertex. (s) Verschieben des Focus Vertex. Alle Knoten der Active List sind erfasst. (t) Nachdem alle Kanten des Gittermodells kodiert wurden ergibt sich folgende Befehlsliste: ,,add 6, add 7, add 4, add 4, add 8, add 5, add 5, add 4, add 5, split 5, add 4, add 4, add dummy 6, add 4”. 9 3.1.3 Flussdiagramm Decoding Abbildung 5: Flussdiagramm zum Decoding der ,,Mesh Connectivity”. 10 3.1.4 Beispiel Decoding Abbildung 6: Beispiel zum Decoding der ,,Mesh Connectivity”. Ausgangspunkt ist die beim Encoding gewonnene Befehlsliste: ,,add 6, add 7, add 4, add 4, add 8, add 5, add 5, add 4, add 5, split 5, add 4, add 4, add dummy 6, add 4”. (a) Mit den ersten 3 Befehlen ,,add 6, add 7, add 4” und den entsprechenden Koordinaten wird das erste Dreieck erstellt. Gleichzeitig wird die Active List erstellt und der Focus Vertex festegelegt. (b) ,,add 4”. Ein neuer Knoten mit 4 Kanten wird erzeugt. die beiden inneren Kanten werden mit den entsprechenden Knoten verbunden. (c) ,,add 8”. (d) ,, add 5”. (Die Kanten die später zum Dummy-Knoten führen werden ebenfalls angelegt.) (e) ,,add 5”. Alle Kanten des Focus Vertex wurden erzeugt. (f) Der Focus Vertex wird auf den nächsten Knoten der Active List verschoben. (g) ,,add 4”. (h) ,,add 5”. (i) ,,split 5”. Durch den offset Wert 5 kann der Endknoten der nächsten (dick gestrichelten) Kante bestimmt werden. (j) Durch den split-Befehl wird die Active List geteilt. Die innere Liste wird auf dem Stack abgelegt. Der Endknoten der neuen Kante wird zum Focus Vertex der aktuellen Active List. (k) ,,add 4”. (l) ,,add 4”. Alle abgehenden Kanten des Focus Vertex wurden erzeugt. (m) Der Focus Vertex wird verschoben. (n) ,,add dummy 6”. Die noch freien 6 Kanten werden mit dem Dummy-Knoten verbunden. Damit sind alle Kanten der Active List wiederhergestellt. (o) Die zweite Active List wird vom Stack genommen. (p) ,,add 4”. Alle abgehenden Kanten des Focus Vertex wurden erzeugt. (q) Der Focus Vertex wird verschoben. Alle abgehenden Kanten des Focus Vertex wurden bereits erzeugt. (r) Der Focus Vertex wird verschoben. (s) Alle Kanten des Gittermodells wurden wiederhergestellt. 11 3.1.5 Komprimierung der Befehlsliste Durch die Umwandlung der Kanteninformationen in eine Befehlsliste wird bereits eine erhebliche Reduzierung der Datenmenge erreicht. Die Befehlsliste selber liegt jedoch noch in einer unkomprimierten Form vor, so dass hier noch ein weiterer Komprimierungsschritt erfolgen kann. Wenn man die Befehlsliste aus dem Beispiel betrachtet, fällt auf, dass der add-Befehl mit Abstand am häu£gsten verwendet wird. In typischen Gittermodellen verh ält es sich ähnlich: Neben dem add-Befehl gibt es nur noch einige wenige split-Befehle. Der mergeBefehl wird nur extrem selten verwendet. Aus diesen Beobachtungen ergeben sich Komprimierungsverfahren deren Anwendung sich sehr gut eignen: Lau¤ ängen- und Huffman-Kodierung. Besonders gute Ergebnisse werden erzielt, wenn eine regelmäßige Struktur innerhalb des Gittermodells vorliegt. Dann lassen sich Kompressionswerte von bis zu 0.2 Bits pro Knoten erreichen. Übliche Werte liegen zwischen 3 und 0.2 Bits pro Knoten. 3.2 Vertex Coordinate Compression Nachdem die Komprimierung der Kanteninformationen abgeschlossen ist, folgt die Komprimierung der Knotenkoordinaten. Zu Beginn werden die Koordinaten quantisiert. Standartwerte für diese Quantisierung liegen zwischen 8 und 12 Bit. Dabei kann die Anzahl der Bits den Details des Gittermodells angepasst werden: Eine höhere Anzahl an Bits für komplexe und aufwändig modellierte Bereiche und niedrige Bitwerte für Regionen mit niedrigem Detailgrad. Der nächste Schritt liegt in der Auswahl eines Anfangsdreiecks. Dieses Dreieck stimmt üblicherweise mit dem Ausgangsdreieck des ,,Connectivity Encoding” überein. Ausgehend von diesem Dreieck werden benachbarte Knotenkoordinaten bestimmt. Dabei wird die Position des Knoten vorhergesagt und dann nur noch die Differenz zu den tatsächlichen Koordinaten gespeichert. Die Vorhersage der Knotenposition erfolgt mit Hilfe der sogenannten ,,Parallelogramm Regel”. Hierbei wird der Knoten durch Bildung eines Parallelogramm aus einem Dreieck erzeugt. Gegeben sind die 3 Vektoren des Dreiecks: z.B. ~u, ~v , w. ~ Der neue Punkt r~p wird dabei mit folgender Formel berechnet: ~ r~p = ~v + ~u − w Eine noch genauere Möglichkeit zur Koordinatenvorhersage erhält man, wenn man die Winkel zwischen zwei oder mehreren benachbarten Dreiecken berücksichtigt. Dieses Verfahren wird auch als ,,crease-adapted prediction” bezeichnet. (Beispiel siehe Abb. 7) Nachdem man alle Differenzwerte zwischen den originalen und vorhergesagten Koordinaten berechnet hat, können diese Werte noch, wie beim ,,Connectivity Encoding”, mit einem Kompressionsverfahren wie z.B. Huffman komprimiert werden. Eine mögliche Alternative hierzu ist das erstellen eines ,,Codebook” in dem die am häu£gsten vorkommenden Differenzwerte abgelegt werden. Gebräuchliche Größen für ein solches ,,Codebook” liegen zwischen 16 und 128 Einträgen. Häu£g werden 32 Eintr äge verwendet. 12 Abbildung 7: ,,Parallelogramm Regel”. Aus dem Dreieck u, v, w wird mit Hilfe der Parallelogramm Regel der Punkt rp bestimmt. Der Abstand zum ,,echten” Punkt r ist im Vergleich zum Punkt rpc noch relative groß. Der Punkt rpc wird mit der ,,crease-adapted prediction” ermittelt. Dabei wird der Winkel zwischen den beiden Dreiecken s, v, w und s, t, v ermittelt und zur Erstellung des Punktes rpc verwendet. 13 4 Ergebnisse Nachdem alle Komprimierungsschritte angewandt wurden, bleibt noch die Betrachtung der erreichten Kompressionsraten. Die Tabelle 1 stellt einen Ausschnitt der, von den Autoren des Verfahrens, erzielten Ergebnisse. Bei den mit VRML97 gespeicherten Modellen ergibt sich ein Wert von 108 Bits pro Knoten, obwohl alle Modelle in einer komprimierten Form (gzipped) vorliegen. Bei dem hier vorgestellten Verfahren ,,Triangle Mesh Compression” teilt sich das Ergebnis in zwei Teilergebnisse auf. Beim ,,Connectivity Encoding” werden Raten von durchschnittlich 1,4 Bits pro Knoten erreicht, in Einzelfällen sogar bis zu 0,2 Bits. Etwas höher ist die Datenmenge bei der ,,Vertex Coordinate Compression”. Hier liegen die Werte bei etwa 9 Bits pro Knoten. Aus den beiden Teilergebnissen ergibt sich eine durchschnittliche Kompressionsrate von 10,4 Bits pro Knoten. Verglichen mit den VRML97 Modellen erreicht das vorgestellte Verfahren eine Datenmenge, die nur noch etwa ein zehntel so groß ist. Model Vertices blob triceratops eight shape beethoven engine dumptruck cow Average 8036 2832 766 2562 2655 2164 11738 3066 Gzipped VRML97 117K (119) 44K (127) 11K (118) 35K (112) 36K (111) 24K (91) 114K (80) 40K (108) (108) Triangle Mesh Compression Conn. Coord. Total 1709 (1.7) 7951 (7.9) (9.6) 764 (2.2) 2937 (8.3) (10.5) 53 (0.6) 683 (7.1) (7.7) 48 (0.2) 2990 (9.3) (9.5) 781 (2.4) 3576 (10.8) (13.2) 330 (1.2) 3425 (12.0) (13.2) 1210 (0.8) 11162 (7.5) (8.3) 779 (2.0) 3376 (8.8) (10.8) (1.4) (9.0) (10.4) Tabelle 1: Vergleich zwischen VRML97-Modellen (gzipped) und Modellen die mit Triangle Mesh Compression kodiert wurden. Die normalen Werte sind in Byte angegeben und die Werte in Klammern geben die Anzahl von Bits pro Knoten an. Abbildung 8: Beispiel für die in Tabelle 1 verwendeten Modelle beethoven und triceratops. 14 Literatur [1] Gabriel Taubin, Jarek Rossignac, Geometric Compression Through Topological Surgery, ACM Transactions on Graphics, Vol.17, No.2, April 1998, pp. 84-115. [2] Costa Touma, Craig Gotsman, Triangle Mesh Compression, Proc. of Graphics Interface 98, pp. 26-34, 1998. [3] Stefan Gumhold, Real time compression of triangle mesh connectivity. In Siggraph98 Conference Proceedings, pp. 133140, July 1998. [4] Jarek Rossignac, Edgebreaker: Connectivity compression for triangle meshes, IEEE Transactions on Visualization and Computer Graphics, 5(1), 1999. 15