Web GL Grundlagen 3 – Bewegungen hinzufügen.
Willkommen zum dritten Teil unserer WebGL Grundlagen. Dieses Mal werden wir Objekte in Bewegung bringen.
Dieses Ergebnis werdet ihr nach der Umsetzung erreichen:
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 den Code sich genau ansehen.
Bevor wir den Code beschreiben, möchten wir zunächst eine Sache klarstellen. Die Art und Weise, wie Sie eine 3D-Szene in WebGL animieren ist sehr einfach. Sie zeichnen einfach wiederholt und jedes mal anders. Letzters ist für viele Personen überraschend, wenn Sie mit WebGL zum erstellen mal 3D-Grafiken entwickeln. Es kann gut sein, dass Sie zunächst verwirrt sind und sich vorstellen, dass eine höhere Abstraktion verwendet wird.b Stattdessen passiert es eher, dass Sie dem 3D-System mitteilen, dass es ein Quadrat an Punkt X, Punkt Y oder Z gibt und so weiter.
Wir hoffen, dass der letzte Teil die Dinge für einen Großteil der Personen klarer gemacht hat.
Wir Sie sich erinnern können, haben wir eine Funktion mit der Bezeichnung DrawScene genutzt, um alles zu zeichnen und Dinge zu animieren. Wir benötigen diese Funktion, um Dinge so zu arrangieren, dass diese Funktion wiederholt aufgerufen wird und jedes mal etwas anderes zeichnet. Im Folgenden beginnen wir am Ende der index.html-Datei und schauen, wie dies funktioniert. Zuerst betrachten wir die Funktion webGLStart, die alles startet, wenn die Seite geladen wird.
Die einzige Änderung besteht darin, dass wir anstelle des Aufrufs von drawScene eine andere Funktion aufzurufen, um die Szene zu zeichnen. Dies ist die Funktion, die regelmäßig aufgerufen werden muss. Sie aktualisiert den Animationszustand der Szene, zeichnet die Szene und sorgt auch dafür, dass Sie in angemessener Zeit wieder aufgerufen wird. Es ist die nächste Funktion oben in der Datei, also schauen wir es uns als nächstes an.
Die erste Zeile beginnt an der Stelle, wo das Häkchen verwendet wird erneut aufgerufen zu werden, wenn als nächstes ein Neulackieren erforderlich ist. RequestAnimFrame ist eine Funktion in irgendeinem von Google bereitgestellten Code, den wir in diese Webseite mit einem script-Tag oben in die Datei webgl-utils.js einbinden. Es existiert eine browserunabhängige Möglichkeit den Browser aufzurufen, wenn dieser die WebGL-Szene neu streichen möchte – zum Beispiel, wenn der Computerbildschirm das nächste mal aktualisiert wird. Im Moment gibt es in allen WebGL-unterstützenden Browsern Funktionen, die dies realisieren können, aber Sie haben unterschiedliche Bezeichnungen dafür. Zukünftig wird erwartetm dass Sie alle nur RequestAnimationFrame verwenden. Bis dahin können wir die Google WebGL Utils nutzen, um nur einen Call zu haben, der überall funktioniert.
Es ist zum Erwähnen wert, dass Sie einen ähnlichen Effekt wie bei der Verwendung von RequestAnimFrame erzielen können, indem Sie JavaScript mitteilen, die DrawScene-Funktion regelmäßig aufzurufen z.B. durch die Verwendung der integrierten JavaScript-Funktion setInterval. Ein Großteil früherer WebGL-Codeschnipsel tut genau dies und funktioniert auch so lange bis Nutzer mehrere WebGL-Seiten in verschiedenen Browser-Tabs öffnen. Naturgemäß war dies eine schlechte Sache und das war auch der Grund dafür, dass RequestAnimFrame eingeführt wurde. Funktionen, die mit eingeplant wurden, werden nur dann aufgerufen, wenn die Registerkarte sichtbar ist.
Betrachten wir die drawScene und animieren wir die Funktionen nacheinander. Wir müssen beachten, dass wir kurz vor der Funktionsdeklaration zwei neue globale Variablen definieren.
Diese werden verwendet, um die Drehung des Dreiecks bzw. des Quadrats zu verfolgen. Beide sind zu Beginn um 0 Grad gedreht und dann werden die Zahlen mit der Zeit zunehmen und Sie werden sich immer mehr drehen.
Die nächste Änderung in drawScene kommt an den Punkt, an dem wir das Dreieck zeichnen. Der Code wird in seinem gesamten Kontext abgebildet, die neuen Linien sind die Roten:
Damit wir den Code verstehen können, erinnern wir uns an den ersten Teil dieser Grundlagenserie.
Wenn Sie in OpenGL eine Szene zeichnen, teilen Sie ihr mit, dass Sie jede Sache, die Sie an einer aktuellen Position zeichnen mit einer aktuellen Rotation zeichnen soll wie z.B. „20 Einheiten vorwärts bewegen, 32 Grad drehen, dann den Roboter zeichnen“, wobei das letzte Bit ein komplexes Set von „so viel bewegen, ein wenig drehen, das zeichnen“-Anweisungen in sich selbst ist. Dies ist nützlich, weil Sie den Code „Draw the Robot“ in einer Funktion kapseln können und dann den Roboter einfach umherbewegen können, indem Sie einfach das Verschieben/Drehen-Material ändern, das Sie vor den Aufruf dieser Funktion machen.
Sie werden sich erinnern, dass dieser aktuelle Zustand in einer Model-View-Matrix gespeichert ist. Angesichts all dessen ist der Zweck des Aufrufs:
Eine Sache wird klar. Wir ändern unseren aktuellen Rotationszustand, wie dieser in der Model-View.Matrix gespeichert ist, indem wir uns um rTri Grad um die vertikale Achse drehen. Das heisst, wenn das Dreieck gezeichnet wird, wird es um rTri Grad gedreht. Beachten Sie, dass mat4.rotation Winkel in Bogenmaß nimmt. Für viele Nutzer ist häufig Grad einfacher zu behandeln. Deshalb wurde hier eine einfache Funktion zur Konvertierung degToRad geschrieben, die hier verwendet werden kann.
Nun, was ist aber mit dem Aufrufen von mvPushMatrix und mvPopMatrix? Wie man es von den Funktionsnamen schon entnehmen kann, sind Sie auch mit der Model-View-Matrix verwandt. Um auf mein Beispiel für das Zeichnen eines Roboters zurückzukommen, nehmen wir an, dass ihr Code auf der höchsten Ebene zu Punkt A gehen muss. Anschließend den Roboter zeichnet, dann gehen Sie zum Offset von Punkt A und zeichnen eine Teekanne. Der Code, welcher den Roboter zeichnet, kann alle möglichen Änderungen an der Matrix der Model-View-Matrix vornehmen. Dieser könnte mit einem Körper beginnen, dann anschließend die Beine, dann oben mit dem Kopf und abschließend mit den Armen enden. Problematisch ist, dass wenn Sie danach versucht haben, sich zu einem Offset zu bewegen, sich nicht relativ zu Punkt A bewegen würden, sondern relativ zu dem, was Sie zuletzt gezeichnet haben. Das bedeutet im konkreten Anwendungsfall, wenn ihr Roboter seine Arme hebt, die Teekanne anfangen würde zu schweben. Das ist nichts Gutes.
Offensichtlich ist es notwendig den Zustand der Model-View-Matrix zu speichern, bevor man mit dem zeichnen des Roboters beginnt, um ihn anschließend wiederherzustellen. Das ist natürlich das, was mvPushMatrix und mvPopMatrix tun. MvPushMatrix legt die Matrix auf einen Stack und mvPopMatrix entfernt die aktuelle Matrix, nimmt eine von der Spitze des Stapels und stellt Sie wieder her. Die Verwendung eines Stapels bedeutet, dass wir eine beliebige Anzahl von Bits verschachtelten Zeichencodes haben können, die jeweils die Matrix der Modellansicht manipulieren und anschließend wiederherstellen. Sobald wir also unser gedrehtes Dreieck fertiggezeichnet haben, stellen wir die Model-View-Matrix mit mvPopMatrix wieder her, so dass folgender Code resultiert:
… bewegt sich in einem nicht gedrehten Bezugsrahmen über die Szene.
Diese drei Änderungen bewirken also, dass sich das Dreieck um die vertikale Achse durch sein Zentrum dreht, ohne das Quadrat zu beeinflussen. Es existieren drei ähnliche Linien, um das Quadrat um die horizontale Achse durch sein Zentrum rotieren zu lassen.
… und das sind alle Änderungen am drawing code in drawScene.
Offensichtlich ist die andere Sache, die wir tun müssen, um unsere Szene zu animieren, die Werte von rTri und rSquare im Laufe der Zeit zu ändern, so dass jedes Mal, wenn die Szene gezeichnet wird, sie etwas anders ist. Dies erfolgt in unserer neuen Animationsfunktion wie folgt:
Eine einfache Möglichkeit eine Szene zu animieren wäre unser Dreieck und unser Quadrat einfach um einen festen Wert zu drehen und zwar jedes Mal, wenn animate aufgerufen wurde. An dieser Stelle haben wir uns dafür entschieden, eine etwas bessere Lösung umzusetzen. Der Wert, um denen die Objekte bewegt werden, wird davon bestimmt, wie lange es her ist seit die Funktion zuletzt aufgerufen wurde. Konkret dreht sich das Dreieck um 90 Grad pro Sekunde, das Quadrat hingegen um 75 Grad pro Sekunde. Das Schöne daran ist, dass jeder die gleiche Bewegungsgeschwindigkeit in der Szene sieht und unabhängig davon, wie schnell seine Maschine ist. Leute mit langsameren Maschinen sehen einfach ruckartige Bilder. Das ist für eine einfache Demo wie diese nicht so wichtig, aber es kann natürlich auch ein größerer Deal mit Spielen und dergleichen sein.
Das ist also der ganze Code, welcher die Szene animiert und zeichnet. Schauen wir uns noch den zusätzlichen Code für mvPushMatrix und mvPopMatrix an:
Dort sollte es keine Überraschungen geben. Wir haben eine Liste um unseren Stapel von Matrizen zu halten und Push und Pop entsprechend zu definieren.
Es gibt nur noch eine Sache zu erklären – die degToRad_Funktion, die bereits erwähnt wurde. Wenn Sie sich an den Mathematik-Unterricht an ihrer Schule erinnern wird es keine Überraschungen geben…
Wir haben unserem dritten Teil hiermit abgeschlossen. Es gibt keine Änderungen mehr, die es zu durchlaufen gilt. Jetzt wissen Sie, wie man einfache WebGL-Szenen animiert.