Herzlich Willkommen zu unserem neunten Teil unserer WebGL Grundlagen. In diesem Artikel werden wir JavaScript-Objekte verwenden, so dass wir eine Reihe von unabhängig animierten Objekten in unserer 3D-Szene haben können. Darüber hinaus werden wir thematisieren, wie man die Farbe einer geladenen Textur ändern kann und was passiert, wenn man Texturen miteinander vermischt.

Dieses Ergebnis werden Sie nach der Umsetzung erhalten:

For privacy reasons YouTube needs your permission to be loaded. For more details, please see our Datenschutzerklärung.

Ein wichtiger Hinweis: Dieser Teil richtet sich an Personen mit fortgeschrittenen Programmierkenntnissen, aber ohne wirkliche Erfahrung mit 3D-Grafiken. Sie werden ein gutes Verständnis für den Code mitbringen und es zum Laufen bringen, so dass Sie so schnell wie möglich mit der Erstellung einer eigenen Website in 3D beginnen können.

Es existieren zwei verschiedene Möglichkeiten, den Code für dieses Beispiel zu erhalten. In den Browsereinstellungen mit „View Source“, während Sie sich die Live-Version ansehen oder wenn Sie GitHub verwenden, können Sie es aus dem Repository kopieren. Unabhängig davon für welchen Weg Sie sich entscheiden, sollten Sie ihren bevorzugten Texteditor herunterladen und Sie den Code genau ansehen.

Um die Unterschiede zum Code im achten Grundlagenteil zu demonstrieren, beginnen wir am unteren Teil der Datei und arbeiten uns Stück für Stück nach oben, um mit webGLStart zu beginnen. Hier sehen Sie, wie die Funktion diesmal aussieht:

Copy to Clipboard

In der Datei findet sich eine Änderung, die rot hervorgehoben wurde. Es handelt sich dabei um den Aufruf der neuen initWorldObjects-Funktion. Diese Funktion erstellt JavaScript-Objekte, um die Szene darzustellen. Bevor wir uns intensiver mit der Funktion auseinandersetzen macht es Sinn, hier noch eine weitere Änderung anzusprechen. Alle bisher betrachteten webGLStart-Funktionen hatten eine Zeile, um ein Depth Testing einzurichten:

Copy to Clipboard

In diesem Beispiel ist diese Zeile nicht vorhanden. Sie werden sich vom letzten Artikel wahrscheinlich daran erinnern, dass Blending und Depth Testing nicht gut zusammenpassen und wir während des gesamten Beispiels Blending verwendet haben. Das Depth Testing ist standardmäßig deaktiviert, so dass wir durch das Entfernen dieser Zeile aus unserem üblichen Textbaustein alle Einstellungen vorgenommen haben.

Die nächste große Änderung ist die Animationsfunktion. Früher haben wir diese verwendet, um die verschiedenen globalen Variablen zu aktualisieren, die die sich verändernden Aspekte unserer Szene repräsentieren – zum Beispiel den Winkel, um den wir einen Würfel drehen sollten, bevor wir ihn zeichnen. Die Änderung, die wir hier vorgenommen haben ist sehr einfach. Anstatt Variablen direkt zu aktualisieren durchlaufen wir alle Objekte in unserer Szene und sagen jedem, dass er sich selbst animieren soll:

Copy to Clipboard

Wenn wir uns nach oben arbeiten, kommt drawScene als nächstes. Der Code hat sich derart verändert, dass es sich nicht mehr lohnt die entsprechenden neuen Abschnitte farblich zu markieren. Wir gehen den Code Stück für Stück durch:

Copy to Clipboard

Dies ist lediglich unser üblicher Setup-Code.

Copy to Clipboard

Als nächstes schalten wir das Blending ein. Wir verwenden das gleiche Blending wie in unserem letzten Beitrag. Sie werden sich erinnern, dass dadurch Objekte sich gegenseitig durchscheinen können. Um die Funktionsweise zu verstehen, werfen Sie einen Blick auf die Beschreibung der Blending Funktion in unserem letzten Beitrag, Dies hat zur Folge, dass wenn wir die Sterne in unserer Szene zeichnen, die schwarzen Bits transparent erscheinen. Je weniger hell ein Teil des Sterns ist, desto transparenter wird dieser erscheinen. Dies bringt uns genau den Effekt, den wir möchten.

Weiter zum nächsten Teil des Codes:

Copy to Clipboard

Hier bewegen wir uns einfach in die Mitte der Szene und zoomen dann entspprechend heran. Wir kippen auch die Szene um die X-Achse. Zoom und Tilt sind immer noch globale Variablen, welche über die Tastatur gesteuert werden. Wir sind jetzt so gut wie fertig, um die Szene zu zeichnen, also prüfen wir zuerst, ob das Kontrollkästchen „Glitzern“ aktiviert ist:

Copy to Clipboard

…und dann, genau wie bei der Animation iterieren wir über die Liste der Sterne und sagen jedem einzelnen, dass er sich selbst zeichnen soll. Wir gehen in der aktuellen Neigung der Szene und dem Glitzerwert vorbei. Wir sagen ihm auch, was der aktuelle Spin ist – er wird benutzt, um die Sterne dazu zu bringen, sich um ihre Zentren zu drehen, während Sie das Zentrum der Szene umkreisen.

Copy to Clipboard

Also, das ist drawScene. Wir haben jetzt gesehen, dass die Sterne eindeutig in der Lage sind, sich selbst zu zeichnen und zu animieren. Der nächste Code ist das Bit, welches Sie erzeugt:

Copy to Clipboard

Eine einfache Schleife, die 50 Sterne erzeugt. Jedem Stern wird ein erster Parameter zugewiesen, welche eine Startecke vom Zentrum der Szene und eine zweite, die eine Geschwindigkeit für die Umlaufbahn um das Zentrum der Szene angibt, die beide von ihrer Position in der Liste herrühen.

Der nächste Code, den man sich genauer anschauen muss, beinhaltet die Klassendefinition für die Sterne. Wenn Sie nicht an JavaScript gewöhnt sind, wird es sehr merkwürdig aussehen:

Das Objektmodell von JavaScript unterscheidet sich stark von anderen Sprachen. Die Art und Weise, wie man es am einfachsten verstehen kann, ist dass Objekt als Wörterbuch zu erstellen und dann in ein vollwertiges Objekt zu verwandeln, indem man Werte hineinlegt. Die Felder des Objekts sind lediglich Einträge im Dictionary, die auf Werte abbilden und die Methoden sind Einträge, die auf Funktionen abbilden. Jetzt fügen wir noch hinzu, dass die Syntax foo.bar eine valide Verknüpfung für foo[„bar“] ist und Sie können sehen, wie Sie von einem ganz anderen Ausgangspunkt aus eine Syntax erhalten, die der anderer Sprachen ähnelt.

Als nächstes, wenn Sie sich in einer JavaScript-Funktion befinden, gibt es eine implizit verbundene Variable mit der Bezeichnung this, die sich auf den Eigentümer der Funktion bezieht. Für globale Funktionen ist dies ein globales, seitenweises Fenster-Objekt. Wenn Sie das Schlüsselwort new voranstellen, dann ist es ein brandneues Objekt. Wenn Sie eine Funktion haben, die this.foo auf 1 und this bar auf eine Funktion setzt und Sie anschließend sicherstellen, dass Sie immer mit dem neuen Schlüsselwort aufrufen, ist es im Grunde genommen dasselbe wie ein Constructor, der mit einer Klassendefinition kombiniert wird.

Als nächstes können wir feststellen, dass wenn eine Funktion mit Methodenaufrufähnlicher Syntax (d.h. foo.bar()) aufgerufen wird, diese an den Eigentümer der Funktion (foo) gebunden wird, so wie wir es erwarten würden, so dass die Methoden des Objekts Dinge mit dem Objekt selbst machen können.

Schließlich gibt es noch ein spezielles Attribut, das mit einer Funktion verbunden ist, den sogenannten Prototyp. Dies ist ein Wörterbuch von Werten, welche mit jedem Objekt verbunden sind, welches mit dem neuen Schlüsselwort mit dieser Funktion erstellt wird. Dies ist eine gute Möglichkeit Werte zu setzen, die für jedes Objekt dieser Klasse gleich sind – zum Beispiel Methoden.

Werfen wir nun einen Blick auf die Funktion, mit dem wir ein Stern-Objekt für die Szene definieren.

Copy to Clipboard

In der Constructor-Funktion wird also der Stern mit den von uns angegebenen Werten und einem Startwinkel von Null initialisiert und dann eine Methode aufgerufen. Der nächste Schritt besteht darin, die Methoden an den zugehörigen Prototyp der Stern-Funktion zu binden, so dass alle neuen Sterne die gleichen Methoden haben. Zuerst die draw-Methode:

Copy to Clipboard

Draw ist definiert um die Parameter, die wir ihm übergeben haben in der Hauptfunktion von drawScene zurückzunehmen. Wir beginnen damit die aktuelle Model-View-Matrix auf den Matrix-Stack zu schieben, so dass wir uns frei bewegen können, ohne Angst vor Nebenwirkungen an anderer Stelle zu haben.

Copy to Clipboard

Als nächstes drehen wir uns um die Y-Achse um den eigenen Winkel des Sterns und bewegen uns um den Abstand des Stern vom Zentrum. Das versetzt uns in die richtige Position, um den Stern zu zeichnen:

Copy to Clipboard

Diese Sterne sind notwendig, damit die Sterne auch dann noch richtig aussehen, wenn wir die Neigung der Szene mit den Cursortasten verändern. Sie werden als 2D-Textur auf en Quadrat gezeichnet, welches genauso so aussieht, wenn wir es geradewegs betrachten, aber nur wie eine Linie aussehen würde, wenn wir die Szene so kippen würden, dass wir Sie von der Seite betrachten. Aus ähnlichen Gründen müssen wir auch die Rotation, welche für die Positionierung des Sterns erforderlich ist, rückgängig machen. Wenn Sie solche Dinge rückgängig machen, müssen Sie dies in der umgekehrten Reihenfolge tun, in der Sie sie zuerst gemacht haben. Das bedeutet, dass wir also zuerst die Drehung von unserer Positionierung rückgängig und dann die Neigung.

Als nächstes zeichnen wir die Sterne:

Copy to Clipboard

Ignorieren wir den Code, welcher für den Glitzereffekt ausgeführt wird für einen Moment. Der Stern wird gezeichnet, indem dieser zuerst um die Z-Achse durch den eingeleiteten Spin gedreht wird, so dass er sich um sein eigenes Zentrum dreht, während er das Zentrum der Szene umkreist. Dann schieben wir die Farbe des Sterns in einer Shader-Uniform auf die Grafikkarte und rufen dann eine globale drawStar-Funktion auf.

Was ist nun mit dem glitzernden Zeug? Der Stern hat zwei Farben, welche mit ihm verbunden sind seine normale Farbe und seine funkelnde Farbe. Um es funkeln zu lassen, zeichnen wir zunächst einen nicht-spinnenden Stern in einer anderen Farbe. Das bedeutet, dass die beiden Sterne miteinander vermischt sind, was eine schöne helle Farbe ergibt, aber auch, dass die Strahlen die aus dem ersten zu zeichnenden Stern kommen stationär sind, während die aus dem zweiten zu zeichnenenden Stern kommenden Strahlen rotieren, was einen schöneren Effekt ergibt.

Sobald wir also den Stern gezeichnet haben, platzen wir einfach unsere Model-View-Matrix aus dem Stack und sind fertig:

Copy to Clipboard

Als nächstes animieren wir den Stern:

Copy to Clipboard

Wie bereits in den vorangegangenen Beiträgen haben wir uns auch hier dafür entschieden, die Szene nicht nur so schnell wie möglich zu aktualisieren, sondern Sie in einem konstanten Tempo für alle zu verändern, damit Leute mit schnelleren Computern flüssigere Animationen und Leute mit langsamen Computern ruckartige Animationen präsentiert bekommen. Die Zahlen für die Winkelgeschwindigkeit, mit der die Sterne das Zentrum der Szene umkreisen und sich in Richtung Zentrum bewegen, sorgfältig berechnet wurden. Wir gehen im Folgenden davon aus, dass die Zahlen für 60 Bilder pro Sekunde kalibriert wurden und die dass die elapsedTime um den Betrag skaliert wurde. Wir möchten effektive Frames-per-Millisekunde von 60/1000. Wir setzen dies in eine globale Variable außerhalb der animierten Methode, so dass es nicht jedes Mal neu berechnet wird, wenn wir einen Stern malen.

Nun haben wir also diese Zahl, mit der wir den Winkel des Sterns einstellen können, d.h. die Entfernung des Sterns vom Zentrum.

Copy to Clipboard

…und wir können seinen Abstand vom Zentrum anpassen, ihn nach außen verschieben und seine Farbe auf etwas Zufälliges zurücksetzen, wenn dieser schließlich das Zentrum erreicht:

Copy to Clipboard

Das letzte Stück Code, das den Prototyp des Sterns ausmacht ist der Code, den wir im Constructor und gerade jetzt im Animationscode gesehen haben, um seine funkelnden und nicht funkelnden Farben zu randomisieren:

Copy to Clipboard

…und wir sind fertig mit dem Prototyp des Sterns. So entsteht also ein Sternobjekt, komplett mit Methoden zum Zeichnen und Animieren. Über diesen Funktionen können Sie jetzt den Code sehen, welcher den Stern zeichnet. Er zeichnet nur ein Quadrat in einer Weise, wie man es von unserer ersten Übung her kennt, indem seine eigene Textur und einen geeigneten Koordinatenpuffer für die Vertexposition/Textur verwendet:

Copy to Clipboard

Etwas weiter oben sehen Sie initBuffers, welche diese Vertexpositions- und Texturkoordinatenpuffer einrichten und dann den entsprechenden Code in handleKeys verwenden, um die Zoom- und Tilt-Funktionen zu manipulieren. Dazu muss der Anwender die Cursortasten nach oben/unten oder die Tasten Page Up/Page Down drücken. Wenn Sie etwas weiter nach oben im Code schauen, werden Sie sehen, dass die beiden Funktionen initTexture und handleLoadedTexture aktualisiert wurden, um die neue Textur zu laden.

Gehen wir einfach bis zu den Shadern, wo Sie die letzte Änderung sehen können, die für diesen Beitrag erforderlich war. Alle beleuchtungsrelevanten Dinge wurden aus dem Vertex-Shader entfernt, der jetzt genauso aussieht wir in unserem fünften Artikel. Der Fragment-Shader ist ein wenig interessanter:

Copy to Clipboard

… aber nicht viel interessanter ist es die Farbuniform, die hier durch den Code in der Zeichenmethode des Sterns nach oben geschoben wurde aufzugreifen und mit ihr die Textur zu färben, die monochrom ist. Das bedeutet, dass unsere Sterne in der richtigen Farbe erscheinen.

Und das wars. Jetzt wissen Sie alles, was Sie aus diesem Beitrag lernen können. Wie Sie JavaScript-Objekte erstellen, um Dinge in ihrer Szene darzustellen und ihnen Methoden an die Hand geben, mit denen Sie separat animiert und gezeichnet werden können.

Vielen Dank fürs Lesen.