Eine offene Welt im Browser entwickeln, Teil 10: Nahtchaos und der Bosskampf an den Ecken
Von Oleg Sidorkin, CTO und Mitgründer von Cinevva
Neu hier? Nutze den Serienleitfaden. Er erklärt, was ein Spike ist, und verlinkt alle Teile.
Wenn die vorherigen Teile methodisch wirkten, fühlte sich dieses Kapitel wie ein Kampf an.
Die Spikes 17 bis 22 waren unsere Ära der Grenzfälle. Dual-LOD-Marching-Cubes, Grenznähte zwischen Heightmaps und MC, Eck-Chunks mit gemischten Auflösungen, an denen drei oder vier LOD-Stufen zusammentreffen, GPU-generierte Nähte und das Verhalten des Fallback-Modus. Jeder Spike befasste sich mit einem bestimmten Fehlerszenario, auf das wir gestoßen waren oder das wir erwartet hatten.
Spike 17 testete zwei Marching-Cubes-Auflösungen, die gleichzeitig aktiv waren. Die Herausforderung bestand darin, dass die Nachbarn eines einzelnen Chunks an verschiedenen Seiten unterschiedliche Auflösungen haben konnten. Die Übergangszellenlogik aus Spike 16 funktionierte jeweils für eine Seite. Wenn ein Chunk jedoch Übergangszellen an mehreren Seiten benötigte, wurde die Verwaltung des Vertex-Puffers kompliziert. Die Übergangszellen jeder Seite mussten generiert und angehängt werden, ohne die der anderen Seiten zu überschreiben.
Spike 19 in einem neuen Tab öffnen ↗ · Quellcode ansehen
Der erste wiederkehrende Bösewicht war die Umlaufrichtung. Mehrfach dachten wir, wir hätten Topologieprobleme, nur um dann festzustellen, dass es Orientierungsprobleme waren. Backface Culling entfernte gültige Nahtdreiecke, weil ihre Umlaufrichtung gegenüber dem Haupt-Mesh umgekehrt war. Dieselbe Ursache, aber je nach Kamerawinkel ein anderes sichtbares Symptom. Die Lösung bestand darin, im Code zur Ausgabe der Übergangszellen eine einheitliche Konvention für die Umlaufrichtung durchzusetzen und sie mit einer Umschaltoption für doppelseitiges Material zu überprüfen.
Der zweite Bösewicht war falsches Vertrauen aufgrund partieller Korrektheit. Eine Naht konnte aus einem Kamerawinkel perfekt aussehen und brechen, sobald die LOD-Rollen zwischen Chunks mit höherer und niedrigerer Auflösung vertauscht wurden. Die Übergangszelle ist asymmetrisch. Sie tastet die hochauflösende und die niedrigauflösende Seite unterschiedlich ab. Wenn die Logik dafür, „welche Seite hochauflösend ist“, bei einer Konfiguration vertauscht ist, wird der Fehler erst sichtbar, wenn sich die Kamera an eine bestimmte Position bewegt.
Dann kam eine unserer liebsten Rettungsaktionen. Wir jagten ein Artefakt an der Nahtkante von Heightmap-Kacheln und gaben der Übergangslogik die Schuld. Zwei Tage gingen dafür drauf. Der wahre Übeltäter war veralteter Overdraw. Höher aufgelöste Geometrie aus einem vorherigen Frame befand sich noch im hinteren Bereich des Puffers, nachdem der Chunk auf eine niedrigere LOD-Stufe heruntergestuft worden war. Der Zeichenbereich war weiterhin auf die alte, größere Vertex-Anzahl eingestellt. Sobald wir den Zeichenbereich auf die aktive Vertex-Anzahl begrenzten, die vom atomaren Zähler des Compute-Shaders gemeldet wurde, verschwand das „mysteriöse Nahtproblem“.
Das war eine hervorragende Erinnerung daran, dass sich Rendering-Fehler oft als Meshing-Fehler tarnen. Die Geometrie war die ganze Zeit korrekt. Der Draw Call las lediglich über das Ende der gültigen Daten hinaus.
In Spike 22 testeten wir einen hybriden Fallback, bei dem Chunks unter bestimmten Bedingungen vom Marching-Cubes- in den Heightmap-Modus wechseln konnten – etwa wenn der Chunk keine volumetrischen Bearbeitungen enthielt und weit genug von der Kamera entfernt war. Das eröffnete uns einen praktischeren Weg als eine Alles-oder-nichts-Strategie. Bearbeitete Chunks im Nahbereich nutzen MC für volumetrische Freiheit. Unbearbeitete Chunks im Fernbereich nutzen Heightmaps für mehr Effizienz.
Spike 22 in einem neuen Tab öffnen ↗ · Quellcode ansehen
Dieses Kapitel war der steile Abfall der Achterbahn. Frustrierend und produktiv zugleich. Viele der einzelnen Korrekturen waren klein – manchmal änderte nur eine einzige Zeile einen Vergleichsoperator oder einen Offset. Doch das daraus gewonnene Verständnis darüber, wie LOD-Übergänge, Pufferverwaltung und Zeichenbereiche zusammenspielen, war alles andere als klein.
In Teil 11 behandeln wir die Stabilisierungsschicht, die auf das Chaos folgte: richtlinienbasierte Chunk-Modi und den Übergang von reaktiver Fehlerbehebung zu expliziten Systemregeln.
In diesem Kapitel erwähnte Technologien
Dual-LOD-Marching-Cubes. Marching Cubes wird gleichzeitig auf zwei Auflösungsstufen ausgeführt, wobei Übergangszellen die Grenze miteinander verbinden. Die Herausforderung besteht darin, dass die Nachbarn eines einzelnen Chunks an verschiedenen Seiten unterschiedliche Auflösungen haben können, weshalb für jede Seite unabhängig Übergangszellen generiert werden müssen. Die Übergangszellen jeder Seite werden an den Vertex-Puffer angehängt, ohne die der anderen Seiten zu überschreiben. Atomare Zähler erfassen die gesamte aktive Vertex-Anzahl über alle Seiten hinweg.
Umlaufrichtung. Die Reihenfolge der Vertices innerhalb eines Dreiecks bestimmt, welche Seite die „Vorderseite“ ist. Für Backface Culling ist eine einheitliche Umlaufrichtung erforderlich – üblicherweise gegen den Uhrzeigersinn, wenn das Dreieck von außen betrachtet wird. Wenn Übergangszellen Dreiecke ausgeben, muss deren Umlaufrichtung der Konvention des Haupt-Meshes entsprechen. Ist sie umgekehrt, entfernt Backface Culling gültige Nahtdreiecke. Aus bestimmten Kamerawinkeln sieht das wie fehlende Oberflächen aus. Eine gängige Debugging-Technik besteht darin, am Material side: THREE.DoubleSide ein- und auszuschalten, um festzustellen, ob Artefakte durch die Umlaufrichtung oder durch echte Lücken in der Topologie verursacht werden.
Heightmap-zu-MC-Fallback. Ein hybrider Chunk-Modus, bei dem entfernte oder unbearbeitete Chunks Heightmap-Terrain verwenden – kostengünstig und mit flacher Oberfläche –, während nahe oder bearbeitete Chunks Marching Cubes nutzen – volumetrisch und für Höhlen geeignet. Die Fallback-Entscheidung hängt von der Entfernung zur Kamera und davon ab, ob der Chunk SDF-Bearbeitungen enthält. Die Naht zwischen einem Heightmap-Chunk und einem MC-Chunk benötigt eigene Übergangsgeometrie, ähnlich wie bei Transvoxel, allerdings werden hier zwei unterschiedliche Darstellungen anstelle zweier LOD-Stufen miteinander verbunden. Siehe hybride Heightmap mit volumetrischen Overlays.
Zeichenbereich und atomare Zähler. Bei der GPU-gesteuerten Mesh-Generierung schreibt der Compute-Shader Vertices in einen Puffer und erhöht einen atomaren Zähler, um zu erfassen, wie viele Vertices ausgegeben wurden. Der Draw Call muss diesen Zähler als Vertex-Anzahl verwenden, nicht die Kapazität des Puffers. Wenn der Zeichenbereich nicht auf die aktive Anzahl begrenzt wird, erzeugen veraltete Vertices aus früheren Frames, die sich noch im hinteren Bereich des Puffers befinden, Geistergeometrie: dünne Splitter und flackernde Dreiecke, die wie Topologiefehler aussehen, tatsächlich aber Rendering-Artefakte sind, die durch das Lesen über die gültigen Daten hinaus entstehen.
Teil 10 von 12.
Zurück: Teil 9 – Transvoxel begann mit einem Grundgerüst
Weiter: Teil 11 – Richtlinienbasierter statt fest codierter Modus
Serienleitfaden: /de/blog/2026-02-25-open-world-browser-series-guide