Skip to content

Eine offene Welt im Browser entwickeln, Teil 8: Integration ohne Verlust unserer stabilen Basis

Von Oleg Sidorkin, CTO und Mitgründer von Cinevva

Neu hier? Nutze den Serienleitfaden. Dort wird erklärt, was ein Spike ist, und alle Teile sind verlinkt.

Bei der Integration werden Projekte schnell unübersichtlich. Einzelne Komponenten funktionieren isoliert. Man verbindet sie, und plötzlich sieht jeder Fehler so aus, als könnte seine Ursache überall liegen.

Die Spikes 13 und 14 waren unsere Antwort auf diese Falle. Mit Spike 13 schufen wir eine saubere Three.js-WebGPU-Basis. Nur ein Renderer, eine Szene, eine Kamera und ein einfaches Mesh. Kein Terrain, keine Compute-Shader, keine Effekte. Wir bestätigten, dass das WebGPU-Backend von Three.js korrekt initialisiert wurde, die Render-Schleife stabil lief und Node-Materialien mit TSL (Three.js Shading Language) wie erwartet funktionierten. Erst nachdem dieser Prüfpunkt bestanden war, begannen wir, weitere Schichten hinzuzufügen.

Spike 13 in einem neuen Tab öffnen ↗ · Quellcode ansehen

Bei Spike 14 ging es um schrittweise Härtung. Wir fügten jeweils nur eine Fähigkeit hinzu: zuerst die Kamerasteuerung, dann die Beleuchtung, anschließend das von der Marching-Cubes-Pipeline per Compute-Shader erzeugte Mesh und schließlich die Buffer-Anbindung, um die GPU-Ausgabe direkt in die Geometrieattribute von Three.js einzuspeisen. Nach jeder Ergänzung überprüften wir, ob die vorherige Schicht weiterhin korrekt funktionierte.

Spike 14 in einem neuen Tab öffnen ↗ · Quellcode ansehen

Das klingt langsam. Es war genau einen Tag lang langsam und ersparte uns kurz darauf mehrere Tage Arbeit, als die Nahtlogik und das Umschalten zwischen Strategien kompliziert wurden.

Die konkrete Fehlerkategorie, die diese Disziplin rechtfertigte, waren haarfeine Artefakte. Dünne Splitter, die wie beschädigte Geometrie aussahen, tatsächlich aber durch veraltete Daten verursacht wurden. Der Compute-Shader schrieb N Vertices in einen Buffer, doch der Draw-Call war weiterhin so konfiguriert, dass er N+M Vertices aus dem vorherigen Frame renderte. Diese zusätzlichen Vertices enthielten Datenmüll aus dem alten Dispatch. Das sichtbare Ergebnis waren flackernde, rasiermesserdünne Dreiecke, die unvorhersehbar erschienen und wieder verschwanden.

Diese Art von Fehler besiegt man nicht mit Intuition. Man besiegt sie mit kontrollierten Änderungen, bei denen genau bekannt ist, was sich zwischen dem letzten funktionierenden und dem aktuellen fehlerhaften Zustand geändert hat.

Durch die WebGPU-Integration lernten wir außerdem viel über den Lebenszyklus von Buffern. GPU-Buffer in WebGPU sind unveränderlich, sobald sie für eine bestimmte Nutzung gemappt wurden. Muss ein Vertex-Buffer vergrößert werden, weil die Ausgabe von Marching Cubes gewachsen ist, muss man einen neuen Buffer erstellen und die Bindung aktualisieren. Ein realloc gibt es nicht. Diesen Lebenszyklus korrekt umzusetzen und alte Buffer zu zerstören, ohne mit noch laufenden GPU-Aufgaben in Konflikt zu geraten, erforderte eine explizite Fence-Verwaltung, die es in WebGL nicht gibt.

In Teil 9 widmen wir uns den Transvoxel-Nähten. Dieses Kapitel beginnt ganz bewusst mit einem Grundgerüst. Zu diesem Zeitpunkt hatten wir die Lektion vollständig verinnerlicht: Überhastete Integration erzeugt rätselhafte Probleme, während ein kontrollierter Aufbau nachvollziehbare und behebbare Fehler hervorbringt.

In diesem Kapitel erwähnte Technologien

WebGPU. Der Nachfolger von WebGL, der im Browser hardwarenahe GPU-Zugriffe mit Compute-Shadern und indirektem Rendering ermöglicht. Die beiden entscheidenden WebGPU-Funktionen für offene Welten: Compute-Shader ermöglichen die GPU-seitige Erzeugung von Terrain, die Platzierung von Vegetation und Culling; beim indirekten Rendering entscheidet die GPU anhand der Compute-Ausgabe, was gezeichnet wird, wodurch CPU-Engpässe in dichten Szenen entfallen. Verfügbar in Chrome, Edge und Firefox auf Desktop-Systemen. Siehe WebGPU als Schlüssel zu mehr Performance.

Three.js Shading Language (TSL). Das Node-basierte Shader-System von Three.js, das rohes GLSL/WGSL durch kombinierbare JavaScript-Ausdrücke ersetzt. TSL-Nodes wie texture(), positionWorld, smoothstep() und fog() bauen zur Laufzeit einen Shader-Graphen auf, der für das jeweilige Backend kompiliert wird: WebGL GLSL oder WebGPU WGSL. Mit TSL lässt sich die Materiallogik einmal schreiben und für beide Renderer verwenden. Der Node-Graph wird in jedem Frame ausgewertet, sodass dynamische Uniforms und bedingte Verzweigungen auf natürliche Weise funktionieren.

Lebenszyklus von GPU-Buffern in WebGPU. WebGPU-Buffer werden mit bestimmten Nutzungs-Flags (VERTEX, STORAGE, COPY_DST usw.) erstellt und können danach nicht vergrößert oder verkleinert werden. Erzeugt ein Marching-Cubes-Dispatch mehr Vertices, als der Buffer aufnehmen kann, muss man einen neuen Buffer erstellen, die Bindung aktualisieren und den alten Buffer zerstören. Wird ein Buffer zerstört, auf den ein noch laufender GPU-Befehl verweist, führt das zu Fehlern. Eine explizite Fence-Verwaltung über device.queue.onSubmittedWorkDone() stellt sicher, dass der alte Buffer erst zerstört wird, nachdem die GPU ihn nicht mehr verwendet. Diese Disziplin beim Lebenszyklus gibt es in WebGL nicht, da dort der Treiber die Speicherverwaltung implizit übernimmt.

Schrittweise Härtung. Eine Prozessdisziplin für die Integration: eine nachweislich funktionierende Basis schaffen, jeweils nur eine Fähigkeit hinzufügen und nach jeder Ergänzung prüfen, ob die vorherige Schicht weiterhin funktioniert. Dieser Ansatz ist einen Tag lang langsamer, spart bei der späteren Fehlersuche aber mehrere Tage, weil sich jede Regression auf eine konkrete, kontrollierte Änderung zurückführen lässt. Dieses Muster aus Basis und schrittweiser Erweiterung ist in der Entwicklung offener AAA-Welten üblich, wo Systeme in einer festgelegten Reihenfolge integriert werden, um Risiken zu beherrschen.


Teil 8 von 12.
Zurück: Teil 7 – Marching Cubes und die ersten echten Höhlen
Weiter: Teil 9 – Transvoxel begann mit einem Grundgerüst
Serienleitfaden: /de/blog/2026-02-25-open-world-browser-series-guide