Files
wds/notes/renderer-plan.md
2026-06-13 02:48:03 +02:00

114 lines
5.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# wds — Renderer-Plan
Stand: 2026-06-12. Entscheidungen aus der irl3d-Analyse, festgehalten bevor
der Projekt-Einstieg über die Kern-Systeme (Signals/KV, CLI-Phase) läuft.
## Ziel
PS1-artiger Look, von vornherein nativ und simpel implementiert statt
emuliert: affines Texture-Mapping, Integer-Vertex-Koordinaten, 16-Bit-Farbe.
Niedrige interne Auflösung (320×240), hochskaliert aufs Fenster.
Referenz ist `../irl3d`: gleiche Engine-Idee (datengetrieben, eine
OBJ-Szene, Signals/KV/Ink), aber dort als 8-Bit-Palette-CPU-Softwarerenderer
in Q16.16. wds ersetzt den Grafikpfad durch echtes GPU-Rastern.
## Hardware-Target
Mittelklasse-Laptop von 2015 (Klasse: i5-5200U + Intel HD 5500).
Konsequenzen:
- Zwei schwache CPU-Kerne → Transformation gehört auf die GPU, die CPU
bleibt für Physik und Spiellogik frei.
- Per-Frame-Vertex-Streaming (CPU-Transform, GTE-Stil) wäre auf alten
iGPU-Treibern der riskante Pfad (Buffer-Upload-Stalls) → statische
Vertex-Buffer, einmal hochgeladen.
- wgpu-Backend: Vulkan primär (ANV unterstützt Gen8+), GL als Fallback
einplanen. Keine Features jenseits von Baseline-WebGPU benutzen.
## Entscheidungen
1. **wgpu direkt, kein pbio.** pbio ist eine Abstraktion für
Software-Renderer (8-Bit-Framebuffer + Palette-Blit) — für echtes
GPU-Rastern Verschwendung. winit + wgpu direkt im Projekt.
2. **Vertex-Shader-Transform mit Pixel-Snapping.** Model→View→Projektion im
Vertex-Shader. Danach `xy/w` auf das Pixelraster der internen Auflösung
runden und wieder mit `w` multiplizieren → Vertex-Jitter wie auf der PS1,
authentisch gerechnet, nicht nachgestellt.
3. **Affines Texture-Mapping via `@interpolate(linear)`.** WGSL kann die
perspektivische Korrektur pro Varying abschalten. UVs (und ggf.
Vertex-Colors) linear interpolieren → das Textur-Wobbeln kommt gratis.
Gegenmittel bleibt wie auf der PS1 das Unterteilen großer Flächen im
Asset, nicht Shader-Tricks.
4. **RGB555 + Ordered Dithering im Fragment-Shader.** wgpu hat kein
555-Render-Target-Format; Render-Target bleibt RGBA8 bei 320×240, aber
der Fragment-Shader quantisiert auf 5 Bit pro Kanal mit 4×4-Bayer-Dither.
Die Farbreduktion ist echt, nur das Speicherformat ist modern.
Das 16. Bit entspricht dem PS1-Maskbit → bei uns **1-Bit-Alpha**
(Alpha-Test, kein Blending) — Fortführung der Index-0-Transparenz
aus irl3d.
5. **Z-Buffer: ja.** Ordering-Tables wären nur eine technische Hürde, kein
ästhetischer Gewinn. Standard-Depth-Buffer auf dem internen Target.
6. **UVs aus dem OBJ (`vt`).** irl3d ignoriert `vt` und mappt planar über
Welt-Koordinaten; wds liest echte UVs. PS1-Spiele hatten handgemachte
UVs — das gehört zum Look.
7. **Licht: gebacken zuerst.** Nur Gouraud-Shading wäre zu einseitig.
Plan: Lichteffekte in die Szene backen (Blender → Vertex-Colors),
im Shader mit der Textur multiplizieren und mitquantisieren. Dynamische
per-Vertex-Punktlichter (im Vertex-Shader, additiv auf die gebackene
Farbe) als spätere Stufe. Kein Per-Pixel-Lighting.
## Pipeline-Skizze
```
statische VBOs (Pos i16/f32, UV, Vertex-Color)
→ Vertex-Shader: Model·View·Proj, dann Snap auf 320×240-Raster
→ Raster (Hardware), UV/Color @interpolate(linear)
→ Fragment: Texel-Fetch (nearest), × Vertex-Color,
Alpha-Test (1 Bit), RGB555-Quantisierung + 4×4-Bayer
→ RGBA8-Offscreen 320×240 + Depth
→ Blit-Pass: Nearest-Upscale mit Letterbox (4:3) aufs Fenster
```
Draw-Granularität wie irl3d: pro Instance ein Draw (Uniform: Pos + Yaw),
Material-Batching später, wenn es weh tut.
## Offene Punkte
- **Asset-Pipeline:** Texture-Painting in Blender ist mühsam. Kandidaten
für Textur-pro-Fläche-Workflows: TrenchBroom (Map-Format-Import nötig),
Crocotile 3D, Blockbench. Entscheidung wenn der Szenen-Loader ansteht.
- **Texturformat:** direkte True-Color-Texturen statt 8-Bit-CLUT-TGA —
wir arbeiten durchgehend mit 15-Bit-True-Color, es gibt keine globale
Palette mehr (das `load_palette`-Verb ist entfernt). Offen bleibt nur
das Dateiformat (TGA/PNG) und ob Quantisierung beim Import oder erst
im Shader passiert.
- **Fog / Distance-Fade:** irl3d hat hartes Pop-In bei `CULL_DIST`
in wds von Anfang an als Fade Richtung Sky-Farbe einplanen (im Shader
trivial, passt zur PS1).
- **Skybox:** Cubemap wie irl3d oder schlicht Far-Plane-Farbe + Fog.
- Interne Auflösung umschaltbar lassen (QVGA/VGA wie irl3d).
## Aus irl3d portierbar (renderer-unabhängig)
signals, kv, ink, story_ctrl, game (ActionCtx-Idee), physics, pick,
camera, obj-Loader (um `vt` erweitern), tga, wav, audio, assets,
fix/math (Q16.16 bleibt für Spiellogik/Physik sinnvoll; der Renderer
selbst rechnet f32 — die GPU kennt ohnehin nur Floats).
## Projektphasen
1. **CLI-Phase (läuft):** KV-Store, Signal/Action-Dispatcher,
Ink-Integration, Konsolen-REPL als erstes Frontend. Deferred Actions
als Daten (`hide_object`, `play_sound`), damit spätere Subsysteme sie
konsumieren, ohne dass der Kern sie kennt.
2. Datenmodell (Mesh/Instance/Material/Scene + TOML-Formate) headless.
3. Renderer nach diesem Plan, gegen das stehende Datenmodell.
4. Physik, Audio, Panel-UI andocken (Ports aus irl3d).