In dieser zweiteiligen Serie soll das Open-Source-Projekt PalletJS vorgestellt werden, das ausschließlich auf Basis von JavaScript und explizit ohne weitere Abhängigkeiten eine Palettenvisualisierung implementiert. Beginnen wollen wir mit einem Aspekt, der in diesem Kontext häufig von externen Bibliotheken übernommen wird: dem Rendering mit WebGL.
Es finden sich viele Ressourcen, die sich mit der Visualisierung von Paletten im Web beschäftigen, diese sind jedoch meistens entweder Closed Source oder von existierenden Bibliotheken wie Three.js oder ähnlichen abhängig. Letzteres ist vor allem in Anbetracht von Quellcodeverwaltung, Robustheit und für den Fall, dass die Software auf Low-End-Geräten zum Einsatz kommen soll, von Nachteil. Hier soll PalletJS ansetzen: Eine abhängigkeitsfreie Open-Source-Software zur Visualisierung von Paletten in 3D, die frei vom Nutzer spezifiziert werden können. Die Applikation befindet sich derzeit in der Endphase der Entwicklung. Nach dem Ende dieser Artikelserie wird der gesamte zugehörige Code unter [1] zur Verfügung stehen. Dort findet sich die vollständige WebGL-Applikation aus diesem Artikel. Abbildung 1 zeigt die Visualisierung einer Beispielpalette in der derzeitigen Version von PalletJS.
In diesem Teil beschäftigen wir uns mit der Theorie und Implementierung des grundsätzlichen Rendering-Prozesses von WebGL, während in der nächsten Ausgabe auf dieses Rendering aufbauend die Konzeption und Implementierung der gesamten Palettenvisualisierung behandelt wird. Auch wenn hierbei Vorwissen im Bereich von Geometrie, Algebra und Grafik zum Verständnis hilfreich ist, richtet sich der Artikel in erster Linie an Personen ohne großen mathematischen Hintergrund.
Da geometrisch betrachtet eine Palette nur aus einer Ansammlung von Quadern besteht (in Wirklichkeit: Kartons auf der Palette), wird der Rendering-Prozess am Beispiel einer Box illustriert. Zuerst wollen wir hierbei auf die notwendigen mathematischen Grundlagen, spezifisch Vektoren, Matrizen und Transformationen, eingehen. Anschließend werden wir diese Konzepte verwenden, um sowohl die Geometrie der Box und ihre Projektion auf das Canvas zu modellieren. Am Ende skizzieren wir noch, welche Schritte zur Implementation mit WebGL nötig sind.
Abb. 1: Visualisierung einer Palette in PalletJS
Viele haben vermutlich schon in Ihrer Schul- oder Studienzeit den Begriff eines Vektors im Kontext der Geometrie kennengelernt. Konzeptionell ist ein reeller n-dimensionaler Vektor nicht mehr als eine Liste von n-vielen reellen Zahlen vielen reellen Zahlen (i.e. Zahlen mit beliebig vielen Nachkommastellen), denen wir eine Bedeutung zuweisen können. Bspw. kann der Vektor verwendet werden, um in einem RGB-System einen dunklen Grauton zu beschreiben, eine Zeitspanne von 55 Stunden, 55 Minuten, 55 Sekunden darzustellen oder die Zeichenfolge 777 im ASCII-Standard notieren.
Im Kontext der Geometrie arbeiten wir in der Regel mit zwei- und dreidimensionalen Vektoren. Die einzelnen Einträge beziehen sich hierbei auf eine gewisse Anzahl Schritte entlang der Achsen eines gegebenen Koordinatensystems. Konkret notiert im dreidimensionalen Fall der Vektor insgesamt entlang der x-Achse (rechts/links), entlang der y-Achse (unten/oben) und entlang der z-Achse (vorne/hinten).
Wie im obigen Beispiel kann hierbei der Vektor verwendet werden, um unterschiedliche Gegebenheiten zu beschreiben. Am Beispiel in Abbildung 2 kann man sehen, dass der Vektor sowohl die Position des Punktes wiedergibt, eine Bewegung dieses Punktes nach notiert und die Ausrichtung des Quadrats Q beschreibt. Um diese verschiedenen Interpretationsmöglichkeiten gut unterscheiden zu können, werden wir im Nachfolgenden einheitlich Positionen im Raum mit (für Punkt), Bewegungen mit (für Translation) sowie RGB-Farben mit (für Color) notieren. Zusätzlich wird für Vektoren ohne zugewiesene Bedeutung verwendet. In jedem anderen Fall ( ) ist eine Richtung gemeint.
Abb. 2: Vektoren, um Positionen im Raum, Bewegungen und Richtungen zu beschreiben
Wie in dem schon aus der Schule bekannten Zahlensystem können wir für Vektoren verschiedene Rechenoperationen definieren. Gegeben sind zwei Vektoren, und , bei denen die in Abbildung 3 dargestellten Verknüpfungen bekannt sind. Beachtet man Abbildung 3 genauer, erkennt man, dass Addition und Subtraktion typischerweise für Bewegungen verwendet werden. Als Beispiel ergibt sich der Ortsvektor zum Punkt , der aus der Addition von und , also sichtbar wird. Multiplikationen von Vektoren mit einer Zahl kommen unter anderem im Skalieren der Größe von geometrischen Objekten (bspw. Quadrat, Hexagon, Quader, …) und zur sogenannten Normierung eines Vektors zum Einsatz.
Abb. 3: Verknüpfungen für Vektoren
Letzteres ist besonderes dann relevant, wenn eine Richtung beschrieben werden soll: Betrachten wir als Beispiel die beiden Richtungsvektoren und . In jedem Fall wird hier dieselbe Richtung beschrieben, nur die Länge der Vektoren ist unterschiedlich. Da wir im besten Fall mit einer eindeutigen Darstellung für Richtungen arbeiten wollen, einigen wir uns nun darauf, dass Richtungsvektoren grundsätzlich normiert sein müssen, sie haben genau Länge 1.
Um Vektoren, die keine Länge 1 haben, zu Vektoren mit Länge 1 umzubauen, kann man sie mit dem multiplikativen Inversen ihrer Norm ( ) multiplizieren. Stellt man sich hierbei den Vektor als einen Pfeil im Raum vor, so entspricht die Norm von der Länge dieses Pfeils, bestimmbar mittels des Satz des Pythagoras. Normieren wir obigen Beispielsvektor , erhalten wir gesamt . ist also die normierte Variante von .
Schließlich haben wir noch das Skalar- und Kreuzprodukt. Ersteres gibt uns Informationen darüber, wie zwei Vektoren zueinanderstehen: Berechnen wir das Skalarprodukt zweier normierter Richtungsvektoren und als , so stehen diese
senkrecht aufeinander, wenn ist,
zeigen in dieselbe Richtung für und
sind zueinander entgegengesetzt bei (wichtig: das gilt nur für normierte Vektoren).
Das Kreuzprodukt hingegen erlaubt uns für zwei gegebene Vektoren einen neuen Vektor, der genau senkrecht auf beiden steht, zu generieren. Das wird unter anderem später helfen, wenn wir die Ausrichtung der Kamera in der Szene modellieren wollen. Beachte: Das Kreuzprodukt ist nur für dreidimensionale Vektoren definiert, während alle anderen Verknüpfungen sich auf beliebig dimensionale Vektoren anwenden lassen.
Betrachtet man in Bezug auf Programmiersprachen einfache Arrays als Analogon zu Vektoren, so entsprechen Matrizen zweidimensionalen Arrays. Konkret ist eine Matrix eine Tabelle von Zahlen mit Zeilen und Spalten, wobei den Eintrag für die -te Zeile und -te Spalte notiert. Zum Beispiel ist in der -Matrix E (Abb. 4) , und . Eine wichtige Beobachtung: Für uns sind Vektoren nichts anderes als -Matrizen, also Matrizen mit einer Zeile und n Spalten.
Abb. 4: Formel der Matrix E
In Bezug auf Verknüpfungen kann man äquivalent wie für Vektoren Addition, Subtraktion und Multiplikation einer Zahl für Matrizen definieren. Für uns besonders relevant ist allerdings die sogenannte Matrixmultiplikation. Nehmen wir zwei Matrizen A und B. Ist A eine Matrix und B eine -Matrix (i. e., A hat genauso viele Spalten, wie B Zeilen hat), so lässt sich eine Produktmatrix berechnen, wobei C eine -Matrix ist. Betrachtet man hierbei und als die Vektoren der -ten Zeile in und -ten Spalte in , so ist der Eintrag von gegeben durch , also das Skalarprodukt dieser beiden Vektoren. Als Beispiel wollen wir die Multiplikation einer -Matrix A mit einem -Vektor betrachten (Abb. 5).
Abb. 5: Formel einer Vektormultiplikation
Beachte: Das , die sogenannte Transposition, vertauscht Zeilen und Spalten und wird hier verwendet, um aus dem -Vektor einen -Vektor zu machen. Das Ergebnis ist ein -Vektor, wobei effektiv die zweite Spalte von selektiert wurde.
Die Klasse von Matrizen, die für uns von besonderer Bedeutung ist, sind die Transformationen. Formal handelt es sich hierbei um genau die Matrizen, die einen sogenannten Basiswechsel beschreiben, näher erläutert in Abbildung 4. Hier beschreiben zwei verschiedene Personen die Position desselben Punktes P. So wird Person den Punkt 2 Meter links und einen Meter vor sich beschreiben (also ), während Person den gleichen Punkt 1 Meter rechts und 3 Meter vor sich beschreibt (also ). Beide Beschreibungen sind valide, nur der Referenzrahmen (das genutzte Koordinatensystem) ist unterschiedlich. Eine Transformation kann hierbei den Übergang zwischen diesen beiden Referenzrahmen beschreiben. Sie beantwortet die Frage: „Wie sieht die Welt gesehen von Person aus Sicht von Person aus?“. In der Grafik ist dieses Konzept vor allem dann wichtig, wenn wir etwa wissen wollen, wie die Welt aus der Sicht der Kamera aufgebaut ist.
Abb. 6: Ein Beispiel für unterschiedliche Basissysteme
Transformationen lassen sich zudem nutzen, um Bewegungen sowie Deformationen im dreidimensionalen Raum zu modellieren. Betrachten wir als Beispiel die Rotation (beschrieben durch die Matrix ) eines Punktes mit anschließender Verschiebung entlang des Vektors (Abb. 7).
Abb. 7: Formel einer Rotation
B beschreibt die vollständige Bewegung des Punktes , die durch Multiplikation realisiert wird. Zu beachten ist hier, dass wir uns zur Implementation der Verschiebung eines Tricks bedienen: Anstatt rein mit dreidimensionalen Koordinaten zu rechnen, erweitern wir diese stattdessen zu sogenannten homogenen Koordinaten. Dabei fügen wir eine weitere Dimension hinzu, die mit dem Wert 1 gefüllt wird (entsprechend sind unsere Bewegungen dann auch mit Matrizen modelliert). Wie im obigen Beispiel zu sehen, wird diese neue Komponente nach der Definition der Matrixmultiplikation mit malgenommen, was schlussendlich effektiv einer Addition von zum Punkt gleichkommt.
Eine besonders kritische Eigenschaft dieser Art Matrizen ist, dass es sehr einfach ist, Transformationen...