Babylon.js ist ein JavaScript-Framework zum Erstellen von 3D-Spielen mit HTML 5, WebGL und Web Audio, das vom Babylon.js-Entwicklerteam entwickelt wurde. Um die neue Version 2.3 der Bibliothek zu feiern, wurde beschlossen, eine neue Demo mit der Bezeichnung „Sponza“ zu erstellen, um zu zeigen, was mit der WebGL-Engine und HTML5 gemacht werden kann, wenn es darum geht, großartige Spiele zu entwickeln.

Babylon.js plattformübergreifendes WebGL-Spiel

Die Idee dahinter war, eine konsistente, ähnliche, wenn auch nicht identische Erfahrung auf allen von WebGL unterstützten Plattformen zu schaffen und zu versuchen, die Funktionen der nativen Apps zu erreichen. In diesem Artikel werden wir erklären, wie alles zusammenarbeitet, zusammen mit den verschiedenen Herausforderungen, mit denen die Entwickler konfrontiert waren.

Um dieses Ziel zu erreichen, verwendet Sponza eine Reihe von HTML5-Funktionen wie WebGL, Web Audio sowie Pointer Events (die jetzt dank jQuery PEP polyfill weitestgehend unterstützt werden), Gamepad API, IndexedDB, HTML5 AppCache, CSS3 Transition/Animation, Flexbox und Fullscreen API. Sie können die Sponza-Demo auf ihrem Desktop-PC, Smartphone oder der Xbox One testen.

Entdecken Sie die Demo.

Zuerst beginnen Sie mit einer automatisch animierten Sequenz, die demjenigen, der die Szene erstellt hat, die Ehre gibt. Die meisten Mitglieder des Teams kommen aus der Demo-Szene. Sie werden feststellen, dass dies ein wichtiger Teil der 3D-Entwicklerkultur ist.

Folgende Personen haben an der Entwicklung von Sponza mitgewirkt:

  • Michel Rousseau aka „Mitch“ hat bemerkenswerte visuelle Animationen und Rendering-Optimierungen als 3D-Künstler durchgeführt. Es brauchte das Sponza-Modell, das Crytek kostenlos auf seiner Website zur Verfügung stellte und nutzte den 3ds Max-Exporter, um das zu generieren, was man sieht.
  • David Catuhe alias „deltakosh“ und David Rousset haben den Kernteil der Babylon.js-Engine und auch den gesamten Code für die Demo (Custom Loader, Spezialffekte etc.) sowie einen neuen Kameratyp mit der Bezeichnung „UniversalCamera“ entwickelt, der alle Arten von Eingaben generisch verarbeitet.
  • David Rousset hat die Musik mit Unterstützung der Soundbank von EastWest Symphonic Orchestra komponiert.
  • Julien Moreau-Mathis half bei der Entwicklung eines neuen Tools, mit dem 3D-Künstler die Arbeit zwischen den Modellierungswerkzeugen (3ds Max, Blender) und dem Endergebnis abschließen können. Michel Rousseau nutzte es zum Beispiel, um verschiedene animierte Kameras zu testen und abzustimmen und Partikel in die Szene zu injizieren.

Wenn Sie bei der automatischen Sequenz bis zum „epischen Ende“ warten, werden Sie automatisch in den interaktiven Modus geschaltet. Wenn Sie den Demo-Modus umgehen möchten, klicken Sie einfach auf das Kamerasymbol oder drücken Sie A auf ihrem GamePad.

Im interaktiven Modus, wenn Sie einen Mac-PC nutzen, können Sie sich innerhalb der Szene mit Tastatur/Maus wie in einem FPS-Spiel bewegen. Wenn Sie ein Smartphone nutzen, können Sie sich mit einer einzigen Berührung bewegen. Schließlich können Sie auf einer Xbox das GamePad verwenden (oder den Desktop, wenn Sie ein GamePad anschließen). Kleine Anmerkung: Auf einem Windows-Touch-PC können Sie potenziell 3 Arten von Eingaben gleichzeitig verwenden.

Im interaktiven Modus ist die Atmosphäre anders. Sie haben drei Sturm-Audiodateien, die zufällig in der 3D-Umgebung positioniert sind, Wind und Bandriss an jeder Ecke. In unterstützten Browsern (Chrome, Firefox, Opera und Safari) können Sie sogar zwischen dem normalen Lautsprecher-Modus und dem Kopfhörermodus wechseln, indem Sie auf das entsprechende Symbol klicken. Anschließend wird das binaurale Audio-Rendering von Web Audio für eine realistischere Audiosimulation genutzt – wenn Sie über Kopfhörer hören.

Um ein komplettes App-ähnliches Erlebnis zu bieten, wurden Symbole und Kacheln für alle Plattformen generiert. Das bedeutet zum Beispiel, dass Sie unter Windows 8/10 die Web App in das Startmenü stecken können.

Offline geht vor.

Sobald die Demo vollständig geladen ist, können Sie ihr Smartphone in den Flugzeugmodus schalten, um die Verbindung zu unterbrechen und auf das Sponzy-Symbol klicken. Die Web-App wird weiterhin das komplette Erlebnis mit WebGL-Rendering, 3D-Web-Audio und Touch-Support bieten. Schalten Sie es auf Vollbild um und Sie werden den Unterschied zwischen der Demo und einer nativen App-Erfahrung buchstäblich nicht spüren können.

Sie können dafür den IndexedDB-Layer verwenden, welcher native in Babylon.js verfügbar ist. Die Szene (JSON-Format) und die Ressourcen (JPG/PNG-Texturen sowie MP3 für die Musik und Sounds) werden in IDB gespeichert. Die IDB-Layer in Verbindung mit dem HTML5-Anwendungscache sorgt dann für das Offline-Erlebnis.

Die gleiche Demo funktioniert fehlerfrei in MS Edge auf ihrer Xbox One.

Die gleiche Codebasis funktioniert unter Mac, Linus, Windows auf MS Edge, Chrome, Firefox, Opera und Safari, auf iPhone/iPad, auf Android-Geräten mit Chrome oder Firefox, Firefox OSA und auf Xbox One. Sie können so viele Geräte mit einem vollwertigen nativen Erlebnis direkt von ihrem Webserver aus ansprechen.

Hacken Sie die Szene mit dem Debug-Layer.

Wenn Sie verstehen möchten, wie Michel Rousseau die Magie der 3D-Modellierung beherrscht, können Sie die Szene mit dem Babylon.js Debug Layer Tool hacken. Um es auf einer Engine mit Tastatur zu aktivieren, drücken Sie CMD/CTRL + Shift + D und wenn Sie ein GamePad auf einem PC oder einer Xbox verwenden, drücken Sie Y. Bitte beachten Sie, dass die Anzeige des Debug-Layers aufgrund der Kompositionsaufgabe, die die Rendering-Engine erledigen muss, ein wenig Leistung kostet. Die angezeigten FPS sind also erwas weniger wichtig als die echten FPS, die Sie ohne den angezeigten Debug-Layer haben.

Herausforderungen.

Auf dem ganzen Weg waren die Entwickler mit einer Reihe von Problemen und Herausforderungen konfrontiert, um die Demo zu erstellen. Lassen Sie uns im Folgenden einige von ihnen etwas detaillierter betrachten.

WebGL-Performance und plattformübergreifende Kompatibilität.

Die Programmierseite war wahrscheinlich die am einfachsten zu bewältigende, da sie vollständig von der Babylon.js-Engine selbst übernommen wird. Die Entwickler verwendeten eine benutzerdefinierte Shader-Architektur, die sich an die Plattform anpasst, indem wir versuchen, anhand verschiedener Fallbacks den besten Shader für den aktuellen Browser/GPU zu finden. Die Idee ist, die Qualität und Komplexität der Rendering-Engine zu verringern, bis es uns gelingt, etwas Sinnvolles auf dem Bildschirm darzustellen.

Babylon.js basiert hauptsächlich auf WebGL 1.0, um zu gewährleisten, dass die darauf aufbauenden 3D-Erlebnisse nahezu überall laufen. Es wurde mit Blick auf die Web-Philosophie entwickelt, daher wird der Shader-Kompilierungsprozess schrittweise verbessert werden. Dies ist für den 3D-Künstler, der sich mit diesen Komplexitäten nicht die meiste Zeit beschäftigen möchte, völlig transparent.

Dennoch spielt der 3D-Künstler eine sehr wichtige Rolle bei der Leistungsoptimierung. Dieser muss die Plattform und die unterstützenden Funktionen und Einschränkungen kennen. Sie können keine Assets aus AAA-Spielen für High-End-Grafikprozessoren und DirectX 12 in ein Spiel integrieren, das auf einer WebGL-Engine läuft. Es wird häufig argumentiert, dass das Targeting von WebGL heute ganz ähnlich ist wie die Arbeit, die Sie leisten müssen, um für Erlebnisse auf mobilen Geräten zu optimieren, mit einem Hauch von zusätzlichem JavaScript, das in hohem Maße einfädig sein muss.

Mitch ist in Folgendes sehr gut: Optimierung der Texturen, Vorberechnung des Lightings, um ihn in die Texturen einzubringen, Reduzierung der Anzahl der Draw Calls etc. Er verfügt über jahrelange Erfahrung und sah die verschiedenen Generationen von 3D-Hardware und Engines (von PowerVR/3DFX bis hin zu heutigen GPUs), die wirklich dazu beigetragen haben, die Demo zu realisieren.

Das erste Ziel für Sponza war es, zwei Szenen zu bauen. Eine für den Desktop und eine für das Smartphone mit weniger Komplexität, kleineren Texturen, einfacheren Meshes und Geometrien. Aber bei entsprechenden Test wurde festgestellt, dass die Desktop-Version auch auf mobilen Endgeräten ziemlich gut läuft, da sie auf einem iPhone 6s oder einem Android PlusOne 2 bis zu 60fps laufen kann. Es wurde dann der Entschluss gefasst, nicht mehr an der einfacheren mobilen Version zu arbeiten.

Auch hier wäre es wahrscheinlich besser gewesen, einen sauberen Mobile-First-Ansatz auf Sponza zu haben, um 30fps auf vielen mobilen Geräten zu erreichen und dann die Szene für High-End-Mobile und Desktop zu verbessern. Dennoch scheint das meiste Feedback, das via Twitter eingegangen ist, darauf hinzudeuten, dass das Endergebnis auf den meisten Geräten gut funktioniert. Zugegebenermaßen wurde Sponza auf eine HD4000 GPU (Intel Core i5 integriert) optimiert, die mehr oder weniger den tatsächlichen GPUs von High-End-Smartphones entspricht.

Die Leistung ist sehr zufriedenstellend, die erreicht wurde. Sponza verwendet einen Shader mit Ambient, Diffuse, Bump, Specular und Reflection. Es existieren einige Partikel, um kleine Brände an jeder Ecke zu simulieren, animierte Bones für die roten Flaggen, 3D positionierte Geräusche und Kollisionen, wenn Sie sich im interaktiven Modus bewegen.

Technisch gesehen wurden 98 Meshes, die in der Szene verwendet wurden und bis zu 377782 Nodes erzeugt, 16 aktive Bones, 60+ Partikel, die bis zu 36 Draw Calls erzeugen können. Eines ist dabei ganz sicher: Weniger Draw Calls sind der Schlüssel zu einer optimalen Performance, vor allem im Web.

Der Loader.

Für Sponza wollte das Team einen neuen Loader entwickeln, der sich von dem Standard-Loader unterscheidet, der auf der Babylon.js verwendet wurde, um eine saubere und ausgefeilte Web-App zu erhalten. Michel wurde anschließend gebeten, etwas Neues vorzuschlagen.

Lassen Sie uns zuerst über die Hintergründe sprechen. Der von Michel erzeugte verschwommene Effekt war schön, funktionierte aber nicht über alle Fenstergrößen und Auflösungen hinweg und erzeugte ein wenig Moire. Es wurde in der Folge durch einen „klassischen“ Screenshot der Szene ersetzt. Ziel war es jedoch, dass der Hintergrund den Bildschirm völlig ausfüllt, ohne schwarze Balken und ohne das Bild zu dehnen, um das Verhältnis zu brechen.

Die Lösung kommt hauptsächlich von der CSS-Hintergrundgröße: Cover + Zentrierung des Bildes auf X- und Y-Achse. Als Ergebnis resultierte das gewünschte Ergebnis, unabhängig vom verwendeten Bildformat.

Die anderen Teile nutzen die gute alte prozentuale CSS-Positionierung. Nun stellt sich natürlich die Frage nach der Typographie, um die Schriftgröße, welche auf der Größe des Ansichtsfensters basieren. Natürlich können wir dafür Viewport-Einheiten verwenden. Vw und vh (wobei 1vw 1% der Viewport-Breite und 1 vh 1% der Viewport-Höhe ist) werden von allen Browsern, insbesondere in allen WebGL-kompatiblen Browsern, ziemlich gut unterstützt.

Schließlich spielen wir mit der Opacity Property des Hintergrundbildes, um es von 0 auf 1 zu verschieben, basierend auf dem aktuellen Download-Prozess, der von 0 auf 100% wechselt.

Die Textanimationen werden übrigens einfach mit CSS-Übergängen oder Animationen kombiniert mit einem Flexbox-Layout erstellt, um eine einfache, aber effiziente Möglichkeit zu haben, diese in der Mitte oder an jeder Ecke anzuzeigen.

Alle Eingaben transparent handhaben.

Die WebGL-Engine erledigt die gesamte Arbeit auf der Rendering-Seite, um die Visuals auf allen Plattformen korrekt darzustellen. Aber wie kann garantiert werden, dass sich der Benutzer innerhalb der Szene bewegen kann, unabhängig vom verwendeten Input-Typ.

In der vorherigen Version von Babylon.js wurden alle Arten von Eingaben und Benutzerinteraktionen unterstützt: Tastatur/Maus, Touch, virtuelle Touch-Joysticks, Gamepad, Geräteorientierung VR und WebVR.

Touch wird universell verwaltet, wobei die Pointer Events-Spezifikation über den jQuery PEP-Polyfill auf allen Plattformen übertragen wird.

Zurück zur Demo. Die Idee für Sponza war, eine einzigartige Kamera zu haben, die alle Benutzerszenarien auf einmal abdeckt: Desktop, Smartphone und Konsole.

Am Ende wurde die UniversalCamera entwickelt. Die Entwicklung gestaltete sich sehr einfach. Die UniversalCamera ist mehr oder weniger eine GamePad-Kamera, die die TouchCamera und FreeCamera erweitert.

Die FreeCamera stellt die Tastatur/Maus-Logik zur Verfügung, die TouchCamera die Touch-Logik und die letzte Erweiterung die GamePad-Logik.

Die UniversalCamera wird nun standardmäßig in Babylon.js verwendet. Wenn Sie durch die Demos blättern, können Sie sich innerhalb der Szene mit Maus, Touch und GamePad auf allen bewegen.

Synchonisierung der Übrgänge mit Musik.

Dieser Bereich hat die meisten Fragen aufgeworfen. Sie haben vielleicht bemerkt, dass die Einfführungssequenz mit bestimmten Bereichen des Musik-Playbacks synchronisiert ist. Die ersten Zeilen werden angezeigt, wenn einige der Drums einsetzen und die letzte Endsequenz ist das schnelle Umschalten von einer Kamera zur anderen auf jeder Note des verwendeten Horninstruments.

Die Synchronisation von Audio mit der WebGL-Rendering-Schleife ist nicht einfach. Auch dies ist die Mono-Thread-Natur von JavaScript, die diese Komplexität erzeugt. Es ist wirklich wichtig, das Problem zu verstehen, um das globale Problem zu verstehen, mit dem User konfrontiert werden. Wir möchten aber hier nicht weiter in die Tiefe gehen, weil das den Rahmen sprengen würde.

Normalerweise werden Sie in Demo-Szenen, wenn Sie die Visuals mit den Sounds/Musik synchronisieren möchten, von Audio-Stack gesteuert. Häufig werden zwei Ansätze verwendet:

  1. Erzeugen Sie Metadaten, die in die Audiodateien eingefügt werden und die dann einige Ereignisse aufrufen können, wenn Sie einen bestimmten Teil davon erreichen.
  2. Echtzeit-Analyse des Audio-Streams über FFT oder ähnliche Technologien, um interessante Spitzen oder BPM-Änderungen zu erkennen, die wiederum Ereignisse für die Visual Engine erzeugen würden.

Diese Ansätze eignen sich besonders gut in Umgebungen wie C++. Aber in JavaScript, mit Web Audio, haben wir zwei Probleme:

  1. JavaScript, das einfädig ist und leider die meiste Zeit Webworker nicht wirklich helfen werden,
  2. Web Audio hat keine Ereignisse, die an den UI-Thread zurückgesendet werden könnten, selbst wenn Web Audio vom Browser auf einem separaten Thread behandelt wird.

Web Audio hat einen viel präziseren Timer als JavaScript. Es wäre fantastisch gewesen, diesen separaten Timer auf einem separaten Thread verwenden zu können, um die Ereignisse zurück zum UI-Thread zu bringen. Aber heute ist das (noch) nicht möglich.

Auf der anderen Seite rendern wir die Szene mit WebGL und der requestAnimationFrame-Methode. Das bedeutet, dass wir im „besten Fall“ ein 16ms Zeitfenster haben. Wenn einer fehlt, müssen Sie bis zu 16 ms warten, um auf den nächsten Frame reagieren zu können, um die Soundsynchronisation zu reflektieren (z.B. um einen „Fade-to-Black“ Effekt zu starten).

Es macht Sinn, die Synchronisationslogik in die requestAnimationFrame-Schleife einzubinden. Es wurde die Zeit seit Beginn der Sequenz studiert und die Möglichkeit untersucht, das Bildmaterial so anzupassen, dass es auf ein Audioereignis reagiert. Die gute Nachricht ist, dass Web Audio den Sound wiedergibt, egal was im Haupt-Thread der Benutzeroberfläche passiert. So können Sie beispielsweise sicher sein, dass ein 12s Zeitstempel der Musik genau 12s nach Beginn der Musikwiedergabe ankommt, auch wenn es dem Grafikprozessor schwer fällt, die Szene wiederzugeben.

Am Ende wurde schließlich die wahrscheinlich einfachste Lösung aller Zeiten gewählt: die Verwendung von setTimeout(). Als Entwickler werden Sie sicher wissen, dass diese Methode ziemlich unzuverlässig ist, aber im konkreten Anwendungsfall, ist uns nach dem Fertigstellen der Szene bekannt, dass alle Ressourcen (Texturen und Sounds) heruntergeladen und Shader zusammengestellt wurden. Wir sollten nicht allzu verärgert sein über unerwartete Ereignisse, die den UI-Thread sättigen. GC könnte ein Problem sein, aber es wurde viel Zeit damit verbracht, in der Engine dagegen anzukämpfen: Verringerung des Drucks auf den Garbage Collector durch Verwendung der F12-Entwicklerleiste des Internet Explorer 11.

Dennoch wissen wir, dass diese Lösung bei weitem nicht ideal ist. Das Wechseln zu einer anderen Registerkarte oder das Sperren ihres Smartphones und das Entsperren einige Sekunden später könnte einige Probleme im Synchronisierungsteil der Demo verursachen. Diese Probleme könnten durch die Verwendung der Page Visibility API behoben werden, z.B. durch das Anhalten der Rendering-Schleife, verschiedener Sounds und das Neuberechnen der nächsten Zeitfenster für die setTimeout()-Aufrufe.

Aber vielleicht wurde etwas verpasst. Wahrscheinlich existiert ein besserer Ansatz, um dieses Problem zu lösen.

Handhabung von Web Audio auf iOS.

Die letzte Herausforderung, ist die Art und Weise, wie Web Audio von iOS auf iPhone und iPad gehandhabt wird. Im Internet werden Sie viele Threads finden, indem Menschen Probleme haben Sounds auf iOS abzuspielen.

IOS hat eine bemerkenswerte Unterstützung von Web Audio – sogar den binauralen Audiomodus. Apple hat jedoch entschieden, dass eine Webseite standardmäßig keinen Sound ohne die Interaktion eines bestimmten Benutzers wiedergeben kann. Diese Entscheidung wurde wahrscheinlich getroffen, um zu vermeiden, dass Werbung oder etwas anderes den Benutzer durch das Abspielen unerwünschter Geräusche stört.

Was bedeutet das für Webentwickler? Sie müssen zuerst den Web-Audio-Kontext von iOS nach der Berührung eines Benutzers freischalten – bevor Sie versuchen, einen Sound abzuspielen. Andernfalls wird ihre Webanwendung stumm bleiben.

Leider besteht der einzige Weg darin, das zu tun, in der Überprüfung, ob durch die Durchführung eines Benutzerplattform-Sniffing-Ansatzes, da es keinen Weg zur Feature-Erkennung gibt, dies zu realisieren.

Es gibt einen Weg dies zu realisieren und zwar der sogenannte User-Platform-Sniffling-Ansatz. Die Entwickler konnten keinen anderen Weg finden, so dass sich diese mit dieser wenig zuverlässigen Lösung zufrieden geben mussten.

Wir hoffen, dass Sie einen paar Einblicke in die Entwicklung der Demo gewinnen konnten.

Vielen Dank für ihren Besuch.