Eine offene Welt im Browser entwickeln, Teil 5: Das Budget für die schönen Dinge
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.
In diesem Kapitel traf visueller Anspruch auf harte Zahlen.
Wir teilten die Rendering-Kosten auf einzelne Spikes auf, weil gebündelte Ergebnisse schwer zu diagnostizieren sind. Wenn man alles gleichzeitig aktiviert, erfährt man nur, dass das Frame langsam ist. Man erfährt nicht, welche Funktion das Budget aufgezehrt hat.
Spike 7 untersuchte die Vegetationsdichte und die Kosten der Animation. Unser Ansatz war die Laufzeitverteilung anhand von 32x32-Dichtekarten pro Gelände-Chunk, die große InstancedMesh-Gruppen speisten. Jeder Grashalm und jede Strauchgruppe erhielt im Vertex-Shader eine Windanimation, die von einer scrollenden Noise-Textur gesteuert wurde. Die entscheidende Kennzahl war für uns nicht die Dreiecksanzahl, sondern der Draw-Call-Overhead und der Vertex-Durchsatz auf Mittelklasse-GPUs. Wir stellten fest, dass das Bündeln von Instanzen in weniger Meshes wichtiger war als die Reduzierung der Polygonzahl pro Grashalm.
Spike 7 in einem neuen Tab öffnen ↗ · Quellcode ansehen
Spike 8 erhöhte die Komplexität der Geländematerialien. Mehrschichtige Überblendung, gewichtet nach Neigungswinkel und Höhe, optionale triplanare Projektion für Felswände sowie Normal Maps pro Schicht. Der Shader führte neigungsbasiertes Splatting mit vier Texturschichten aus, für die jeweils ein Diffuse- und ein Normal-Sample nötig waren. Das sind
Spike 8 in einem neuen Tab öffnen ↗ · Quellcode ansehen
Spike 9 konzentrierte sich auf die Kosten kaskadierter Shadow Maps bei realistischer Gelände- und Objektlast. CSM mit drei Kaskaden war unsere Ausgangsbasis. Wir testeten gezielt mit tief stehender Sonne, weil die Belastung der Kaskaden dann am höchsten ist. Die entfernte Kaskade deckt einen riesigen Ausschnitt des Sichtfrustums ab, und die Schattendetails werden durch die Texeldichte bestimmt:
Spike 9 in einem neuen Tab öffnen ↗ · Quellcode ansehen
Die eigentliche Herausforderung in dieser Phase war die Produktdisziplin. Manche Effekte sahen hervorragend aus und mussten dennoch eingeschränkt werden, weil sie im Verhältnis zu ihrer visuellen Wirkung zu viel vom Frame-Budget beanspruchten.
Unsere Regel wurde einfach: Eine Funktion wird nur weiterentwickelt, wenn ihre Kosten durch gemessene Frame-Time-Daten gerechtfertigt werden können.
Das klingt selbstverständlich. In schnellen Prototyping-Zyklen, in denen sich alle auf den nächsten visuellen Erfolg freuen, ist es das nicht. Dass wir diese Regel früh beibehielten, vereinfachte spätere Architekturentscheidungen rund um Clipmaps und volumetrische Zonen erheblich, weil wir bereits die Kosten jeder einzelnen Funktion kannten, die um dieselben 16 ms konkurrierte.
In Teil 6 erreichten wir mit Geometrie-Clipmaps den ersten großen Wendepunkt in der Gelände-Architektur.
In diesem Kapitel erwähnte Technologien
InstancedMesh und GPU-Vegetation. InstancedMesh von Three.js rendert N Kopien derselben Geometrie mit einem einzigen Draw Call. Bei Vegetation steuert eine Dichtekarte (32x32 pro Chunk) die Laufzeitverteilung von Grashalmen und Strauchgruppen in Instanzpuffer. Die Windanimation läuft im Vertex-Shader und verwendet eine scrollende Noise-Textur. Im großen Maßstab entfernt ComputeInstanceCulling von WebGPU Instanzen außerhalb des Sichtbereichs und weit entfernte Instanzen vor der Rasterisierung, während IndirectBatchedMesh mehrere Vegetationstypen in einem einzigen Puffer zusammenfasst, der per Multi-Draw Indirect gezeichnet wird. Siehe unseren Landschaftsleitfaden zum GPU-Culling von Vegetation.
Triplanare Projektion. Standardmäßig UV-gemappte Texturen werden an steilen Hängen verzerrt, weil die UV-Koordinaten gestaucht werden. Bei der triplanaren Projektion werden Texturen entlang aller drei Achsen (X, Y, Z) projiziert und anhand der Oberflächennormalen überblendet. Felswände erhalten die X- oder Z-Projektion ohne Verzerrung, flacher Boden die Y-Projektion. Die Überblendung erfolgt weich und automatisch, ohne dass UV-Unwrapping erforderlich ist. Bei PBR-Gelände gelten dieselben Gewichtungen für Albedo-, Normal-, Rauheits- und Ambient-Occlusion-Kanäle. Siehe Details zur triplanaren Projektion.
Neigungs- und höhenbasiertes Material-Splatting. Anstelle von manuell gemalten Splat-Maps werden Materialien im Fragment-Shader anhand der Geländeeigenschaften prozedural zugewiesen. Flacher Boden in geringer Höhe erhält Gras, steile Hänge erhalten Fels, große Höhen erhalten Schnee – allerdings nur auf Flächen, die flach genug für Ablagerungen sind – und Bereiche nahe dem Meeresspiegel erhalten Sand. Die Übergänge verwenden smoothstep für eine weiche Überblendung. In unserer Implementierung wertet jeder Gelände-Chunk vier Texturschichten mit Diffuse- und Normal-Samples pro Schicht aus. Das ergibt acht Texturzugriffe pro Fragment, noch bevor die Beleuchtung berechnet wird. Siehe neigungs- und höhenbasierte Materialzuweisung.
Kaskadierte Shadow Maps (CSM). CSM teilt das Sichtfrustum der Kamera in 3–4 Entfernungsbereiche, sogenannte Kaskaden, auf. Jede Kaskade rendert aus Sicht der Sonne eine Shadow Map mit einer an ihre Entfernung angepassten Auflösung. Nahe Kaskaden erhalten hochauflösende Schatten, etwa detaillierte Kontaktschatten unter Bäumen und Gebäuden, während entfernte Kaskaden eine niedrigere Auflösung für großflächige Bergschatten verwenden. Der Gelände-Shader liest alle Kaskaden aus und wählt für jedes Fragment die passende aus. Performance-Kosten: 3–4 Kaskaden mit 1024x1024 Pixeln benötigen etwa 0,5–1 ms für das Rendern der Shadow Maps sowie etwa 0,2–0,3 ms für das Sampling. Siehe Schatten für Gelände.
Teil 5 von 12.
Zurück: Teil 4 – Streaming vor aufwendigem Gelände
Weiter: Teil 6 – Clipmaps veränderten den Verlauf
Serienleitfaden: /blog/2026-02-25-open-world-browser-series-guide