EnglishGitHub
Prototyppackages/webgl-sand-engine/src

WebGL Sand Engine

@ralphschuler/webgl-sand-engine

Führt eine deterministische CPU-Sandreferenz und begrenzte Nachbarschaftsabfragen aus.

cellular automatagridWebGL

01 · Problem

Wofür braucht man das?

Sand, Wasser und Hindernisse benötigen lokale Nachbarschaftsregeln, deterministische Updates und eine effiziente Darstellung vieler Rasterzellen.

02 · Denkmodell

Das mentale Modell

Jede Zelle enthält einen Materialcode. Ein Simulationsschritt liest den alten Buffer und schreibt Entscheidungen in einen zweiten Buffer; Rendering ist davon getrennt.

Im Repository

Führt eine deterministische CPU-Sandreferenz und begrenzte Nachbarschaftsabfragen aus. Der geprüfte Kern erhält Partikel und nutzt Snapshot-Updates. Vollständige WebGL-Simulation, Wasser und Chunk-Synchronisation bleiben spätere Integrationen.

03 · Kontrollfluss

Was passiert in welcher Reihenfolge?

  1. Raster als flachen typisierten Buffer mit Breite und Höhe anlegen.
  2. Für jede aktive Zelle gültige Nachbarn aus dem alten Zustand lesen.
  3. Materialregel anwenden und Ergebnis konfliktfrei in den nächsten Buffer schreiben.
  4. Buffer tauschen und den neuen Zustand separat rendern.

04 · Bauteile

Die entscheidenden Verträge

SandSimulation.step / snapshotFührt einen deterministischen CPU-Schritt aus und liefert eine Kopie.
NeighborProvider.getNeighborsFiltert positionierte Elemente innerhalb eines begrenzten Radius.

05 · Selbst bauen

Selbst implementieren

Starte auf der CPU mit einer einzigen Sandregel; danach kann derselbe Zustandsvertrag auf die GPU wandern.

  1. Leere Zelle und Sand als numerische Codes festlegen.
  2. Von unten nach oben arbeiten, damit ein Korn pro Tick höchstens einmal fällt.
  3. Grenzen prüfen und für komplexere Regeln einen zweiten Buffer einführen.
Konzeptskizze · kann ausgelassene Helfer verwenden
const EMPTY = 0, SAND = 1;
function stepSand(grid: Uint8Array, width: number, height: number) {
  const next = grid.slice();
  for (let y = height - 2; y >= 0; y--) for (let x = 0; x < width; x++) {
    const from = y * width + x, below = from + width;
    if (grid[from] === SAND && grid[below] === EMPTY) {
      next[from] = EMPTY; next[below] = SAND;
    }
  }
  return next;
}

06 · Verifizieren

Was solltest du testen?

  • Ein Korn fällt genau eine Zelle und verlässt niemals den Rasterrand.
  • Blockierter Sand bleibt stehen; Eingabebuffer wird nicht verändert.
  • Mehrere Körner erzeugen unabhängig von Iterationsdetails ein dokumentiertes Ergebnis.

Selbst ausprobieren

Formuliere die erste Prüfaussage als fehlschlagenden Test, implementiere die kleinste Korrektur und ergänze einen Grenzfall.

Ein Korn fällt genau eine Zelle und verlässt niemals den Rasterrand.

Ansatz vergleichen

Dein Test sollte beim gezielten Verletzen der Invariante fehlschlagen und nach der Korrektur wieder bestehen. Halte den Aufbau deterministisch und prüfe ein beobachtbares Ergebnis.

07 · Grenzen

Kompromisse und Stolperfallen

  • CPU-Regeln sind leicht zu debuggen, skalieren aber schlechter als parallele Shader.
  • Double Buffering vermeidet Lese-/Schreibkonflikte und verdoppelt den Zustandsbedarf.
  • Zufällige Diagonalwahl wirkt natürlich, erschwert ohne seedbaren RNG die Reproduktion.
Wichtig

Der geprüfte Kern erhält Partikel und nutzt Snapshot-Updates. Vollständige WebGL-Simulation, Wasser und Chunk-Synchronisation bleiben spätere Integrationen.

08 · Weiterdenken

Quellcode und verwandte Ideen

yarn tsx packages/webgl-sand-engine/examples/basic.ts