In dem folgenden Beitrag möchten wir die Verwendung der JavaScript-Library React in Kombination mit Three.js untersuchen. Wir verwenden Three.js seit einigen Jahren in unseren Projekten und haben erwartet, dass der Einsatz von React die Codequalität in 3D-Projekten erheblich verbessern könnte.
Derzeit gibt es zwei Bibliotheken, die React-Bindings für Three.js bereitstellen. Dieser Beitrag wird ihre Unterschiede anhand von Arbeitsbeispielen untersuchen. Wir hoffen, dass es ihnen hilft, zu entscheiden, was besser für ihre Bedürfnisse geeignet ist.
React.
React hat sich zu einer beliebten Wahl für die Erstellung von Benutzeroberflächen entwickelt. React behält ein virtuelles DOM und Änderungen in der Benutzeroberfläche werden zuerst auf dieses virtuelle DOM angewendet. Dann berechnet React den minimalen Satz von Änderungen, die erforderlich sind, um das reale DOM an das virtuelle DOM anzupassen. Dieser Prozess wird als Reconciliation bezeichnet. Da DOM-Operationen teuer sind, ist der Leistungsvorteil von React erheblich.
Aber es gibt noch mehr erwähnenswertes als die Auswirkungen auf die Leistung. Insbesondere in Kombination mit Flux, JSX und den Debug-Tools für den Browser ist es eine sehr leistungsfähige und dennoch einfach zu bedienende Bibliothek zur Erstellung komplexer UI`s mit wiederverwendbaren Komponenten.
Wo React letztendlich HTML erzeugt, das vom Browser gerendert wird, gibt es eine wachsende Anzahl von Bibliotheken, die React-Bindings für Bibliotheken bereitstellen, die auf das Canvas-Element rendern, wie D3.js, Flipboard und Chart.js. Es gibt auch Bindings für SVG und ein weiteres interessantes Experiment ist gl-react.
React und Three.js
Für Three.js gibt es zwei Bibliotheken, die React-Bindings bereitstellen:
- react-three
- react-three-renderer
Three.js hält eine virtuelle 3D-Szene im Speicher, die bei jedem Aufruf der Rendermethode in den WebGL-Kontext des Canvas-Elements übertragen wird. Die Render-Methode räumt das Canvas vollständig aus und erstellt die gesamte Szene neu, auch wenn sich nichts geändert hat.
Daher haben wir nichts zu gewinnen, wenn wir React mit Three.js verwenden, aber es gibt immer noch ausreichend Gründe, es zu verwenden. React ermutigt Sie, Komponenten zu erstellen und den Zustand so weit wie möglich aus den Komponenten herauszubewegen, was zu sauberem, besserem Code führt und die JSX-Notation gibt ihnen einen sehr klaren Überblick über die hierarchische Struktur der Komponenten in ihrer 3D-Szene, wie wir in den Codebeispielen im nächsten Kapitel sehen werden.
Zwei Bibliotheken im Vergleich.
React-three ist in es5 geschrieben, react-three-renderer ist neuer und in es6 geschrieben. Die folgenden Codebeispiele, die beide einen einfachen Würfel erzeugen, zeigen uns die Unterschiede zwischen den Bibliotheken. Betrachten wir zunächst react-three:
Und jetzt das Gleiche bei react-three-renderer:
Wir sehen zwei offensichtliche Unterschiede:
- In react-three importieren wir ein Objekt und dieses Objekt enthält alle verfügbaren Komponenten. Wir haben den Komponenten den gleichen Namen wie den Eigenschaften des importierten Objekts gegeben, aber wir hätten jeden beliebigen Namen verwenden können. Die Namenskonvention in React befiehlt uns, benutzerdefinierte Komponenten zu schreiben, die mit einem Großbuchstaben beginnen, dem ich freiwillig gefolgt bin. Im react-three-renderer importieren wir eine Komponente und die verfügbaren Komponenten sind innerhalb dieser Komponente bekannt. Dies liegt daran, dass der react-three-renderer interne Komponenten verwendet, ähnlich wie div, span und so weiter. Beachten Sie, dass die Namen der Komponenten mit Kleinbuchstaben beginnen.
- In react-three sind die Eigenschaftsgeometrie und das Material der Mesh-Komponente Instanzen der entsprechenden Three.js-Klassen, während im react-three-renderer sowohl die Geometrie als auch das Material Komponenten sind. React-three hat nur 17 Komponenten, aber react-three-renderer strebt danach, Komponenten für jede (relevante) Three.js-Klasse zu erstellen und erhält so eine höhere Granularität.
Komponenten erzeugen.
Das folgende Beispiel ist ein Minecraft-Zeichenkonfigurator, mit dem wir die Größe aller Würfel, aus denen der Charakter besteht, ändern können.
Es zeigt ihnen, wie einfach es ist, 3D-Komponenten mit beiden Bibliotheken zu erstellen und wie ihr Code von der Verwendung von React profitiert, sowohl im Hinblick auf eine gute Organisation als auch auf die Wartbarkeit.
Der Code der Hauptkomponente sieht wie folgt aus:
Zuerst erstellen wir einen Abschnitt, der alle Controls enthält, dann erstellen wir die Szenengrafik mit einer Ebene (World), auf der der Minecraft-Charakter platziert wird. Wie Sie sehen können, ist der gesamte Code, der für den Minecraft-Charakter spezifisch ist, ih seiner eigenen Komponente versteckt, so dass die hierarchische Struktur trotz ihrer Komplexität sehr klar bleibt.
Wenn wir uns den Code der Minecraft-Charakterkomponente ansehen, sehen wir, wie viel Komplexität tatsächlich abstrahiert wird:
Hier sehen wir eine Komponente mit der Bezeichnung „Box“, die ein Wrapper-Code um einen Cube ist. Durch die Verwendung diese Komponente reduzieren wir nicht nur die Codemenge im Minecraft-Zeichenmodul, sondern abstrahieren auch Unterschiede zwischen den beiden Bibliotheken.
Das bedeutet, dass wir die Minecraft-Zeichenkomponente sowohl in Projekten, die react-three verwenden, als auch in Projekten, die react-three-renderer verwenden, nutzen können.
Modelle importieren.
Die Modell-Loader für Three.js laden die verschiedenen 3D-Formate (Collada, FBX, Obj, JSON etc.) und analysieren sie in Three.js-Objekte, die sofort in der Szene hinzugefügt werden können. Dies ist sehr praktisch, wenn Sie Three.js ohne React-Bindings verwenden, aber es erfordert einen zusätzlichen Conversion-Schritt, wenn wir React-Bindings verwenden, da wir das Three.js-Objekt in Komponenten zerlegen müssen.
Die Utility ist ein Parser und ein Loader in einem und so verwendet man es:
Nachdem das Modell geladen wurde, wird es rechts weg geparst. Während des Parsing-Schrittes wird eine Map mit allen Geometrien erstellt. Alle diese Geometrien werden ebenfalls zu einer einzigen großen Geometrie zusammengeführt und für diese zusammengeführte Geometrie wird ein Multi-Material erzeugt.
Jetzt können wir es in einer React-Komponente verwenden. In react-three sieht es folgendermaßen aus:
In react-three-renderer benötigen wir mehr Code, einerseits weil Multi-Materialien (noch) nicht unterstützt werden, so dass wir die zusammengeführte Geometrie nicht verwenden können und andererseits wegen ihrer höheren Granularität.
Vor- und Nachteile.
Die Verwendung von React-Bindings für Three.js führt zu sehr sauberem Code. Normalerweise haben Sie keinen hierarchischen Überblick über ihre 3D-Szene, aber mit React ist ihre Szene in einem Baum von Komponenten übersichtlich dargestellt. Als Bonus können Sie ihre Szene mit dem React-Browser-Tool debuggen.
Wie wir im Minecraft-Konfigurator gesehen haben, ist die Verwendung von React sehr effizient für Anwendungen, die Verbundkomponenten verwenden und wir haben gesehen, wie reibungslos React GUI-Controls mit einer 3D-Szene verbunden werden können.
In Anwendungen mit flacher Struktur, z.B. wenn Sie viele 3D-Objekte auf der Szene platziert haben, wird der JSX-Code ihrer Szenengrafik lediglich zu einer langen Liste, die genauso schwer zu verstehen sein könnte wie die ursprüngliche Three.js-Darstellung der Szenengrafik.
Mit React können Sie jedoch eine so lange Liste im Handumdrehen aufteilen, z.B. durch Kategorisierung der 3D-Objekte:
Manchmal erfordert die Verwendung von React einige zusätzliche Schritte z.B. beim Laden von 3D-Modellen und manchmal kann es etwas dauern, bis man den richtigen Weg gefunden hat, um die gängige Three.js-Funktionalität zu implementieren, wie z.B. Benutzerkontrollen oder manuellen Aufruf der Three.js eigenen Rendermethode.
Um auf das letztgenannte Beispiel näher einzugehen: Standardmäßig rufen sowohl react-three- als auch react-three-renderer die Renderfunktion von Three.js kontinuierlich auf, indem sie an Window.requestAnimationFrame() übergeben werden. Dies ist zwar eine gute Wahl für 3D-Spiele und Animationen, aber es könnte in Anwendungen, die eine statischere Szene haben, wie z.B. Anwendungen, die einfach 3D-Modelle zeigen übertrieben sein. In beiden Bibliotheken ist es möglich, das automatische Rendern auszuschalten, indem man einen Parameter auf die Szenengraphenkomponente setzt, wie man im Code des Minecraft-Zeichenkonfigurators sehen kann.
Schlußfolgerungen.
Für die Art von Projekten, die wir oben besprochen haben, würden wir definitiv empfehlen, React-Bindings für Three.js zu verwenden. Nicht nur ihr Code wird besser eingerichtet und damit wartungsfreundlicher, sondern auch ihre Arbeit erheblich beschleunigt, wenn Sie sich auch mit dem Workflow von React vertraut machen.
Ob Sie reacht-three- oder react-three-renderer verwenden sollten, hängt von ihrem Projekt ab. Beide Bibliotheken sind relativ neu, aber wie Sie auf GitHub sehen können, wird der Code wöchentlich aktualisiert und außerdem gibt es lebhafte Diskussionen in den Issue Trackern sowie Probleme und Vorschläge werden ziemlich schnell aufgegriffen.
Einige abschließende Bemerkungen, die ihnen helfen können, sich zu entscheiden:
- React-three hängt von Three.js ab r72 React Version 0.14.2, react-three-renderer funktioniert mit den neuesten Versionen von Three.js und React.
- React-three-renderer hat noch nicht alle Three.js-Features implementiert, react-three schon.
- In react-three funktioniert der Ray Caster nicht, in react-three-renderer hingegen schon.
- Beide Bibliotheken bieten ausgezeichnete Beispiele, deren Studium ihnen ein gutes Verständnis für die Grundprinzipien vermittelt.
Wenn Sie Fragen zu React und Three.js haben sollten, zögern Sie nicht, uns über unser Forum zu kontaktieren.
Vielen Dank für ihren Besuch.