Die ersten Linien

Alles Rund um Computergrafik, OpenGL, prozeduraler Content Generierung, etc :)

Die ersten Linien

Also, wie versprochen gibt es hier mal die ersten Linien zu sehen. Diese werden mittels three.js gezeichnet und animiert. Es folgt die Demo Applikation und weiter unten eine genauere Code-Beschreibung. Dieses Format wird auch in Zukunft in ähnlicher Form weitergeführt.

Demo Applikation

Diese Demo Applikation war nur einmal ein erster Test zum Zeichnen von Linien mit three.js, um in nächster Folge die erste Applikation mit L-Systemen erstellen zu können.

Bleibt nur noch festzuhalten, dass ich mit diesen Applikationen hier sozusagen „on-the-fly“ gleich three.js lerne, denn bis auf die kleinen Apps, die ich hier verwende, habe ich mit dieser Bibliothek noch nichts gemacht.

Sollte daher jemandem ein viel schönerer Weg einfallen, wie man einzelne Teile effizienter / besser gestalten kann, dann bitte nur her mit den Ideen. Noch eine Kleinigkeit: bei den späteren Applikationen werde ich vermutlich nicht mehr den gesamten Code in eine einzelne HTML – Datei packen, sondern beginnen die HTML Seite vom Javascript Code zu trennen. Dabei werde ich dann auch gleich mit Klassen arbeiten (und diese auf einzelne JS Dateien aufteilen), ich finde das schöner, als alles in eine Datei zu packen.


Der Code

Interessant sind natürlich vor allem die Javascript Teile des Codes. Diese werde ich nun alle beschreiben, damit klar ist, was bei den einzelnen Code – Teilen passiert. In dieser kleinen Applikation habe ich noch keine Klassen definiert und verwendet, ich denke, das wäre hier noch ein wenig zu früh (obwohl man die Linien – Logik natürlich schon schön kapseln könnte).

Den Start macht diese Prototyp – Funktion, welche die Klasse Number (also alle Zahlen) durch die Funktion clamp erweitert. Die Funktion benötigt als Übergabeparameter zwei Zahlen, welche einen Bereich definieren. Die Zahl min stellt die untere Grenz des Bereichs und die Zahl max die obere Grenze des Bereichs dar.

Verwendet wird die Funktion zum Animieren der Linien. Sollte die X- oder Y- Koordinate eines Endpunktes einer Linie außderhalb des sichtbaren Bereichs gesetzt werden, dann wird die Koordinate auf den sichtbaren Bereich beschränkt.

three.js hätte kein Problem mit nicht sichtbaren Linien, diese werden natürlich korrekt abgeschnitten bzw. gar nicht gezeichnet, ich möchte jedoch vermeiden, dass nach 10 Minuten alle Linien außderhalb des Sichtbereichs landen (was rein theoretisch passieren könnte).

Nach der clamp – Funktion werden die Basis Setups für die Anzeige der Szene durchgeführt. Diese Schritte sind bei jeder three.js Anwendung in einer ähnlichen Form nötig, daher kann man diesen Teil gar nicht oft genug beschreiben und noch einmal durchgehen :).

Zur besseren Lesbarkeit erstelle ich zu Beginn zwei Hilfsvariablen maxX und maxY. Diese beiden Variablen definieren die halbe Breite und die halbe Höhe des Zeichenbereiches (Canvas).

Mit der Hilfe der beiden Variablen wird dann ein Kamera Objekt erstellt, welches keine Perspektivische Verzerrung besitzt (eine Othografische Kamera). Die Argumente bei der Kamera – Erstellung definieren die minimalen und maximalen Werte der drei Achsen. Diese Werte definieren sozusagen den sichtbaren Bereich, der später dann angezeigt werden soll.

Der sichtbare Bereich ist so definiert, dass ich die Linien Koordinaten in Pixel des Bereiches angeben kann, wobei der Koordinatenursprung in der Mitte der Anzeige ist. Der Z – Bereich ist auf die Werte 1 – 11 beschränkt. Zusammen mit dem Setzen der Kameraposition auf den Punkt 0 / 0 / 10 ergibt das einen sichtbaren Bereich von 1 bis -1. Ich versuche den Z – Bereich stets so klein wie möglich zu halten, da dann negative Effekte, wie zum Beispiel Z-fighting reduziert oder ganz vermieden werden können.

Als nächstes wird eine three.js Szene erstellt und der Hintergrund auf ein helles Grau gesetzt (Farbe #efefef). Zuletzt wird noch der Renderer erstellt und dessen Größe und ClearColor gesetzt, bevor der Renderer zu dem dafür vorgesehenen HTML-Element gehängt wird. Die ClearColor ist ebenfalls ein helles Grau (Farbe #cccccc) und definiert, mit welcher Farbe ein neuer Frame befüllt wird, um dann darauf zu zeichnen.

Es folgt die Definition eines Materials. In diesem Fall wird ein LineBasicMaterial verwendet, welches zum zeichnen von Linien eingesetzt werden kann. In diesem Fall wird eine Eigenschaft vertexColors gesetzt, was zu Folge hat, dass die später gesetzten Farben verwendet werden und nicht eine Farbe für alle Linien verwendet wird. Dadurch ist es möglich, jeder Linie eine eigene Farbe zuweisen zu können.

Es wird ein three.js BufferGeometry Objekt erstellt, welches mit den Positionen der Linienendpunkte und deren Farben befüllt wird. Um diese Daten zu generieren, werden zwei Arrays positions und colors definiert und in der Schleife mit teilweise zufälligen Werten befüllt. Diese Werte werden nach der Schleife verwendet um die Positionen und Farbwerte des BufferGeometry Objekts zu setzen. Die Berechnung der Boundingsphrere spielt in dieser kleinen App keine Rolle, wird jedoch in Zukunft verwendet, um die Szene richtig im Sichtbereich darzustellen und zu skalieren.

Zum Abschluss wird noch ein Objekt lines erstellt und der Szene hinzugefügt. Die lines bestehen aus der vorher berechneten Geometrie und der Materialdefinition. In unserem Fall wird die Farbinformation der Geometrie zum Zeichnen verwendet.

Abschließend wird noch eine Funktion namens render definiert. Diese Funktion wird immer wieder aufgerufen und definiert unsere Render-Loop.

Am Beginn der Funktion wird eine Funktion aufgerufen, die ein Animations-Frame anfordert. In dieses neue Frame wird dann gezeichnet. Solange das Zeichnen in den Frame nicht abgeschlossen ist, wird noch das alte Frame angezeigt, sobald das Frame jedoch fertiggezeichnet ist, wird das alte Frame verworfen und das neu gezeichnete Frame wird angezeigt. Dies nennt man Double-Buffering, weil im Grunde immer zwei Frames abwechselnd angezeigt werden. Es gibt jedoch komplexere Buffer Strategien, wie zum Beispiel Triple-Buffering, bei welchen drei Frames nacheinander angezeigt werden.

Um eine Animation zu erhalten, werden die aktuell gezeichneten Positionen der Linien ermittelt. Diese Positionen werden dann nacheinenander in der Schleife aktualisiert, also leicht in X- und Y- Richtung verschoben. Hierbei wird die oben genannte clamp – Funktion verwendet, um sicherzustellen, dass die Linien alle innerhalb des sichtbaren Bereichs bleiben.

Nach dem Verschieben der Punkte wird three.js mitgeteilt, dass die Positionen der Linien neu zur Grafikkarte geschickt werden müssen, weil sich ja deren Positionen geändert haben. Dies ist absolut nicht optimal, aber mit 1000 Linien habe ich keine Performance Probleme feststellen können. Die Applikation läuft mit 60FPS bei mir am Laptop.

Am Ende der Funktion wird dann das tatsächliche Rendern der Linien mit Hilfe des Renderer gestartet.

Nach der Definition der Funktion render wird diese einmalig aufgerufen, was die Animation startet (weil Render immer wieder aufgerufen wird).