5 Punkte von GN⁺ 2024-07-29 | 1 Kommentare | Auf WhatsApp teilen
  • Während 3D-Szenen- und Level-Projekte, die im Browser laufen, größer wurden, sammelten sich wiederverwendbare Werkzeuge an, die auf prozedurale und generative Techniken für bestimmte Elemente eines Levels setzen statt auf die Erzeugung einer ganzen Welt
  • Im Zentrum der Texturarbeit stehen Triplanar Mapping und Hex Tiling; sie werden genutzt, um Texturen ohne UVs aufzubringen oder sich wiederholende Muster zu verbergen
  • Bei beiden Techniken wird pow() auf die Interpolationsgewichte angewendet, um die dominante Achse bzw. das Gewicht einzelner Lookup-Ergebnisse zu verstärken; außerdem wird die Performance durch das Auslassen einiger Texture-Lookups verbessert
  • Teure Fragment-Shader lassen sich mit einem Depth Pre-Pass abmildern; in Szenen mit viel Overdraw kann sich die Performance um mehr als 30 % verbessern
  • Auf der Mesh-/Geometrieseite gibt es einen Trend zu LoD-Terrain, einer Runtime-Mesh-Verarbeitungspipeline und künftig zu Constructive Solid Geometry, um Dekoration, Hintergründe und Schadensdarstellungen zu erweitern

Prozedurale Werkzeuge, die sich in Browser-3D-Szenen angesammelt haben

  • Über mehrere Jahre hinweg wurden im Browser laufende 3D scenes and levels erstellt, wobei aus eigenständigen Demos mit Fokus auf Custom-Shader nach und nach stärker verbundene, spielartige Formen wurden
  • Der gemeinsame Ansatz besteht darin, prozedurale und generative Techniken eher auf bestimmte Teile eines Levels anzuwenden als auf vollständig prozedural erzeugte Welten
  • Dabei haben sich prozedurale und halbprozedurale Werkzeuge und Effekte, die in mehreren Levels wiederverwendet werden, ganz natürlich angesammelt

Shader und Texturen

  • Die meisten Texturen sind in beide Achsen nahtlos kachelbare seamless textures; wenn sie über große Flächen gelegt werden, können Wiederholungsmuster sichtbar werden
  • Durch mehrere Ergänzungen an einem Custom-Shader, der MeshPhysicalMaterial von Three.JS erweitert, wurde die Unterstützung für seamless Texturing verbessert
  • Triplanar Mapping

    • Triplanar Mapping ist das zentrale Texturierungswerkzeug, das in fast allen Levels eingesetzt wird
    • Damit lassen sich Meshes mit seamless textures texturieren, ohne dass vorab eine UV-Map definiert werden muss; das ist nützlich etwa bei prozedural erzeugtem Terrain, wo Modellierer keine Gelegenheit haben, ein UV-Mapping festzulegen
    • Es funktioniert gut sowohl für generierte Meshes als auch für von Hand modellierte Meshes
    • Die Implementierung ist leichtgewichtig und einfach; eine Referenzimplementierung steht in triplanarMapping.ts
  • Verbesserungen für Triplanar Mapping

    • Gewöhnliches Triplanar Mapping mischt Texture-Lookups entlang der drei Achsen linear anhand der Fragment-Normale
    • In Bereichen, in denen die Normale keiner einzelnen Achse nahekommt, kann die Textur geschichtet wirken
    • Wendet man auf die Gewichte pow() mit einem hohen Exponenten an und normalisiert danach erneut, steigt der Anteil der dominanten Achse und die Übergangsbereiche werden kleiner
    • Durch diese Transformation liegt bei den meisten Bereichen des Meshes das Gewicht einer Achse nahe 1, während die beiden anderen nahe 0 liegen
    • Werden Texture-Lookups für Gewichte unterhalb eines Schwellwerts ausgelassen, lässt sich der Performance-Overhead von Triplanar Mapping auf ein Niveau reduzieren, das nur noch leicht über klassischer UV-basierter Texturierung liegt
    • Die Verarbeitung von Normal Maps erfordert im Shader-Code gesonderte Behandlung; dafür wird der GPU Gems-Ansatz verwendet
    • Details zur Implementierung finden sich in Normal Mapping for a Triplanar Shader
  • Hex Tiling

    • Hex Tiling ist ein Algorithmus, der sichtbares Kacheln und Wiederholungen bei seamless textures verbirgt
    • Schon das Hinzufügen einer einzigen Konfigurationsoption zu einem Material kann eine Szene von einer unausgereiften Mockup-Anmutung in Richtung semi-realistischer Darstellung verschieben
    • Die erste Implementierung basierte auf dem Shadertoy von Fabrice Neyret, wurde an das Materialsystem von Three.JS angepasst und in den Haupt-Material-Shader des Projekts integriert
    • Später wurde sie mit Erlaubnis als eigenständige Bibliothek three-hex-tiling portiert, die Hex Tiling zu den Built-in-Materials von Three.JS hinzufügen kann
    • Anders als Triplanar Mapping ist dafür ein vorab definiertes UV-Mapping erforderlich
    • Werden beide Techniken zusammen verwendet, kann die maximale Zahl an Texture-Fetches pro Fragment und pro Map auf 27 steigen, was in der Praxis unbrauchbar ist
    • Auch Hex Tiling interpoliert pro Fragment drei Lookup-Ergebnisse linear; daher kann derselbe pow()-Gewichtungsansatz wie bei Triplanar Mapping sowohl die Performance als auch die Ergebnisqualität verbessern
  • Depth Pre-Pass

    • Fortgeschrittene Texturierungstechniken können in großen Szenen teure Fragment-Shader verursachen
    • Ein Depth Pre-Pass rendert zunächst die gesamte Szene mit einem sehr einfachen und günstigen Material, um die Tiefenwerte jedes Pixels zu schreiben
    • Zwar entsteht dabei Overhead durch das zweimalige Rendern der Szene, doch in Szenen mit viel Overdraw ist der Nutzen meist größer als die Kosten
    • Bei viel Overdraw kann das Hinzufügen eines Depth Pre-Pass die Performance um mehr als 30 % verbessern
    • Wenn man die Pre-Pass-Einstellungen ändert, lassen sich nur verdeckte Fragmente rendern, wodurch sichtbar wird, welche Fragmente beim Einsatz des Pre-Pass übersprungen werden
    • Details zur Implementierung in Three.JS und zur Konfiguration stehen in einem dedicated article
  • KI-basierte PBR-Textursynthese

    • KI-generierte Texturen werden in fast allen Szenen verwendet
    • Wenn sie sparsam eingesetzt werden, können die Ergebnisse ziemlich gut aussehen; in den Beispielszenerien sind alle Texturen KI-generiert
    • Der Prozess für Texturerzeugung, PBR-Map-Erstellung und das Zusammenführen zu nahtlosen 4K-Texturen ohne Upscaling wird in einem separaten Beitrag behandelt
    • Die dort zur Erzeugung von PBR-Maps erwähnte Website ist inzwischen nicht mehr nutzbar
    • Aktuell wird für die Erzeugung von Normal Maps DeepBump verwendet und bei Bedarf für andere Maps ein nicht KI-basiertes Tool wie Materialize
  • Volumetric Fog/Clouds

    • Volumetrisches Rendering ist ein Bereich von Interesse, weil es Szenen besondere Effekte verleihen kann
    • Dafür wurde ein relativ allgemeiner Shader erstellt, der sich nutzen lässt, um jeder Three.JS-Szene Wolken oder Nebel hinzuzufügen
    • Inspiriert von einem Shadertoy von Inigo Quilez entstand zunächst ein grundlegender Volumetric-Clouds-Shader mit ähnlichen LoD-Noise-Lookups, der später zu einer allgemeineren und konfigurierbaren Form erweitert wurde
    • Dieser Shader eignet sich gut, um leere Bereiche in eher spärlichen Levels zu füllen und statischen Levels durch bewegte Wolken oder Nebel ein dynamisches Gefühl zu geben
    • Dabei wurden teilweise auch Code und Ansätze verwendet, die n8programs im Projekt three-good-godrays entwickelt hat
    • Auch three-good-godrays selbst wird oft genutzt und verleiht Levels eine sehr besondere Atmosphäre

Meshes und Geometrie

  • Die Erzeugung von Runtime-Meshes ist ein Bereich, mit dem sich zunehmend mehr beschäftigt wird
  • Die Idee, dass eine Welt aus einem Software-Seed heraus wächst, ist attraktiv, doch die für einige Spiele mit Fokus auf prozedurale Generierung typische Wirkung von „unendlich, aber leer“ soll vermieden werden
  • Deshalb liegt der Schwerpunkt weniger auf der vollständigen prozeduralen Erzeugung des gesamten Kernerlebnisses als auf dem Hinzufügen von Dekoration, Hintergründen und prozeduralem Flourish zu Levels
  • LoD-Terrain

    • Terrain-Generierung ist ein klassisches Feld der prozeduralen Spieleentwicklung, und die Umsetzung selbst ist nicht besonders außergewöhnlich
    • Wie in den meisten Ansätzen wird per Noise-Funktion eine Terrain-Heightmap erzeugt, die dann für das Rendering in Dreiecke tesselliert wird
    • Für die Texturierung kommen Triplanar Mapping oder Hex Tiling zum Einsatz
    • Zentral ist das LoD-System: Das Terrain wird in Tiles aufgeteilt, und jedes Tile wird in mehreren Auflösungen erzeugt
    • Je nach Entfernung zwischen Tile und Kamera werden unterschiedliche Auflösungen dynamisch ausgetauscht
    • Dieses Terrain-Generierungssystem wird häufig wiederverwendet; durch seine flexible und effiziente Auslegung lässt es sich mit wenig Aufwand in vielen Levels einsetzen
  • Pipeline zur prozeduralen Mesh-Verarbeitung und -Manipulation

    • Der Bereich, an dem zuletzt am meisten gearbeitet wurde, ist eine Pipeline für procedural mesh processing
    • Das ursprüngliche Ziel war es, Low-Poly-Meshes prozedural zu unterteilen und zu deformieren, einschließlich dynamisch erzeugter Meshes
    • Dadurch sollen einfache Meshes wie Plattformen, Felsbrocken oder große Strukturen, die in ein Level gesetzt werden, realistischer oder interessanter wirken
    • Daraus entstand eine Software-Pipeline, die im Browser zur Laufzeit rohe Geometriedaten entgegennimmt, beliebig verändert und wieder in ein renderbares Format exportiert
    • Dabei sind detailreiche Überlegungen nötig, insbesondere bei der Normalenbehandlung
    • Implementierungsdetails stehen im Beitrag subdividing meshes for displacement

Nächster Experimentkandidat: Constructive Solid Geometry

  • Die meisten hier aufgelisteten Werkzeuge begannen ursprünglich als einmalige Implementierungen für einen bestimmten Anwendungsfall, wurden dann aber in anderen Levels und Kontexten immer wieder weiterverwendet
  • Die nächste größere Idee, die ausprobiert werden soll, ist Constructive Solid Geometry
  • Constructive Solid Geometry ist ein System, das boolean operators auf den 3D-Raum anwendet
    • Beliebige zwei Meshes können zusammengeführt werden
    • Aus einem Mesh lassen sich Chunks herausschneiden
    • Außerdem sind ähnliche Operationen möglich
  • csg.js implementiert ein CSG-Toolkit mit Mesh-Primitives, Boolean-Operatoren und einer sauberen API in einer einzigen kommentierten JavaScript-Datei mit rund 500 LoC
  • Irgendwann ist geplant, diese Bibliothek nach Rust zu portieren, um ihre Funktionsweise besser zu verstehen
  • Zusammen mit der bestehenden Mesh-Verarbeitungspipeline könnte CSG interessante Ergebnisse ermöglichen
  • Besonders reizvoll ist der Versuch, Meshes prozedural zu beschädigen
    • So könnten aus Gebäuden oder Brücken Stücke herausgeschnitten werden, um Zerfall oder Verwitterung zu simulieren
    • Ebenso könnten Risse in Wänden oder Straßen erzeugt werden

1 Kommentare

 
GN⁺ 2024-07-29
Hacker-News-Kommentare
  • Ich habe früher etwas mit prozeduraler Generierung herumgespielt, insbesondere um gut aussehende Bäume zu erzeugen, und das fehlende Puzzleteil für mich war eine einfache Methode, Geometrie miteinander zu verbinden
    Zwei Zylinder zu erzeugen ist einfach, aber sie natürlich miteinander zu verbinden, war sehr schwierig
    Theoretisch könnte CSG diese Lücke füllen, aber so über das Problem nachzudenken war nicht einfach. Man kann es nicht einfach als Schleife zum Hinzufügen von Vertices betrachten, weil man alles als 3D-Form modellieren muss
    Ich habe auch eine Routine gebaut, die zwei Vertex-Loops nimmt und heuristisch Flächen hinzufügt, um sie zu verbinden, aber die Auswahl, welche Vertices miteinander verbunden werden sollen, war viel schwieriger als erwartet, und hässliche Verbindungen entstanden leicht
    Irgendwann würde ich gern ein Spiel machen, in dem verschiedene modulare prozedurale Generierungssysteme zusammenarbeiten und dabei ihre Spontaneität behalten. Zum Beispiel könnten Systeme Teile der Welt „besetzen“, diese Teile an andere Systeme delegieren oder sie organisch mit benachbarten Elementen verbinden

    • Am besten weitermachen, aber es nicht zu kompliziert machen
      Kürzlich gab es ein Video [0] über die Erzeugung prozeduraler Bäume, und die Lösung war einfach, die Zylinder einander schneiden zu lassen. Solche hackigen Ansätze können gut funktionieren und ordentliche Ergebnisse liefern
      Wie du gesagt hast, ist auch CSG möglich, kann aber unnötig komplex werden. Eine andere Methode ist, zuerst das Skelett des Baums zu erzeugen, dann ein Lofting durchzuführen und es bei Bedarf mit CSG zu kombinieren, um Stamm und Rinde zu erzeugen
      Eine gute Bibliothek eröffnet Möglichkeiten. Wenn du eine 3D-Geometriebibliothek findest, die boolesche Operationen wie das Verbinden von Zylindern oder das Subtrahieren von Geometrie ausführt, kannst du viele neue Ideen ausprobieren. Ich habe einige verwendet, und die einzige, die mir halbwegs gefiel, war JSCAD [1]
      [0] https://youtu.be/8zMbJmuwEUc?si=KQclrVPeSrIRmsbA
      [1] https://github.com/jscad/OpenJSCAD.org
    • Schau dir Signed Distance Fields an. Das ist vermutlich die eleganteste CSG-Lösung für prozedurale Geometrie
      Es ähnelt einer funktionalen Art, 3D-Formen zu beschreiben und zu transformieren
      Wenn du sehen willst, was Leute in der Demoszene damit gemacht haben, kann ich nur dringend empfehlen, auf YouTube nach Mercury Delight zu suchen. Auf Shadertoy gibt es auch viele Beispiele, und da sind wirklich erstaunliche Sachen dabei
    • Wenn du Schleifen entfalten kannst, kann man es auch als eine Schleife zum Hinzufügen von Vertices betrachten
      https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Condition...
      Das folgende Beispiel ist ebenfalls sehenswert
      https://github.com/MaxBondABE/batteries/blob/master/src/geom...
  • Gute Terrain-Generierung ist keineswegs trivial, egal ob im Maßstab der ganzen Welt oder auf Augenhöhe
    Ein einfacher Ansatz erzeugt, wie im Artikel zu sehen, eine zerklüftete Heightmap, die realem Terrain kaum ähnelt und auch zum Erkunden nicht besonders interessant ist
    Dwarf Fortress beginnt zum Beispiel mit einer grundlegenden Midpoint-Displacement-Methode, durchläuft danach aber viel maßgeschneiderte Nachbearbeitung

    • Ich halte das für ein Problem der ganzen Spieleindustrie
      Nichts von dem, was in diesem Artikel gezeigt wird, ist auch nur vage trivial. Der Autor ist, was GPU-Shader-Rendering angeht, offensichtlich eher ein 100x-Entwickler als fast jeder andere Mensch auf diesem Planeten
      „Einfacher Ansatz“ — ist das wirklich einfach?
      Wie viel Prozent von 8 Milliarden Menschen könnten überhaupt schon die „Hello World“-Stufe umsetzen, die nötig ist, um mit diesem Artikel anzufangen? Und wie viele wissen überhaupt, was Shader sind? Es gibt ja auch nicht so viele OpenGL-Jobs. Situationen wie „Was ist OpenGL, wir benutzen nur Unity“ sind nicht selten
      Und wie sieht es bei Online-Gamern aus? Von den 1.021.282 Personen [1], die am 28. Juli 2024 um 13:22 Uhr EST in Counter Strike 2 eingeloggt waren, wie viel Prozent könnten auch nur den ersten Schritt eines Shaders implementieren, den das von ihnen gespielte Spiel braucht?
      Wie viel Prozent könnten ein simples C++-Kommandozeilenprogramm kompilieren oder ein noch einfacheres JavaScript-Skript im Browser schreiben? Eigentlich ist das eine Fangfrage, denn die meisten Menschen kommen schon mit E-Mail gerade so zurecht
      [1] https://steamdb.info/app/730/charts/
    • Ich habe schon Ansätze gesehen, bei denen man zuerst eine zerklüftete Heightmap erzeugt und dann per Erosionssimulation Terrain generiert
    • Ich habe den Artikel eher als guten Ausgangspunkt verstanden als als etwas auf „Release-Niveau“
      Wie bei KI wird selbst bei der besten prozeduralen Inhaltsgenerierung das Endergebnis maßgeschneiderte Nachbearbeitung enthalten
  • Das ist ein wirklich hilfreicher Artikel, der verschiedene prozedurale Techniken für den Einsatz im Web gut zusammenfasst
    Wer Interesse an einem prozeduralen RPG-Level-Editor hat, an dem ich arbeite, kann sich auch https://github.com/gamedevgrunt/3D-Action-RPG-JavaScript ansehen

  • Ich habe mit Depth Pre-Passes gemischte Erfahrungen gemacht
    Als ich das ein paar Mal ausprobiert habe, sah ich auf Desktop-GPUs der mittleren bis oberen Klasse keinen spürbaren Leistungsgewinn
    Den genauen Grund kenne ich nicht, aber ich vermute, dass frühes Z-Culling bereits die Aufrufe des Pixel-Shaders reduziert hat. Normalerweise rendert man opake Meshes von vorn nach hinten
    Meine Experimente fanden allerdings nicht im Spiel-, sondern im CAD/CAM-Kontext statt. Die Szenen unterschieden sich auch ziemlich von typischen Spielumgebungen: wenig Texturen, dafür Geometrie mit extrem vielen Polygonen

    • All diese Techniken sind kontextabhängig und hängen nicht nur vom Spiel und der Engine insgesamt ab, sondern auch von spezifischem Artwork und Szenenaufbau
      Ein Depth Pre-Pass ist oft auch der erste Schritt für Occlusion Culling, aber auch das hängt stark vom Kontext ab. In komplexen Stadtlandschaften ist es wahrscheinlich viel nützlicher als bei CAD-Modellen
    • Wenn dein Flaschenhals in der Vertex-Verarbeitung liegt, hilft ein Depth Pre-Pass nicht
      Am nützlichsten ist er bei hoher Depth Complexity und teuren Fragment-Shadern. Fairerweise trifft das auf die meisten Spiele zu
      Bei Deferred Renderern ist er normalerweise nicht zwingend nötig, aber bei forward+ bringt er in der Regel ziemlich viel