feat(world): HDRI-Himmel „Nowhere Road" mit Umgebungslicht und scharfen Kaskaden-Schatten

- Himmel: 4k-HDRI von Poly Haven (CC0, tools/assets/hdri.mjs) statt Sky-Shader. Kuppel zeigt das Foto
  ab 12°, darunter weiches Band und Dunst in Nebelfarbe; Umgebungslicht und Spiegelungen per PMREM;
  Sonne aus dem Bild gekappt und als Licht übernommen (77°, Stärke 2,5)
- Schatten: CSM mit vier Stufen (25/100/350/1000 m, je 2048²), Schutznetz für beleuchtete Materialien,
  Normal-Bias 1,5 Texel je Stufe gegen Streifen auf doppelseitigen Materialien (Figur, Autos)
- Startfigur wirft Schatten, Palmen und Laternen empfangen Schatten, Wasser als ruhige See
- Debug-Panel: Sonne, Himmelslicht, Himmelshelligkeit; Namensnennung um das HDRI ergänzt

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
knsoandClaude Opus 5 committed 2026-09-13 15:38:44 +02:00
1 parent 034b12537a
commit f76e97b69d
15 files changed
+762 -41

No files matched your search

+2 -1
View File
@@ -2,6 +2,7 @@
// - Kartenkacheln und Wahrzeichenhöhen: schon in manifest.json (Task 6), wörtlich übernommen.
// - SUV: Boris731 (Sketchfab, "Low-poly cyberpunk SUV") unter CC BY — Namensnennung Pflicht.
// - Texturen: Poly Haven, CC0.
// - HDRI "Nowhere Road" (Himmel, Umgebungslicht; tools/assets/hdri.mjs): Poly Haven, CC0.
// - Palme: Nobiax (OpenGameArt), CC0 (public/models/palm/readme.txt: "absolutely free to
// use or to modify in any kind of work").
// - Figuren/Frisuren: Quaternius, "Universal Base Characters[Standard]"-Paket, CC0 1.0
@@ -9,7 +10,7 @@
// task-11-report.md für den wörtlichen Fund.
// Jede Quelle bekommt ihren eigenen Satz mit ihrer eigenen Lizenz: keine Lizenz kürzen,
// keine zwei Lizenzen in einer Zeile vermischen, kein CC0 für die CC-BY-Quelle behaupten.
const MODEL_CREDITS = "SUV: Boris731 (Sketchfab, CC BY). Texturen: Poly Haven (CC0). " +
const MODEL_CREDITS = "SUV: Boris731 (Sketchfab, CC BY). Texturen und HDRI: Poly Haven (CC0). " +
"Palme: Nobiax (OpenGameArt, CC0). Figuren: Quaternius (CC0).";
/** Reine Textzusammensetzung, ohne DOM — so ohne Browser testbar (siehe tests/hud.test.js). */
+15 -3
View File
@@ -157,13 +157,14 @@ export async function switchCharacterAndSyncHeight(characterModule, params, heig
/**
* Baut das lil-gui-Debug-Panel (Task 11, Schritt 6): Figur, Frisur, Höhe, Hitbox anzeigen,
* Chunk-Grenzen anzeigen, Kartenbild auf dem Boden, Sichtweite (Nebel) und Sprung zu
* Wahrzeichen. requestTeleport(position) kommt aus main.js, weil nur main.js weiß, wie ein
* Chunk-Grenzen anzeigen, Kartenbild auf dem Boden, Sichtweite (Nebel), Sonne, Himmelslicht,
* Himmelshelligkeit (HDRI-Himmel, src/world/sky.js) und Sprung zu Wahrzeichen.
* requestTeleport(position) kommt aus main.js, weil nur main.js weiß, wie ein
* Sprung sicher wird (Collider am Ziel abwarten, s. dortiger Kommentar zu pendingTeleport) —
* ohne das würde ein Sprung auf ein noch nicht gestreamtes Wahrzeichen den Spieler ins
* Leere fallen und vom Auffangnetz zurückschicken lassen.
*/
export function createPanel({ player, characterModule, scene, manifest, baseUrl = "./data/vice-a", requestTeleport }) {
export function createPanel({ player, characterModule, scene, sky, sun, manifest, baseUrl = "./data/vice-a", requestTeleport }) {
const targets = teleportTargets(LANDMARKS);
const chunkGrid = buildChunkGrid();
scene.add(chunkGrid);
@@ -191,6 +192,9 @@ export function createPanel({ player, characterModule, scene, manifest, baseUrl
showChunkGrid: false,
showMapImage: false,
fogDistance: scene.fog?.far ?? 2200,
sunIntensity: sun.intensity,
skyLight: scene.environmentIntensity,
skyBrightness: sky.brightness,
landmark: targets[0]?.name ?? "",
};
@@ -215,6 +219,14 @@ export function createPanel({ player, characterModule, scene, manifest, baseUrl
.onChange((value) => setMapImageVisible(value));
worldFolder.add(params, "fogDistance", 400, 3000, 50).name("Sichtweite (Nebel)")
.onChange((value) => { if (scene.fog) scene.fog.far = value; });
// decimals: Startwert ist die gemessene Sonnenstärke (2,5023…), nicht auf die Schrittweite gerundet.
worldFolder.add(params, "sunIntensity", 0, 10, 0.1).name("Sonne").decimals(1)
.onChange((value) => { sun.intensity = value; });
worldFolder.add(params, "skyLight", 0, 10, 0.1).name("Himmelslicht")
.onChange((value) => { scene.environmentIntensity = value; });
// Setzt auch die Nebelfarbe neu, damit der Horizont zur helleren oder dunkleren Kuppel passt.
worldFolder.add(params, "skyBrightness", 0, 10, 0.1).name("Himmelshelligkeit")
.onChange((value) => sky.setBrightness(value));
const teleportFolder = gui.addFolder("Sprung zu Wahrzeichen");
teleportFolder.add(params, "landmark", targets.map((t) => t.name)).name("Wahrzeichen")