3D-Plotter & Open-World WebGPU Engine
Vom 3D Function Plotter zur vollständigen Browser-Engine — Architektur, Tutorials, Code
Zuletzt aktualisiert: 13.04.2026
Zielgruppe: Einsteiger in HTML/JavaScript — schrittweise bis zur Engine-Architektur
Inhaltsverzeichnis
Teil 1: 3D Function Plotter | Teil 2: Engine Architecture | Teil 3: Tutorials | Teil 4: Diagramme | Teil 5: Internals | Teil 6: Glossar & Schlusswort
- Was ist der 3D Function Plotter?
- Was ist Plotly.js?
- Starten — direkt im Browser
- Version 1: Grundfunktionen
- Version 2: Animation mit t
- Version 3: Export
- Eigene Funktionen eingeben
- Wie der Plotter funktioniert
- Projektstruktur
- WebGPU Engine: Einleitung
- Engine Architecture: Überblick
- Core Framework: ECS & Loop
- Rendering Architecture
- World Composition
- Physik & KI-Framework
- Networking & Editor
- Getting Started
- Erste Entity erstellen
- Eigenes System erstellen
- NPC erstellen
- Fahrzeug erstellen
- Neues Biom erstellen
- Shader erstellen (WGSL)
- Multiplayer hinzufügen
- UML-Diagramme
- Sequence- & Dataflow-Diagramme
- Shader-Pipeline-Diagramm
- ECS Internals & Sparse Set
- Memory Layout & Zero-GC
- Performance-Optimierungen
- GPU-Optimierungen & Debugging
- Glossar
- Schlusswort & Credits
Teil 1 — 3D Function Plotter
1. Was ist der 3D Function Plotter?
Der 3D Function Plotter ist eine reine Browser-Anwendung — eine einzelne HTML-Datei, die mathematische Funktionen als interaktive 3D-Oberflächen darstellt. Keine Installation, kein Server, kein Backend benötigt.
Du gibst eine Funktion z = f(x, y) ein — der Plotter berechnet alle z-Werte für ein x/y-Gitter und zeigt das Ergebnis als drehbare, färbige 3D-Fläche an.
| Version | Features |
|---|---|
| Version 1 Basis | Beispiel-Funktionen, eigener Formel-Editor, Auflösungs-Slider, Live-Update |
| Version 2 Animation | + Animationsparameter t, Start/Stop-Buttons, zeitabhängige Funktionen |
| Version 3 Export | + PNG-Screenshot, JSON-Datendump, TXT-Funktionsexport |
| Version 4 Physik | + Boot-System mit Auftrieb, Hydrodynamik, Segel- und Motorphysik |
| Version 5 Multiplayer | + WebSocket-Multiplayer mit Client/Server, Prediction & Replication |
| Version 6 WebGPU Engine | + Vollständige WebGPU-Engine mit Renderer, Editor, Shader-Compiler, Gizmos |
2. Was ist Plotly.js?
Plotly.js ist eine Open-Source-JavaScript-Bibliothek für interaktive Diagramme und 3D-Visualisierungen. Sie wird über ein einzelnes <script>-Tag eingebunden.
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id="plot"></div>
<script>
Plotly.newPlot('plot', data, layout);
</script>
- 3D-Oberflächen (
surface), Streudiagramme, Linien, Heatmaps - Drehbare, zoombare, verschiebbare 3D-Ansicht per Maus
- Automatische Farb-Skalierung nach z-Wert
- PNG-Export eingebaut — keine Abhängigkeiten
3. Starten — direkt im Browser
- Projekt-Ordner herunterladen (oder klonen)
Version-1/index.htmlim Browser öffnen- Fertig — der Plotter läuft sofort
file://. Erst wenn man eigene Dateien per fetch() oder WebGPU verwendet, wird ein lokaler Server benötigt (z.B. npx serve oder VS Code Live Server).
4. Version 1: Grundfunktionen
| Element | Funktion |
|---|---|
| Beispiel-Funktion (ComboBox) | Vorausgewählte Beispiele: Wellen, Sättel, Kegel, Spiralen |
| Formel-Eingabe | Eigene Funktion eingeben, z.B. Math.sin(x)*Math.cos(y) |
| Auflösungs-Slider | Feinheit des Gitters — höher = schöner, aber langsamer |
| Live-Update | Plot aktualisiert sich automatisch beim Ändern der Formel |
// Klassische Beispiel-Funktionen:
Math.sin(x) * Math.cos(y) // Welle
Math.sin(Math.sqrt(x*x + y*y)) // Ringwelle
x*x - y*y // Sattelfläche
Math.exp(-(x*x + y*y)) // Gauß-Glocke
3D-Plot-Steuerung
- Linke Maustaste + ziehen: Drehen
- Scrollrad: Zoom
- Rechte Maustaste + ziehen: Verschieben
- Doppelklick: Ansicht zurücksetzen
5. Version 2: Animation mit Parameter t
Version 2 fügt einen Zeitparameter t hinzu. Die Funktion kann nun von t abhängen — der Plot animiert sich automatisch.
// t = Zeit (läuft automatisch hoch)
Math.sin(x + t) * Math.cos(y + t) // Pulsierende Welle
Math.sin(Math.sqrt(x*x + y*y) - t) // Rotierende Ringwelle
Math.sin(x) * Math.sin(y) * Math.cos(t) // Stehende Welle
setInterval() ruft die Plot-Funktion regelmäßig auf, erhöht t und aktualisiert mit Plotly.react() (effizienter als newPlot() für Updates).
6. Version 3: Export
| Export | Format | Verwendung |
|---|---|---|
| Screenshot | PNG | Word, PowerPoint, Präsentationen |
| Datendump | JSON (x/y/z) | Weiterverarbeitung in Python, MATLAB |
| Formel | TXT | Funktion speichern und später laden |
// PNG-Export:
Plotly.downloadImage('plot', { format: 'png', filename: '3d-plot' });
// JSON-Export:
const blob = new Blob([JSON.stringify({x, y, z})], {type: 'application/json'});
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'plot-data.json';
a.click();
7. Eigene Funktionen eingeben
Im Formel-Feld können alle JavaScript-Math-Funktionen verwendet werden:
Math.sin(x) Math.cos(y) Math.tan(x)
Math.sqrt(x*x+y*y) Math.pow(x, 3)
Math.exp(-x) Math.log(Math.abs(x)) Math.PI
// Kreative Beispiele:
Math.floor(x) + Math.floor(y) // Treppe
Math.sin(Math.sqrt(x*x+y*y)) / (Math.sqrt(x*x+y*y)+0.01) // Mex. Hut
(Math.floor(x) + Math.floor(y)) % 2 // Schachbrett
8. Wie der Plotter funktioniert
function plotFunction(formel, aufloesung) {
const n = aufloesung; // z.B. 50 → 50×50 = 2500 Punkte
const range = 5; // x und y von -5 bis +5
const x = [], y = [], z = [];
for (let i = 0; i < n; i++) {
const xRow = [], zRow = [];
const xVal = -range + (2 * range * i) / (n - 1);
for (let j = 0; j < n; j++) {
const yVal = -range + (2 * range * j) / (n - 1);
const fn = new Function('x', 'y', 't', `return ${formel}`);
xRow.push(xVal);
zRow.push(fn(xVal, yVal, t));
}
x.push(xRow);
z.push(zRow);
}
Plotly.react('plot', [{ type: 'surface', x, y, z, colorscale: 'Viridis' }]);
}
9. Projektstruktur
3D_Plotter/
├── Version-1/index.html ← Basis-Plotter (Plotly.js)
├── Version-2/index.html ← + Animation mit Parameter t
├── Version-3/index.html ← + PNG / JSON / TXT Export
├── Version-4/engine/ ← Boot-Physik, Hydrodynamik, Segel
├── Version-5/engine/ ← Multiplayer (WebSocket, Prediction, Replication)
└── Version-6/ ← WebGPU Engine (aktuell)
├── index.html ← Einstiegspunkt
├── main.js ← Engine-Bootstrap
├── core/ ← Renderer, Kamera, Materialien, Shader-Compiler
├── editor/ ← Editor-UI, Inspector, Transform-Gizmos
├── shaders/ ← WGSL-Shader (basic.wgsl)
└── world/ ← Welt-Systeme
3DFunctionPlotter.html ← Standalone-Version (eine Datei, kein Server nötig)
Teil 2 — Open-World WebGPU Engine: Architecture
10. Open-World WebGPU Engine: Einleitung
Der 3D Function Plotter ist der Ausgangspunkt einer größeren Entwicklungsreise: der Open-World WebGPU Engine — eine modulare, vollständig prozedurale 3D-Engine, die direkt im Browser läuft.
| System | Beschreibung |
|---|---|
| WebGPU Rendering | Physically-Based Rendering, Compute Shader, GPU-Instancing, Post-Processing |
| Prozedurale Welt | Terrain, Biome, Vegetation-Spawner, Wetter-Simulation |
| FFT-Ozean | Realistischer Ozean per Fast Fourier Transform auf der GPU |
| KI-NPCs | Behavior Trees, Pathfinding, NPC-Ökosysteme (Fische, Haie, Vögel, Händler) |
| Gameplay | Charakter-Controller, Boot-Physik, Interaktionen |
| Multiplayer | WebSocket-Client, State Replication, Prediction & Reconciliation |
| Editor | Terrain-Painter, Vegetation-Tools, Shader-Editor, Debug-Panels |
Architektur-Philosophie
- Modularität: Jedes System ist unabhängig und austauschbar
- Determinismus: Prozedurale Welt + Seed = reproduzierbare Ergebnisse (wichtig für Multiplayer)
- Performance: ECS-basiert, GPU-optimierte Datenstrukturen, Zero-GC Architektur
Zielgruppe der Engine
Engine-Entwickler, Gameplay-Programmierer, Shader-Autoren, WebGPU-Enthusiasten, Forscher & Studenten.
11. Engine Architecture: Überblick
Die Engine besteht aus fünf Schichten von oben nach unten:
┌──────────────────────────────────────────────┐
│ APPLICATION LAYER │
│ (Game Code, Scripts, Editor Tools) │
├──────────────────────────────────────────────┤
│ ENGINE LAYER │
│ (Core, ECS, Systems, World, Rendering) │
├──────────────────────────────────────────────┤
│ SUBSYSTEM LAYER │
│ (Terrain, Ocean, Weather, AI, Physics) │
├──────────────────────────────────────────────┤
│ RENDERING LAYER │
│ (WebGPU, Pipelines, Shaders, Materials) │
├──────────────────────────────────────────────┤
│ PLATFORM LAYER │
│ (Browser, WebGPU API, Input, Network) │
└──────────────────────────────────────────────┘
| Schicht | Inhalt |
|---|---|
| Application | Spielcode, Skripte, Editor-Tools — das, was Entwickler schreiben |
| Engine | ECS, Systems, World-Manager, Renderer — das Herzstück |
| Subsystem | Spezialisierte Systeme: Terrain, Ozean, Wetter, KI, Physik |
| Rendering | WebGPU-Pipelines, Shader, Materialien |
| Platform | Browser-APIs, WebGPU, Input, Netzwerk — die Basis |
12. Core Framework: ECS & Engine Loop
12.1 Was ist ECS?
Das Entity-Component-System ist das Fundament der Engine. Statt klassischer Klassen-Hierarchien werden drei Konzepte sauber getrennt:
| Konzept | Bedeutung | Beispiel |
|---|---|---|
| Entity | Ein Objekt in der Welt — nur eine ID | Boot, Fisch, Baum, Charakter |
| Component | Reine Daten eines Objekts — keine Logik | Position, Geschwindigkeit, NPC-State |
| System | Logik, die auf viele Entities angewendet wird | PhysicsSystem, AISystem, RenderSystem |
// Beispiel: Hai-Entity mit Komponenten
const shark = ecs.createEntity();
ecs.addComponent(shark, Transform, { x: 10, y: 0, z: 5 });
ecs.addComponent(shark, NPC, { type: "shark", state: "idle" });
ecs.addComponent(shark, Rigidbody, { vx: 0, vy: 0, vz: 0 });
// ECS-Datenfluss:
// Entity → Component → System → Frame Update
12.2 System Scheduler
Systeme werden in fester Reihenfolge ausgeführt. Die Reihenfolge ist entscheidend — z.B. muss die Physik vor dem Rendering laufen:
Ausführungsreihenfolge pro Frame:
1. InputSystem ← Tastatur, Maus, Touch
2. PhysicsSystem ← Kollision, Schwerkraft
3. OceanSystem ← FFT-Wellensimulation
4. WeatherSystem ← Wind, Regen, Wolken
5. AISystem ← Behavior Trees der NPCs
6. CharacterSystem ← Spieler-Controller
7. BoatSystem ← Boot-Physik & Auftrieb
8. VegetationSystem ← Wind-Animation für Bäume/Gras
9. RenderingSystem ← Alles zeichnen
10. EditorSystem ← Debug-Panels (nur im Editor-Modus)
12.3 Engine Loop
function frame(timestamp) {
const dt = (timestamp - lastTime) / 1000; // Delta-Zeit in Sekunden
lastTime = timestamp;
input.update(); // Eingaben auslesen
ecs.preUpdate(dt); // Vorab-Berechnungen
ecs.update(dt); // Alle Systeme in Reihenfolge
renderer.render(scene); // WebGPU Render-Pass
requestAnimationFrame(frame); // Nächsten Frame planen
}
requestAnimationFrame(frame); // Loop starten
13. Rendering Architecture
13.1 Rendering Pipeline
CPU:
Scene → Culling → Render Graph → Command Buffers → GPU
GPU:
Vertex Shader → Fragment Shader → PostFX → SwapChain → Bildschirm
13.2 Render Graph
Der Render Graph definiert die Reihenfolge aller Render-Passes:
| Pass | Inhalt |
|---|---|
| Depth Pre-Pass | Tiefenwerte vorberechnen für Occlusion Culling |
| Opaque Pass | Alle undurchsichtigen Objekte (Terrain, Gebäude) |
| Ocean Pass | FFT-Ozean mit Transparenz und Reflektionen |
| Vegetation Pass | Bäume und Gras (GPU-Instancing) |
| Sky & Weather Pass | Himmel, Wolken, Regen, Atmosphäre |
| Post-Processing | Bloom, Tonemapping, FXAA, Farbkorrektur |
13.3 Material System
// Material als JSON-Definition:
{
"shader": "pbr.wgsl",
"textures": ["albedo.png", "normal.png"],
"uniforms": {
"roughness": 0.4,
"metallic": 0.1
}
}
13.4 Shader Beispiel (WGSL)
WGSL (WebGPU Shading Language) ist die Shader-Sprache von WebGPU, vergleichbar mit GLSL für WebGL:
// Vertex Shader: Transformiert 3D-Position in Bildschirmkoordinaten
@vertex
fn vs_main(input: VertexInput) -> VertexOutput {
var out: VertexOutput;
out.position = camera.proj * camera.view * model * vec4(input.position, 1.0);
out.normal = normalize((model * vec4(input.normal, 0.0)).xyz);
return out;
}
// Fragment Shader: Berechnet Farbe pro Pixel
@fragment
fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
let light = max(dot(input.normal, vec3(0.2, 1.0, 0.3)), 0.0);
return vec4(light, light, light, 1.0); // Graustufen-Beleuchtung
}
14. World Composition
14.1 Terrain System
- Heightmap-basiert: Höhenwerte werden per Noise-Generator berechnet
- Biom-System: Je nach Höhe und Feuchte wird das Biom bestimmt (Strand, Wald, Gebirge, Schnee)
// Terrain API:
const height = terrain.getHeightAt(x, z); // Höhe an Position (x,z)
const biome = terrain.getBiomeAt(x, z); // Biom an Position (x,z)
const slope = terrain.getSlopeAt(x, z); // Hangneigung
14.2 Ocean System (FFT)
Der Ozean wird mit einer Fast Fourier Transform simuliert — physikalisch realistisch, GPU-beschleunigt:
- Phillips Spectrum: Beschreibt die Wellenverteilung basierend auf Windgeschwindigkeit
- FFT: Berechnet aus dem Spektrum die tatsächliche Wellenhöhe in Echtzeit
- Normalmap: Erzeugt realistische Lichtreflektionen auf der Wasseroberfläche
// Wellenhöhe an Position abfragen (für Physik):
const waveHeight = ocean.sampler.sample(x, z);
14.3 Vegetation System
- Bäume und Gras werden automatisch gemäß Biom gespawnt
- GPU-Instancing: Tausende Objekte mit einem einzigen Draw Call gerendert
- Wind-Animation: Shader simuliert Windeinfluss auf Bäume und Grashalme
15. Physik & KI-Framework
15.1 Physik-Schicht
| Komponente | Beschreibung |
|---|---|
| Rigidbody | Geschwindigkeit, Beschleunigung, Masse |
| Collision | Einfache AABB- und Sphere-Kollision |
| Buoyancy | Auftrieb basierend auf aktueller Wellenhöhe |
| Character Capsule | Kollision für den Spieler-Controller |
| Boat Physics | Boot-Auftrieb, Rückstellung, Wasser-Widerstand |
// Buoyancy-Beispiel: Boot treibt auf dem Ozean
const wave = ocean.sampler.sample(x, z);
const buoyancy = (wave - boat.position.y) * boat.physics.buoyancyFactor;
boat.rigidbody.vy += buoyancy * dt; // Auftriebskraft anwenden
15.2 KI-Framework
Die KI der NPCs basiert auf Behavior Trees — einem baumartigen Entscheidungssystem:
// Behavior Tree eines Hai-NPCs:
Root
└─ Selector (wählt erste erfolgreiche Aktion)
├─ FleeNode ← Fliehen, wenn Gefahr nah
├─ ChaseNode ← Verfolgen, wenn Beute sichtbar
└─ WanderNode ← Ziellos schwimmen (Standard)
// Tick-Aufruf im AISystem (einmal pro Frame):
bt.tick(npc, dt);
16. Networking & Editor
16.1 Networking Architecture
Multiplayer basiert auf WebSockets — einer dauerhaften bidirektionalen Verbindung zwischen Browser und Server:
// Replication Flow:
Client → sendet Input → Server
Server → berechnet autoritativen State → sendet an alle Clients
Clients → wenden State an + Prediction & Reconciliation
// Deterministische Seeds:
// Alle Clients erzeugen dieselbe prozedurale Welt aus demselben Seed
// → Kein ständiges Übertragen von Terrain-Daten nötig
16.2 Editor Framework
Der integrierte Editor ermöglicht Echtzeit-Bearbeitung der Welt ohne Neustart:
| Panel | Funktion |
|---|---|
| Terrain Painter | Höhe malen, senken, glätten direkt im 3D-Viewport |
| Vegetation Tools | Bäume und Gras platzieren, löschen, konfigurieren |
| Shader Editor | WGSL-Shader live bearbeiten und sofort sehen |
| Debug Panels | ECS-Entities inspizieren, Systeme deaktivieren, Seed fixieren |
// Panel-Registrierung im Editor:
editor.addPanel("Terrain", new TerrainPanel());
editor.addPanel("Vegetation", new VegetationPanel());
editor.addPanel("Shader", new ShaderEditor());
Teil 3 — Tutorials & How-To Guides
17. Getting Started
Ziel: Projekt starten, Engine laden, erste Szene sehen.
Voraussetzungen
- Browser mit WebGPU-Unterstützung (Chrome 113+ oder Edge 113+)
- Lokaler Webserver (VS Code Live Server, Node.js
npx serve, oder Python) - VS Code empfohlen als Editor
Schritte
# 1. Lokalen Server starten (Node.js):
npx serve
# Oder mit Python:
python -m http.server 3000
# 2. Browser öffnen:
http://localhost:3000
# 3. Engine lädt automatisch und zeigt:
# - Terrain mit Biomen
# - Ozean mit FFT-Wellen
# - Vegetation (Bäume, Gras)
# - Charakter und Boot
# - Editor-Panels
Steuerung
| Taste | Aktion |
|---|---|
| W / A / S / D | Charakter bewegen |
| Maus | Kamera drehen |
| Space | Springen |
| Shift | Rennen |
| E | Interagieren (Boot betreten) |
| W / S (im Boot) | Vorwärts / Rückwärts |
| A / D (im Boot) | Lenken |
| F12 | Browser-Konsole für Debug-Logs |
18. Erste Entity erstellen
Ziel: Lernen, wie man ein Entity mit Komponenten erstellt und in der Welt platziert.
Beispiel: Ein „Fisch“-Entity
// Schritt 1: Entity erstellen (bekommt eine eindeutige ID)
const fish = ecs.createEntity();
// Schritt 2: Komponenten hinzufügen (Daten des Objekts)
ecs.addComponent(fish, Transform, { x: 5, y: -2, z: 10 }); // Position
ecs.addComponent(fish, NPC, { type: "fish", state: "swim" }); // KI-State
ecs.addComponent(fish, Rigidbody, { vx: 0, vy: 0, vz: 0 }); // Physik
// Schritt 3: Renderer registrieren (wie der Fisch gezeichnet wird)
renderer.register("fish", FishRenderer);
// Schritt 4: KI-Verhalten registrieren
AISystem.register("fish", FishBehaviorTree);
Transform und Vegetation, aber kein Rigidbody. Ein Boot hat Rigidbody und BoatPhysics, aber keine KI.
19. Eigenes System erstellen
Ziel: Ein neues System erstellen und in den ECS-Scheduler einbinden.
Beispiel: FloatingSystem — Objekte treiben auf dem Ozean
// Schritt 1: System-Klasse definieren
class FloatingSystem {
update(dt) {
// Alle Entities abfragen, die Transform UND Floating haben:
const entities = ecs.query(Transform, Floating);
for (const e of entities) {
const t = ecs.getComponent(e, Transform);
const f = ecs.getComponent(e, Floating);
// Wellenhöhe an der aktuellen Position abfragen:
const wave = ocean.sampler.sample(t.x, t.z);
// Y-Position auf Wellenhöhe + Offset setzen:
t.y = wave + f.offset;
}
}
}
// Schritt 2: System im Scheduler registrieren
ecs.addSystem(new FloatingSystem());
// Schritt 3: Komponente zu gewünschten Entities hinzufügen
ecs.addComponent(driftwood, Floating, { offset: 0.2 });
ecs.addComponent(buoy, Floating, { offset: 0.5 });
20. NPC erstellen
Ziel: Einen NPC mit Behavior Tree, Physik und Rendering erstellen.
Beispiel: Hai-NPC
// Schritt 1: Entity + Komponenten
const shark = ecs.createEntity();
ecs.addComponent(shark, Transform, { x: 0, y: -3, z: 0 });
ecs.addComponent(shark, NPC, { type: "shark", state: "idle" });
ecs.addComponent(shark, Rigidbody, { vx: 0, vy: 0, vz: 0 });
// Schritt 2: Behavior Tree definieren
const SharkBT = {
root: Selector([
FleeNode(), // Zuerst: Fliehen wenn bedroht?
ChaseNode(), // Dann: Beute in Sichtweite?
WanderNode() // Sonst: Ziellos umherschwimmen
])
};
// Schritt 3: KI und Renderer registrieren
AISystem.register("shark", SharkBT);
renderer.register("shark", SharkRenderer);
Behavior Tree Knoten
| Knoten | Typ | Funktion |
|---|---|---|
| Selector | Komposit | Führt Kinder aus bis einer erfolgreich ist |
| Sequence | Komposit | Führt alle Kinder aus — bricht ab wenn einer scheitert |
| FleeNode | Action | Bewegt NPC vom Spieler weg |
| ChaseNode | Action | Bewegt NPC auf Beute zu |
| WanderNode | Action | Zufällige Bewegung |
21. Fahrzeug erstellen
Ziel: Ein neues Fahrzeug basierend auf dem Boot-System erstellen.
Beispiel: Floß
// Schritt 1: Entity + Physik-Komponenten
const raft = ecs.createEntity();
ecs.addComponent(raft, Transform, { x: 0, y: 0, z: 0 });
ecs.addComponent(raft, Rigidbody, { vx: 0, vy: 0, vz: 0 });
ecs.addComponent(raft, BoatPhysics, { buoyancy: 1.2, drag: 0.3 });
// Schritt 2: Steuerung hinzufügen
ecs.addComponent(raft, BoatController, { turnSpeed: 0.5, accel: 1.0 });
// Schritt 3: Renderer registrieren
renderer.register("raft", RaftRenderer);
// Das BoatSystem sorgt automatisch für:
// - Auftrieb basierend auf Wellenhöhe (ocean.sampler)
// - Wasser-Widerstand (drag)
// - Rückstellkraft (Kippverhinderung)
22. Neues Biom erstellen
Ziel: Ein neues Biom erstellen, das Terrain-Farben und Vegetation beeinflusst.
Beispiel: Vulkan-Biom
// Schritt 1: Biom im TerrainSystem registrieren
terrain.registerBiome("volcano", {
heightMin: 0.6, // Ab Höhe 60%
heightMax: 1.0, // Bis zur Spitze
color: [0.3, 0.1, 0.1], // Dunkelrote Farbe
vegetation: "lavaRocks" // Welche Vegetation?
});
// Schritt 2: Passende Vegetation registrieren
vegetation.register("lavaRocks", LavaRockSpawner);
// Schritt 3: Noise-Regel in der Terrain-Generierung
// (in terrainGenerator.js):
if (height > 0.8) biome = "volcano";
// Weitere Biome als Referenz:
// height < 0.05 → beach (Sand)
// height < 0.35 → forest (Wald)
// height < 0.65 → mountain (Gebirge)
// height < 0.85 → snow (Schnee)
23. Shader erstellen (WGSL)
Ziel: Einen eigenen Shader in WGSL schreiben und ins Material-System einbinden.
Beispiel: Glühender Lava-Shader
// Schritt 1: Shader-Datei erstellen (lava.wgsl)
@fragment
fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
// sin() erzeugt pulsierendes Glühen basierend auf Zeit und Höhe
let glow = sin(time * 5.0 + input.position.y * 10.0) * 0.5 + 0.5;
return vec4(glow, 0.1, 0.0, 1.0); // Rot-Orange
}
// Schritt 2: Material-Datei erstellen (lava.material.json)
{
"shader": "lava.wgsl",
"uniforms": { "intensity": 1.0 }
}
// Schritt 3: Renderer registrieren
renderer.registerMaterial("lava", LavaMaterial);
// Schritt 4: Material einem Entity zuweisen
ecs.addComponent(volcanoMesh, Material, { type: "lava" });
@vertex transformiert die 3D-Position eines Punktes in Bildschirmkoordinaten. @fragment berechnet die endgültige Farbe für jeden Pixel. Beide laufen direkt auf der GPU — parallel für alle Vertices/Pixel gleichzeitig.
24. Multiplayer hinzufügen
Ziel: Multiplayer-Funktionalität in die Engine integrieren.
// Schritt 1: WebSocket-Verbindung aufbauen
client.connect("ws://localhost:8080");
// Schritt 2: Eigenen Spieler-State senden (jeder Frame)
client.sendState(playerEntity);
// Schritt 3: Anderen Spielern State empfangen und anwenden
client.onState((remoteState) => {
multiplayer.apply(remoteState);
});
// Schritt 4: Client-seitige Vorhersage (Prediction)
// Spieler-Bewegung sofort anwenden ohne Server-Bestätigung:
player.position += inputVector * dt * speed;
// Bei Server-Korrektur: Position sanft angleichen (Reconciliation)
Determinismus als Grundlage
Da die gesamte Welt prozedural aus einem Seed erzeugt wird, müssen Terrain-Daten nicht übertragen werden. Alle Clients erzeugen exakt dieselbe Welt — nur Spielerpositionen und Interaktionen werden synchronisiert.
// Alle Clients starten mit demselben Seed:
const WORLD_SEED = 42;
terrain.generate(WORLD_SEED); // Ergibt überall exakt dasselbe Terrain
ocean.init(WORLD_SEED);
vegetation.spawn(WORLD_SEED);
Teil 4 — Diagramme & Technische Spezifikationen
25. UML-Diagramme
Diese Diagramme zeigen die wichtigsten Klassen und ihre Beziehungen in der Engine — im ASCII-Format, das überall funktioniert.
ECS UML
┌──────────────┐
│ Entity │
├──────────────┤
│ id: number │
└───────┬──────┘
│ hat viele
▼
┌──────────────┐
│ Component │
├──────────────┤
│ data: object │
└───────┬──────┘
│ verarbeitet von
▼
┌──────────────┐
│ System │
├──────────────┤
│ update(dt) │
└──────────────┘
Renderer UML
┌────────────────┐
│ Renderer │
├────────────────┤
│ drawMesh() │ ← zeichnet 3D-Geometrie
│ drawBillboard() │ ← zeichnet 2D-Sprites (Gras)
│ register() │ ← meldet neuen Typ an
└───────┬────────┘
│ nutzt
▼
┌────────────────┐
│ Material │
├────────────────┤
│ shader │ ← WGSL-Programm
│ textures │ ← Bilder (Albedo, Normal)
│ uniforms │ ← Parameterwerte
└────────────────┘
Terrain UML
┌────────────────┐
│ TerrainSystem │
├────────────────┤
│ generate() │
│ getHeightAt() │
│ getBiomeAt() │
└───────┬────────┘
│ nutzt
▼
┌────────────────┐
│ Biome │
├────────────────┤
│ rules │ ← ab welcher Höhe aktiv
│ vegetation │ ← welche Pflanzen spawnen
└────────────────┘
Ocean (FFT) UML
┌────────────────┐
│ OceanSystem │
├────────────────┤
│ update(dt) │
│ setWind() │
└───────┬────────┘
│ verwendet
▼
┌────────────────┐
│ OceanSampler │
├────────────────┤
│ sample(x, z) │ ← gibt Wellenhöhe zurück
└────────────────┘
AI UML
┌────────────────┐
│ BehaviorTree │
├────────────────┤
│ root: Node │
└───────┬────────┘
│ enthält
▼
┌────────────────┐
│ Node │
├────────────────┤
│ tick() │ ← wird jeden Frame aufgerufen
└────────────────┘
26. Sequence- & Dataflow-Diagramme
Sequence-Diagramme zeigen den zeitlichen Ablauf von Ereignissen. Dataflow-Diagramme zeigen, wie Daten von System zu System fließen.
Frame-Update-Sequenz (pro Frame)
User Input
│
▼
InputSystem.update() ← Tastatur/Maus auslesen
│
▼
ECS.preUpdate() ← Entities vorbereiten
│
▼
PhysicsSystem.update() ← Physik berechnen
│
▼
AISystem.update() ← NPC-Entscheidungen treffen
│
▼
CharacterSystem.update() ← Spieler bewegen
│
▼
BoatSystem.update() ← Boot physik berechnen
│
▼
Renderer.render() ← Alles auf den Bildschirm zeichnen
Behavior Tree Tick (NPC-Entscheidung)
AISystem.update()
│
▼
BehaviorTree.root.tick()
│
├── FleeNode.tick()? ← Feind in der Nähe? → fliehe!
│ │
│ └── success → fertig (return)
│
├── ChaseNode.tick()? ← Beute in Sichtweite? → verfolge!
│ │
│ └── success → fertig (return)
│
└── WanderNode.tick() ← Sonst: zufällig wandern
Multiplayer-Sync-Sequenz
Client Input
│
▼
Client → Server: sendInput() ← Tastendruck senden
│
▼
Server: simulate() ← Spielwelt berechnen
│
▼
Server → alle Clients: sendState() ← Ergebnis verteilen
│
▼
Clients: Prediction + Reconciliation ← Eigene Vorhersage korrigieren
Terrain → Vegetation Dataflow
Noise Generator ← erzeugt zufällige Höhenwerte
│
▼
Heightmap ← 2D-Raster mit Höhenwerten
│
▼
Biome Rules ← welches Biom bei welcher Höhe?
│
▼
Vegetation Spawner ← platziert Bäume, Gras, Felsen
│
▼
Renderer ← zeichnet alles auf den Bildschirm
Weather → Ocean → Boot Dataflow
WeatherSystem ← bestimmt Windstärke und Richtung
│
▼
Wind ← Parameter für Wellen
│
▼
Ocean FFT ← berechnet Wellenbewegung per GPU
│
▼
Wave Sampler ← liefert Wellenhöhe an beliebiger Position
│
▼
Boat Physics ← Boot hebt und senkt sich mit den Wellen
27. Shader-Pipeline-Diagramm
So fließen Daten von der CPU zur GPU und schließlich auf den Bildschirm.
Vollständige Shader-Pipeline
CPU-Seite:
├─ Scene Graph ← alle Objekte in der Welt
├─ Culling ← unsichtbare Objekte ausblenden
├─ Render Graph ← Reihenfolge der Passes festlegen
└─ Command Buffers ← GPU-Befehle vorbereiten
│
│ (an GPU senden)
▼
GPU-Seite:
├─ Vertex Shader ← 3D-Punkte → 2D-Bildschirmposition
├─ Fragment Shader ← Pixel-Farbe berechnen
├─ Ocean Compute Shader ← FFT-Wellensimulation (parallel)
├─ Post-Processing ← Bloom, Tonemapping, Farbkorrektur
└─ SwapChain Present ← fertiges Bild anzeigen
Ocean FFT Pipeline
Initial Spectrum (Phillips) ← Wellenform basierend auf Wind
│
▼
Time Evolution ← Wellen bewegen sich mit der Zeit
│
▼
FFT Compute Shader ← Fast Fourier Transform (GPU)
│
▼
Displacement Map ← wie weit bewegt sich jeder Punkt?
│
▼
Normal Map ← in welche Richtung zeigt die Oberfläche?
│
▼
Ocean Rendering ← Wasser mit Licht und Reflexionen zeichnen
Teil 5 — Engine Internals
28. ECS Internals & Sparse Set
Wie speichert die Engine Millionen von Komponenten effizient? Die Antwort heißt Sparse Set — eine Datenstruktur, die O(1)-Zugriff mit Cache-Freundlichkeit kombiniert.
Sparse Set Struktur
SparseSet:
sparse[] → Entity-ID → Index in dense[]
dense[] → Index → Entity-ID
data[] → Index → Komponenten-Daten
Beispiel (Transform-Komponente für 4 Entities):
dense: [E3, E7, E1, E9] ← welche Entities haben Transform?
data: [T3, T7, T1, T9] ← deren Transform-Daten
sparse: [ -, -, 2, 0, -, -, -, 1, -, 3] ← Lookup: E1 → Index 2
Vorteile gegnüber HashMap:
- O(1) Zugriff — kein Hashing, kein Pointer-Chasing
- Cache-freundlich — alle Komponenten-Daten liegen hintereinander im Speicher
- Perfekt für GPU-Transfer —
data[]kann direkt in einen GPU-Buffer kopiert werden - Keine Garbage Collection Peaks
Warum keine Archetypes?
Andere ECS-Systeme (z.B. Unity DOTS) nutzen Archetypes — Gruppen von Entities mit exakt denselben Komponenten. Das ist schnell bei statischen Welten, aber in einer dynamischen Open-World ändern sich Komponenten ständig (NPC bekommt Feuer-Debuff, Boot wird verlassen, ...). Sparse Sets sind für diese dynamischen Welten schneller.
Query-Engine: Wie Systeme Entities finden
// Intern: ecs.query(Transform, Floating) läuft so ab:
// 1. Wähle die Komponente mit den wenigsten Einträgen (z.B. Floating mit 50 Entities)
// 2. Iteriere über deren dense[]-Array
// 3. Prüfe für jede Entity: hat sie auch Transform?
// → sparse[entity] !== -1 → ja → Entity zur Ergebnisliste hinzufügen
// Ergebnis: nur Entities, die ALLE gewünschten Komponenten haben
const entities = ecs.query(Transform, Floating);
for (const e of entities) {
// E wurde garantiert gefunden: hat Transform UND Floating
}
29. Memory Layout & Zero-GC Architecture
Die Engine wurde so designed, dass sie keine Garbage Collection ausloest. Warum ist das wichtig? GC-Pausen können in JavaScript mehrere Millisekunden dauern — das fühlt sich als Stotter-Frame an.
Structure of Arrays (SOA)
Jede Komponente speichert ihre Felder als separate Arrays — nicht als Array von Objekten:
// FALSCH (Array of Structures = AoS) — schlecht für Cache
transforms = [
{ x: 1, y: 0, z: 5 },
{ x: 2, y: 1, z: 3 },
// ... tausende Objekte → JavaScript GC muss alle verwalten
];
// RICHTIG (Structure of Arrays = SoA) — gut für Cache und GPU
Transform = {
x: Float32Array([1, 2, ...]), // alle x-Werte hintereinander
y: Float32Array([0, 1, ...]),
z: Float32Array([5, 3, ...]),
rot: Float32Array([...]), // Quaternion
};
// Vorteil: Float32Array ist kein GC-Objekt → keine GC-Pausen!
// Vorteil: GPU kann Float32Array direkt nutzen (kein Konvertieren)
GPU-Friendly Data Layout
// Vegetation Instance Buffer — direkt GPU-kompatibel:
struct Instance {
vec3 position; // 12 Bytes
vec3 scale; // 12 Bytes
vec4 rotation; // 16 Bytes (Quaternion)
uint biomeId; // 4 Bytes
};
// Ein Buffer für 10.000 Bäume = 440 KB → 1 GPU-Upload, 1 Draw Call
Zero-GC Design-Regeln
| Verboten (erzeugt GC) | Erlaubt (kein GC) |
|---|---|
new Vector3(x, y, z) im Loop | Werte direkt in Float32Array schreiben |
entities.filter(...) | Sparse-Set-Query (keine neuen Arrays) |
Object.assign({}, state) | Direkte Property-Zuweisung |
| Temporäre Arrays im Frame | Pre-allokierte, wiederverwendete Puffer |
30. Performance-Optimierungen
Frustum Culling
Objekte, die die Kamera nicht sehen kann, werden überhaupt nicht gerendert:
// Für jedes Objekt prüfen: ist es im Sichtfeld der Kamera?
for (const entity of allEntities) {
const pos = ecs.getComponent(entity, Transform);
// Ist die Bounding Sphere des Objekts im Frustum?
if (!camera.frustum.containsSphere(pos, radius)) {
continue; // überspringen → kein Draw Call → spart GPU-Zeit
}
renderer.draw(entity); // nur wenn sichtbar
}
LOD (Level of Detail)
Weit entfernte Objekte werden mit vereinfachten Modellen gerendert:
const dist = distanceTo(camera.position, tree.position);
if (dist < 50) renderMesh(tree, lod0); // Hochdetail
else if (dist < 150) renderMesh(tree, lod1); // Mitteldetail
else if (dist < 300) renderMesh(tree, lod2); // Niedrigdetail
else renderBillboard(tree); // 2D-Sprite (sehr weit)
GPU Instancing für Vegetation
Statt 10.000 einzelner Draw Calls für Bäume: ein einziger Draw Call mit einem Instance Buffer:
// Einmalig: Instance Buffer erstellen (alle Baum-Positionen)
const instanceBuffer = device.createBuffer({
size: treeCount * INSTANCE_STRIDE,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
});
// Jeden Frame: ein Draw Call für ALLE Bäume
renderEncoder.drawIndexedIndirect(treeIndexBuffer, 0);
// → GPU rendert alle treeCount Bäume parallel
Ocean FFT Optimierungen
| Technik | Erklärung |
|---|---|
| Precomputed Spectrum | Phillips-Spektrum wird einmalig berechnet, nicht jeden Frame |
| Ping-Pong Buffers | Zwei GPU-Buffer wechseln sich ab — kein Warten auf GPU |
| Shared Memory | var<workgroup> in WGSL für schnellen Thread-Austausch |
| Compute Shader | FFT läuft parallel auf tausenden GPU-Kernen |
31. GPU-Optimierungen & Debugging
Bind Group Reuse
Bind Groups (Shader-Ressourcen) werden nicht pro Frame neu erstellt — das wäre extrem teuer:
// FALSCH — neue Bind Group jeden Frame:
function render() {
const bg = device.createBindGroup({...}); // TEUER!
renderPass.setBindGroup(0, bg);
}
// RICHTIG — Bind Group einmalig erstellen und wiederverwenden:
const bindGroup = device.createBindGroup({...}); // einmal beim Start
function render() {
renderPass.setBindGroup(0, bindGroup); // günstig: nur Referenz setzen
}
Pipeline Caching
Alle GPU-Pipelines werden beim Start erstellt — nie zur Laufzeit:
// Beim Laden der Engine — einmalig:
const pbrPipeline = device.createRenderPipeline({...}); // PBR-Objekte
const oceanPipeline = device.createRenderPipeline({...}); // Wasseroberfläche
const vegetationPipeline= device.createRenderPipeline({...}); // Bäume/Gras
const skyPipeline = device.createRenderPipeline({...}); // Himmel
const postPipeline = device.createRenderPipeline({...}); // Post-Processing
Compute Shader Workgroup-Größen
// Ocean FFT: 2D-Grid (passt zu 2D-Textur)
@compute @workgroup_size(16, 16)
fn fft_pass(/* ... */) { /* ... */ }
// Vegetation Wind: 1D (einfache Listen-Verarbeitung)
@compute @workgroup_size(64)
fn wind_update(/* ... */) { /* ... */ }
Debug-Panels (im integrierten Editor)
| Panel | Zeigt |
|---|---|
| ECS Inspector | Alle Entities und ihre Komponenten |
| System Profiler | Wie viel ms jedes System benötigt |
| GPU Stats | Draw Calls, GPU-Zeit, Buffer-Größen |
| Ocean Debug | Wellenkarte, FFT-Buffer-Visualisierung |
| Terrain Debug | Heightmap, Biome-Karte |
| AI Debug | Behavior-Tree-Zustand aller NPCs |
// Logging-System:
debug.log("AI", "Shark switched to chase mode");
debug.log("Physics", "Boat capsized at (123, 0, 456)");
debug.log("GPU", "Draw calls this frame: 142");
// Profiling-Daten abrufen:
const stats = profiler.getFrame();
// → { physics: 1.2ms, ai: 0.8ms, rendering: 4.1ms, total: 6.1ms }
Teil 6 — Glossar & Schlusswort
32. Glossar
Alle wichtigen Begriffe der Engine kurz erklärt — alphabetisch sortiert.
| Begriff | Erklärung |
|---|---|
| AABB | Axis-Aligned Bounding Box. Nicht-rotierte Hüllbox für Culling und Kollision. |
| AI | Subsystem für NPC-Verhalten: Behavior Trees, Pathfinding, State Machines. |
| Archetype | In manchen ECS-Systemen: Gruppierung von Entities nach Komponenten. Diese Engine nutzt stattdessen Sparse Sets. |
| Behavior Tree | Baumstruktur zur Steuerung von NPC-Verhalten. Nodes: Selector, Sequence, Action. |
| Biome | Regelset für Terrain-Zonen (Wald, Wüste, Vulkan, Ozean). |
| Billboard | 2D-Sprite, das immer zur Kamera zeigt. Wird für Gras und weit entfernte Objekte genutzt. |
| Bind Group | WebGPU-Objekt, das Shader-Ressourcen (Uniforms, Texturen, Buffer) bündelt. |
| Buoyancy | Auftriebskraft, die Objekte im Wasser hält. Berechnet aus Wellenhöhe minus Objekt-Y. |
| Compute Shader | Shader für allgemeine GPU-Berechnungen (FFT-Ozean, Wind-Animation). |
| Component | Datencontainer eines Entities (Transform, Rigidbody, NPC). |
| Culling | Nicht-sichtbare Objekte ausschließen, bevor sie zur GPU geschickt werden. |
| Delta Time (dt) | Zeit in Sekunden zwischen zwei Frames. Macht Bewegung frame-rate-unabhängig. |
| Draw Call | GPU-Befehl zum Zeichnen eines Meshes. Jeder Draw Call hat Overhead — daher Instancing. |
| ECS | Entity-Component-System. Architektur, die Daten (Components) von Logik (Systems) trennt. |
| Entity | Eindeutige ID-Nummer für ein Objekt in der Welt. Hat selbst keine Daten. |
| FFT | Fast Fourier Transform. GPU-Algorithmus zur realistischen Ozeanwellen-Simulation. |
| Frustum | Sichtvolumen der Kamera. Objekte außerhalb werden nicht gerendert (Culling). |
| GPU Instancing | Viele gleiche Objekte mit einem einzigen Draw Call rendern. |
| Heightmap | 2D-Raster mit Höhenwerten für das Terrain. |
| LOD | Level of Detail. Weit entfernte Objekte mit niedrigerem Detailgrad rendern. |
| Material | Kombination aus Shader + Texturen + Uniform-Werten. |
| Normal Map | Textur, die die Oberflächenrichtung für Beleuchtungsberechnungen simuliert. |
| Ocean Sampler | Funktion, die die Wellenhöhe an einer (x, z)-Position zurückgibt. |
| Pipeline | WebGPU-Objekt, das Shader + Render-State definiert. Wird einmalig erstellt. |
| Post-Processing | Bildeffekte nach dem Rendering: Bloom, Tonemapping, Depth of Field. |
| Prediction | Client berechnet Bewegung sofort lokal, ohne auf Server-Bestätigung zu warten. |
| Reconciliation | Server-Korrektur der Client-Prediction. Positionen werden sanft angepasst. |
| Render Graph | Definiert Reihenfolge und Abhängigkeiten der Rendering-Passes. |
| Replication | Synchronisation von Entity-Zuständen im Multiplayer. |
| Rigidbody | Komponente für physikalische Eigenschaften: Geschwindigkeit, Masse, Kräfte. |
| Seed | Startwert für prozedurale Generatoren. Gleicher Seed = gleiche Welt. |
| Selector Node | Behavior-Tree-Node, der das erste Kind ausführt, das Erfolg zurückgibt. |
| Sparse Set | ECS-Datenstruktur für O(1)-Zugriff auf Komponenten. Cache-freundlich. |
| SOA | Structure of Arrays. Speicherlayout: alle X-Werte zusammen, alle Y-Werte zusammen, ... |
| System | Logik-Klasse mit update(dt). Verarbeitet alle Entities mit bestimmten Komponenten. |
| Transform | Komponente für Position (x, y, z), Rotation und Skalierung. |
| Tick | Ein einzelner Aufruf von update(dt) — passiert jeden Frame. |
| Uniform Buffer | GPU-Buffer für Shader-Parameter (Kamera-Matrix, Zeit, Lichtrichtung). |
| Vegetation | Subsystem für Bäume, Gras, Büsche — mit GPU-Instancing und Wind-Animation. |
| Vertex Shader | Shader, der 3D-Koordinaten eines Punktes in 2D-Bildschirmkoordinaten umrechnet. |
| WGSL | WebGPU Shading Language. Shader-Sprache für WebGPU (ersetzt GLSL). |
| Workgroup | Gruppe von parallel laufenden Threads in einem Compute Shader. |
33. Schlusswort & Credits
Vision der Engine
Die Open-World WebGPU Engine wurde mit einem klaren Ziel entwickelt: Eine moderne, modulare, prozedurale Engine zu schaffen, die vollständig im Browser läuft und dennoch die Architektur-Qualität einer AAA-Engine besitzt.
- Leicht erweiterbar (jedes System ist austauschbar)
- Für Entwickler verständlich (keine Black-Box-Abstraktion)
- Hohe Performance (ECS + Sparse Sets + GPU-Instancing)
- Moderne Technologien (WebGPU, Compute Shader, Zero-GC)
- Prozedurale Welten mit deterministischem Seed
- Multiplayer-fähig durch WebSocket + Prediction
- Integrierter Editor für Terrain, Vegetation, Shader
Ziele
| Zeithorizont | Ziele |
|---|---|
| Kurzfristig | Stabilisierung der Kernsysteme, Dokumentation vervollständigen, Editor-Tools verbessern |
| Mittelfristig | Erweiterte Multiplayer-Features, Modding-Support, Asset-Pipeline, Animationssystem, Partikelsystem |
| Langfristig | Vollständige Open-World Simulation, persistente Welten, KI-Ökosysteme, Creator-Tools |
Community & Contribution
Die Engine ist modular aufgebaut und ideal für Zusammenarbeit. Entwickler können beitragen durch: neue Systeme, neue Shader, neue NPC-Typen, neue Editor-Tools, Bugfixes, Performance-Optimierungen oder Dokumentation.
- Code klar strukturieren und kommentieren
- Naming Conventions beachten (camelCase für Variablen, PascalCase für Klassen)
- Pull Requests klein halten (ein Feature = ein PR)
- Tests hinzufügen, wenn möglich
Geplante Kapitel
- Kapitel 3: API Reference — vollständige Dokumentation aller Klassen und Methoden
- Kapitel 6: Best Practices — Naming, Folder Structure, Code Style
- Kapitel 9: Shader Cookbook — Sammlung fertiger WGSL-Shader
- Kapitel 10: Multiplayer Deep Dive — Server-Implementierung, Lag Compensation
- Kapitel 11: Editor-Erweiterungen — eigene Panels und Tools bauen
Danksagung
Ein großes Dankeschön an die WebGPU-Community, die Open-Source-ECS-Projekte (EnTT, Flecs, Bevy ECS), die Shader-Communities (Shadertoy, WGSL-Pioniere), die Game-Engine-Pioniere (Unreal, Unity, Godot) und alle Entwickler, die moderne Web-Technologien vorantreiben.