WebGL - 3D im Browser

siehe auch WebGPU!
 Home
 WebGL Api Spickzettel
 WebGL Sicherheit

Tutorial
 0 : WebGL Browser
 1 : Das erste Dreieck
 2 : 3D-Mathematik
 3 : Farbe
 4 : Animation
 5 : Interaktion I
 6 : Texturen
 7 : Beleuchtung I
 8 : Interaktion II

Links
 WebGL Beispiele
 WebGL Frameworks
 ext. WebGL Tutorials


 Kontakt / Impressum
 webgl ([ät)] peter-strohm Punkt de

5 Interaktion I

>>>> Direkt zum Beispiel <<<<

5.1 Was bedeutet Interaktion?

Alles was wir mit WebGL in den vorangegangenen Kapiteln angestellt haben, hatte eines gemeinsam: der Anwender konnte sich zwar an der Darstellung erfreuen, aber ansonsten keinen Einfluss darauf nehmen. Im Prinzip sind bunte rotierende Dreiecke seit den Zeiten von animierten gifs möglich und noch lange nicht die Revolution von WebGL.Tutor In diesem Kapitel werden wir dem Anwender ermöglichen, die Darstellung der 3D-Szene zu beeinflussen. Ich habe es Interaktion I genannt, weil wir in diesem Kapitel nur mit der Ansicht der 3D-Szene interagieren werden. D.h. der Benutzer kann mit Maus und Tastatur die 3D-Szene in ihrer Darstellung drehen oder Verschieben. Dies ist ein großer Fortschritt im Vergleich zu rein passiven Darstellungen, allerdings wird es noch viel spannender, wenn wir in Interaktion II nicht nur mit der Ansicht, sondern mit den 3D-Objekten interagieren.

Nebenbei werde ich aus dem bisher verwendeten flachen Dreieck auch eine "echt 3dimensionale" Pyramide machen. Dieser Schritt bedeutet eigentlich nur, dass zwei weitere Raumpunkte hinzugefügt werden und statt einem nun 4 Dreiecke (4 Seitenflächen) verwendet werden und war mir deshalb kein eigenes Kapitel wert.
Hier kannst du dir das WebGL-Beispiel anschauen. Wie immer ist der Quellcode kommentiert.
Pyramide mit Pfeilen
Bild 5.1 : Die bewegbare Pyramide

Das Beispiel erinnert schon an bisschen an neumodische Produktpräsentationen, wie sie bereits mit Hilfe von Flash oder Java3D auf manchen Internetseiten zu sehen sind. Die Pyramide ist zwar schwer mit einer neuen Digitalkamera oder einem Automodell zu verwechseln, aber bis auf ein paar (texturierte) Dreiecke mehr oder weniger ist es die gleiche Applikation.

Da ich auf Rotation und Translation in den vorangegangenen Kapiteln bereits eingegangen bin, müssen wir uns hier nur noch mit den Benutzereingaben (Maus, Tastatur) und deren Verarbeitung beschäftigen. Im Vergleich zum letzten Kapitel (Animation) besteht der wesentliche Unterschied darin, WOMIT die Modelview-Matrix erstellt wird. Im Animationskapitel wurde sie kontinuierlich mit der Zeit (bzw. mit jedem neuen Bild) verändert. Jetzt interessiert uns die Zeit nicht, sondern nur, was der Anwender macht (Mausbewegungen, Tastdruck...). Vieles davon ist nicht WebGL-spezifisch, sondern JavaScript wie es leibt und lebt.

5.2 Navigationsmodi

Nur um Verwirrung auszuschließen: wenn ich hier von Navigation schreibe geht es nicht um Routenplanung wie TomTom, Garmin und Co. sie betreiben, sondern vielmehr um Arten der virtuellen Fortbewegung.
Wir werden hier die Navigationsart Examine (untersuchen) in das WebGL-Beispiel einbauen. Hier möchte ich kurz eine Übersicht geben, welche Navigationsarten sonst noch üblich sind:

Examine
"untersuchen"
Examine-Navigation
Der Examine-Modus wird üblicher weise bei einzelnen 3D-Objekten, die von mehreren Seiten betrachtet werden sollen, verwendet. Das 3D-Objekt ist mittig dargestellt (zentriert) und die Mausbewegung (mit gehaltener Taste) rotiert das Objekt UM DESSEN ZENTRUM. Zusätzlich wird das Mausrad häufig zum Zoomen verwendet. Typische Beispiele für diese 3D-Navigationsart sind CAD-Konstruktionsprogramme und Produktpräsentationen.

Walk
"gehen"
virtuelle Welt mit Walk-Navigation
Im Walk-Modus bewegt sich nicht ein einzelnes Objekt vor der virtuellen Kamera, sondern die Kamera bewegt sich durch die Welt. Rotationszentrum ist immer die virtuelle Kamera. Typische Beispiele sind virtuelle Welten a la Second Life und 3D-Egoshooter (bzw. "Weltrettungsspiele" wie ich sie wegen des schlechten Images gerne nenne ;-) ). Im Walk-Modus ist die virtuelle Kamera durch (mehr oder weniger realistisch) simulierte Schwerkraft mit dem Untergrund verbunden.

Fly
"fliegen"
virtuelle Welt mit Fly-Navigation
Der Fly-Modus bietet dem Anwender die größte Bewegungsfreiheit. Im Vergleich zum Walk-Modus ist die virtuelle Kamera nicht an den Untergrund gebunden und kann sich in alle Richtungen drehen und bewegen. Häufig findet die Bewegung der Kamera nur in Blickrichtung (oder entgegengesetzt dazu) statt. Typische Beispiele sind Flugsimulatoren und virtuelle Rundflüge (z.B. über ein in Planung befindliches Gebäude).

Kombinationen In den meisten Fällen genügt eine Art der Navigationen Examine/Walk/Fly. Es gibt jedoch mit Google-Earth (u.A.) ein prominentes Beispiel, das mehrere Arten kombiniert:
Beim Programmstart verwendet man i.d.R. den Examine-Modus um das 3D-Objekt (=die Erdkugel) so zu drehen wie man sie sehen möchte. Hat man dann ein best. Gebiet (z.B. Hamburg) nahe genug herangezoomt, kann man die Kamera kippen um einen perspektivischen Eindruck von Gebäuden und Landschaft zu bekommen. Dieser zweite Schritt ähnelt der Fly-Navigation, obwohl der Drehpunkt nicht die Kamera selbst, sondern ein Punkt auf der Erdoberfläche ist.
Tabelle 5.1 : Navigationsmodi

Welcher Navigationsmodus der richtige ist, hängt von der jeweiligen Anwendung ab. Es gibt in der Literatur noch ein paar Varianten der aufgelisteten Bewegungsarten:
Pan(verschieben der Kamera parallel zur Bildebene)
"Game-Like" (Walk-Modus, mit typischer w-a-s-d Tastaturbelegung)
u.A....

5.3 JavaScript Benutzereingaben (Events)

Schauen wir endlich mal wieder in den Quellcode des Beispiels:
Zeile 147
147    var canvas = document.getElementById("WebGL-canvas");
148    canvas.onmousedown=mouseDown;
149    canvas.onmouseup=mouseUp;
150    canvas.onmousemove=mouseMove;
151       canvas.addEventListener('DOMMouseScroll', mouseWheel, false);
152    document.addEventListener("keydown", keyDown, false);



Hier werden die für uns interessanten Benutzereingaben "Maustaste-gedrückt" (mousedown), "Maustaste-losgelassen" (mouseup), "Maus bewegt", "Mausrad gedreht" (mousewheel) und "Tastatur-Taste gedrückt" (keydown) jeweils mit einer Funktion verknüpft. Es wird der JavaScript-Engine mitgeteilt, welche (lokale) Funktion ausgeführt werden soll, wenn das jeweilige Ereignis auftritt. Diese Funktionsnamen ("mouseDown") sind frei wählbar und alle in der Beispieldatei kapitel5.html selbst implementiert.

Mausrad und Tastatur benötigen statt der einfachen Zuweisung die Installation eines Event-Listeners, das Ergebnis ist aber das gleiche. Warum und in welchen Versionen Istgleich-Zuweisungen oder EventListener verwendet werden müssen, überlasse ich der JavaScript-Literatur...Ich entwickele wie immer mit Minefield 3.7.

Was soll nun passieren wenn diese Events eintreten?

Der einfachste Fall ist das Mausrad: Wenn es einen Schritt vor gerollt wird, soll die Pyramide sich einen Schritt entfernen; wenn es zurück gerollt wird, soll die Pyramide näher kommen.
In WebGL-ausgedrückt heißt das: "der Translationswert in Z-Richtung in der Modelview-Matrix soll sich vergrößern bzw. verkleinern".
Und im Quellcode:

166function mouseWheel(wheelEvent) {
167   if(wheelEvent.detail>0)
168     g_fZZoomlevel += 0.3;
169else
170  g_fZZoomlevel -= 0.3;
171drawScene();
172}


Die globale Variable g_fZZoomlevel finden wir in der Funktion drawSzene in Zeile 132 wieder: Dort wird sie in Form einer temporären Translationsmatrix in die Modelview-Matrix hineinmultipliziert.
Die Zahlenwerte +/-0.3 sind natürlich frei gewählt und bestimmen die Schrittweite, die die Pyramide bei einem Schritt des Mausrades zurücklegt.
In der letzten Zeile von mouseWheel wird die Funktion drawSzene() aufgerufen um die Neuberechnung der 3D-Ansicht zu veranlassen und auf den Schirm zu bringen.

Etwas komplexer wird es bei den Events mouseDown, mouseUp und mouseMove. Wie du sicher schon ausprobiert hast, dreht sich die Pyramide nur, wenn die Maus bewegt wird während gleichzeitig eine Taste gedrückt gehalten wird (JavaScript unterscheidet nicht zwischen linker, rechter oder sonstiger Maustaste).
Der Reihe nach im Programmablauf:

Zuerst wird die Maustaste heruntergedrückt (und gehalten!):
185function mouseDown(einEvent) {
186  g_bMousedown=true;
187_DrawInterval = setInterval(drawScene,40);
188}


Mit SetInterval wird veranlasst, dass ab sofort alle 40 Millisekunden die 3D-Szene neu gezeichnet wird. Vorher wurde die Szene nur bei ersten Laden und beim Rollen des Mausrades berechnet und gezeichnet.
Die Funktion mouseMove wird in diesem Beispiel IMMER aufgerufen, wenn sich der Mauszeiger über die Canvas bewegt. Und zwar bei jedem Wechsel des Mauszeigers von einem Pixel zum nächsten. Bei einer Bewegung des Cursors von links nach rechts über die komplette Fläche wird mouseMove also 500(!) mal aufgerufen.

174function mouseMove(einEvent) {
175  if(g_bMousedown== true) {
176    g_fAnimationYAngle+=(einEvent.clientX-g_lastpoint.x);
177    g_fAnimationXAngle+=(einEvent.clientY-g_lastpoint.y);
178 //drawScene();
179  }
180  
181  g_lastpoint.x=einEvent.clientX;
182  g_lastpoint.y=einEvent.clientY;
183}


Wie du siehst, wird hier die globale Variable g_bMousdown, die wir in der Funktion mouseDown auf true gesetzt haben verwendet.
Nur wenn die Maustaste gedrückt ist, werden die neuen Rotationswinkel um X und Y berechnet. Andernfalls wird nur die Position des Mauszeigers gespeichert.
Beachte das drawSzene() NICHT explizit aufgerufen wird. Stattdessen haben wir ja das 40-Sekunden-Interval, das wir in mouseDown gestartet haben. Der Grund für dieses Konstrukt ist, dass mouseMove bei schnellen Mausbewegungen SEHR häufig aufgerufen wird und so häufige Neuzeichnen nur unnötige Prozessorlast verursachen würde.
Die beiden in mouseMove veränderten Rotationswinkel fließen in drawSzene in die Modelview-Matrix ein (Zeilen 135- 138).
Wenn du bis hierhin alles verstanden hast, sollte die Funktion mouseUp keine Überaschungen für dich bringen:

190function mouseUp(einEvent) {
191  g_bMousedown=false;
192learInterval(g_DrawInterval);
193}


Durch das Rücksetzen von g_bMousedown auf false wir bei folgenden MouseMove-Events keine Rotationswinkeländerung berechnet. Das zyklische Zeichnen der Szene wird mit clearInterval wieder gestoppt. Dadurch sollte auch die CPU-Last, die WebGL/Minefield verursacht wieder in die Nähe von 0% zurückgehen.

In dem WebGL-Beispiel habe ich der Vollständigkeit halber noch Tastaturabfragen zum seitlichen Verschieben der Pyramide eingebaut. Das Prinzip ist genau das gleiche wie beim Mausrad, nur dass es statt 2 Optionen (vorwärts und rückwärts) hier vier Optionen (hoch, runter, rechts, links) gibt

195function keyDown(einEvent) {
196    switch(einEvent.keyCode)    {
197
198ase 37: // cursor links
199 g_fTranslatX-=0.2;
200reak;
201ase 38: // cursor hoch
202 g_fTranslatY+=0.2;
203reak;
204ase 39: // cursor rechts
205 g_fTranslatX+=0.2;
206reak;
207ase 40: // cursor runter
208 g_fTranslatY-=0.2;
209reak;
210efault:
211reak;
212
213rawScene();
214}


Die Cursor-Zahlencodes (37,38,39,40) findest du in jeder ASCII-Tabelle. Mit diesem Beispiel kannst du natürlich auch analog (fast) alle anderen Tasten verarbeiten und statt der Translation um 0.2 in X und Y jede andere Operation ausführen lassen.

Das soll es zum Thema Interaktion I (Ansicht) gewesen sein. Im nächsten Kapitel werde ich mich dem Thema Texturen zuwenden. Es bleibt spannend !

<< Kapitel 4 <<    >> Startseite <<   ^ Seitenanfang ^     >> Kapitel 6 >>
Fehler? Kommentare? webgl ([ät)] peter-strohm Punkt de