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.
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?
- Raster als flachen typisierten Buffer mit Breite und Höhe anlegen.
- Für jede aktive Zelle gültige Nachbarn aus dem alten Zustand lesen.
- Materialregel anwenden und Ergebnis konfliktfrei in den nächsten Buffer schreiben.
- 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.
- Leere Zelle und Sand als numerische Codes festlegen.
- Von unten nach oben arbeiten, damit ein Korn pro Tick höchstens einmal fällt.
- Grenzen prüfen und für komplexere Regeln einen zweiten Buffer einführen.
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.
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