Was macht Perspektiv-Illusion?
Der Stil in einem Absatz
Eine waagrecht ausgerichtete Kamera mit einem sehr langen Objektiv (ca. 2° Gesichtsfeld aus 400 m Entfernung) drückt eine tiefe Szene flach – sie sieht aus wie eine gezeichnete Collage. Dann vollführt sie einen Dolly-Zoom: Das Objektiv wird weiter, während die Kamera nach vorne fährt, sodass das Motiv gleich groß bleibt, aber der Raum dahinter sich aufdehnt. Die Kamera schwenkt auch um Ecken. Jede dieser Einstellungen beginnt flach und öffnet sich in 3D. Während der flachen Ansicht tauschen Objekte unbemerkt ihre Tiefe – sobald die Tiefe zurückkommt, ist aus einer geraden Reihe eine Diagonale geworden. Alles ist als saubere Tusche-Line-Art mit flacher Zweiton-Schattierung, schraffierten Schlagschatten und einem gemalten Himmel gezeichnet, und das Ganze loopt nahtlos.
Schnellstart
mkdir -p ~/Movies/edits/<datum>-<name>/stills && cd $_
printf '{"type":"module"}' > package.json && npm i [email protected] [email protected]
cp -r ~/simcraft/skills/video/perspective-illusion/templates/* .
cp scramble_night.html scene.html # oder die nächstgelegene Vorlage (siehe unten), dann editieren
node render.mjs scene.html x --stills=0.3,1.5,3,6,8.5 # Einzelbilder jeder Einstellung, in stills/
node render_fast.mjs scene.html preview.mp4 --preview # gesamter Loop, halbe Größe, ca. 1,5 Min.
node render_fast.mjs scene.html final.mp4 # volle Qualität, ca. 30 Min. für 10 s
python3 ~/simcraft/skills/video/vox-explainer/scripts/motion_check.py final.mp4
python3 ~/simcraft/skills/video/perspective-illusion/scripts/blink_map.py ffmpeg final.mp4 blink.png
Behebe Probleme an Einzelbildern und Vorschauen. Die vollständige Render-Ausführung starte erst, wenn die Vorschau freigegeben ist. Setze $FFMPEG, falls ffmpeg nicht im PATH ist.
Vorlagen
Starte mit der Vorlage, die der neuen Szene am nächsten kommt. Behalte ihr Kamerarig, die Timeline, den Post-Pass und die Render-Hooks bei, und ersetze nur den Szenenabschnitt (alles zwischen // ---------- kit und // ---------- cameras).
| Vorlage | Szene | Renderer |
|---|---|---|
street.html, street_plus.html | Tagstraße mit Masten, Kegeln, einem Zebrastreifen und rotem Himmel. street_plus fügt Details und Eckansichten hinzu. | render_smooth.mjs |
market.html, market_real.html | Supermarktgang, gezeichnet und realistisch. | render_smooth.mjs (market.html: render.mjs) |
scramble_night.html | Große Kreuzung bei Nacht mit Regen, Menschenmenge, nassen Reflexionen und Werbetafeln. Enthält alle Beschleunigungen. | render_fast.mjs |
Für eine neue Szene ist scramble_night.html die beste Basis für die Engine, da es als einziges die Hooks für den schnellen Renderer hat. Entferne ihre Nacht-Extras (siehe reference.md) für einen Tageslook.
Schritt 1: Eine Szene bauen, die das Auge täuscht
Jeder Ort funktioniert, wenn er Folgendes hat:
- Eine lange Achse. Richte die Szene entlang -z aus (eine Straße, ein Gang, ein Bahnsteig, ein Korridor), mit der Kamera, die darauf blickt. Die Tiefe entlang dieser Achse wird durch das Tele versteckt.
- Wiederholte Dinge in verschiedenen Tiefen. Masten, Lampen, Regale, Säulen, Schilder, Bäume, Menschen. Bei langer Brennweite stapeln sie sich zu einer Ebene. In einer Linie aufgestellte Masten verschmelzen zu einem, und Schildstapel an fernen Fassaden türmen sich zu einer Wand auf.
- Tausch-Objekte. 3 bis 6 ähnliche Objekte (Kegel, Menschen, Mülltonnen, Stühle, Kisten), deren Anordnung zwischen einer Reihe und einer Diagonale wechselt. Sie können in der flachen Ansicht (
cs) auch größer gezeichnet werden, sodass der Maßstab im flachen Zustand stimmt. - Realwelt-Maßstäbe in Metern. Die Objektive und Fokusebenen funktionieren nur bei echten Maßstäben: ein 0,75 m hoher Kegel, 1,7 m große Menschen, eine 3,2 m hohe Etage.
- Details dort, wo die Eckansichten hinschauen. Jede Eckansicht zeigt Gebäudeseiten, Rückseiten von Objekten und den Boden. Fülle sie: Seitenfenster, Abflüsse, Kabel, Bordsteine, den Berührungsschatten unter einem Auto. Flache Ansichten verzeihen Schlamperei, Eckansichten decken sie auf.
- Jedes Detail muss entweder Teil einer Oberfläche sein (Texturen, gestapelte Hüllen) oder mindestens 0,4 m Abstand zu anderen Oberflächen haben. Aus 400 m Entfernung flimmern Oberflächen, die nur wenige Zentimeter auseinanderliegen.
Schritt 2: Kamera-Choreografie
Die Kamera wird niemals geneigt. Sie bleibt waagrecht, sodass Vertikalen vertikal bleiben – wie in einer Architektenzeichnung. Jede Einstellung ist ein einfaches Objekt:
{ x, y, zf, W, fov, H, el, yaw, lay, cs }
| Schlüssel | Bedeutung |
|---|---|
x, y | Seitliche Position und Höhe der Kamera (m) |
zf, W | Die Fokusebene und die dort eingefasste Breite (m). Wenn man W beibehält und fov ändert, erhält man den Dolly-Zoom. |
fov | Horizontaler Öffnungswinkel. Ca. 2° ergibt flach, 25–90° erzeugt Tiefe. |
H | Lens-Shift: Wo der Horizont liegt (-1 unten, 0 Mitte, 1 oben). |
el | Blick nach unten durch Anheben der Kamera und Verschieben des Objektivs, niemals durch Neigen. |
yaw | Schwenken um den Fokuspunkt – für Eckansichten. |
lay, cs | Anordnung der Tausch-Objekte (0 Reihe, 1 Diagonale) und ihr Maßstab in der flachen Ansicht. |
Bewährtes Einstellungs-Vokabular:
| Einstellung | Einstellungen |
|---|---|
| Flach | fov ca. 2, H 0. Die gesamte Szene wirkt wie ein Aufriss. |
| Froschperspektive | Knöchelhöhe, fov 90, Horizont tief (H ca. -0.7). |
| Hoch | Kamera hoch oben bei mittlerer Brennweite, Horizont über dem Bildrand. |
| Eckansicht | yaw ±18 bis 35°, el 9 bis 50°. Kommt als Paar: zuerst eine Tele-Version (fov 4), dann die gleiche Ansicht weit (fov 25 bis 45). |
| Diagonal und niedrig | Mittlere Brennweiten, die die Tausch-Objekte in der Tiefe zeigen. |
Loop-Struktur. Flach, dann eine tiefe Ansicht, dann eine Eckansicht (flach dann 3D), zurück zu flach (hier die Objekte tauschen), eine hohe Diagonale, die andere Eckansicht, eine niedrige Einstellung, dann wieder flach. Der Loop endet auf der gleichen Einstellung, mit der er beginnt.
Timing. Alle Parameter durchlaufen eine monotone kubische (Fritsch-Carlson) Kurve über die Key-Zeiten, sodass die Geschwindigkeit nie springt. Interpoliere W und fov im logarithmischen Raum. Füge winzige „Drift“-Einstellungen hinzu (W × 0.96), damit Haltepunkte atmen, statt einzufrieren. 10 s fühlen sich flüssig an, 7 s wirken gehetzt. Um die Länge zu ändern, skaliere die Key-Zeiten statt sie neu zu schreiben.
Schummeln während der flachen Phasen. Ändere lay nur während flacher Halte. Wenn sich die Kamera in der Tiefe bewegt, während sich Objekte bewegen, wird der Trick sichtbar.
Die erste Eckansicht sollte auf die Seite gehen, die das Helden-Objekt (ein Auto, ein Schild) nicht verdeckt. So zeigt die Enthüllung etwas Neues.
Schritt 3: Der Look
Die Seite rendert vier Durchgänge: Farbe, Normalen mit Tiefe, Flachfarbe (ohne Beleuchtung) und eine Sonnen-nur-Schattenmaske. Ein finaler Shader kombiniert sie.
- Tuschel-Linien dort, wo sich Tiefe (zweite Differenz), Normalen oder Flachfarbe ändern. Mache Linien in der Nähe schwerer. Bodenmarkierungen erhalten dünne graue Linien. Lasse Farblinien ausblenden, wo ein Pixel viele Meter abdeckt oder ferne Texturen zu Rauschen werden.
- Zweiton-Schattierung. Hell gegen Schatten, ohne Abstufungen. Setze die Maskenlichtquelle auf
2*PI, weil drei.js den Lambert durch pi teilt – ohne wird alles zu Schatten. - Schraffiere nur Schlagschatten, mit weltraumverankerter (niemals bildschirmraum-basierter) Schraffur und ausgeblendet durch
fwidth. - Malen den Himmel nach Blickrichtung, nicht nach Bildschirmposition, damit Wolken beim Kamerafahren stehen bleiben. Halte Wolkenkanten weich.
- Palette. 6 bis 10 flache Farben plus eine Akzentfarbe (ein roter Himmel, ein gelbes Schild). Um eine Referenz zu treffen, nimm die Farben aus ihren Bildern mit k-Means.
- Licht. Eine einzige, tief stehende, harte Sonne, so platziert, dass die Hauptmotive beleuchtet bleiben und lange Schatten über den Boden werfen. Eine Sonne von der hohen Seite begräbt die Szene in einem großen Schatten.
Für eine Nacht-Variante (Mondlicht, beleuchtete Fenster, nasse Reflexionen, Glühen, Regen, Grading), eine Menschenmenge oder Werbetafeln und Leuchtschriften siehe reference.md.
Um aus scramble_night.html einen Tageslook zu erhalten:
- Ersetze den Mond durch eine warme Sonne und erhöhe das Umgebungslicht.
- Entferne die Punktlicht-Schleife.
- Entferne den nassen-Boden-Block, die Dunstlinie und das Nacht-Grading aus dem Post-Shader.
- Entferne den Regen und setze
emissiveIntensitybei Fenstern auf 0. - Setze einen Taghimmel
skyAt.
Schritt 4: Detail-Durchgänge (was es großartig macht)
Erste Versionen wirken immer leer. Großartige Versionen entstehen durch gezielte Durchgänge, die jeweils an Einzelbildern geprüft werden, bevor der nächste beginnt.
- Formen. Hauptmassen im echten Maßstab, die lange Achse, die Tausch-Objekte und die Kamera-Timeline. Prüfe, dass die flache Einstellung wirklich flach wirkt und jede Bewegung etwas offenbart.
- Struktur. Alles, was ein realer Ort hat, aufgebaut aus realweltlicher Logik. Für eine Straße bedeutet das: Bordsteine, Abflüsse, Kanaldeckel, Masten mit ihren Armen und Kästen, Schilder auf beiden Seiten, Zäune, Mülltonnen, Getränkeautomaten, Fahrradständer, Oberleitungen, Fensterrahmen mit Fensterbänken und Klimaanlagen. Sieh dir Fotos der echten Art von Ort an und liste auf, was dort vorhanden ist, bevor du baust.
- Abnutzung und Geschichte. Kleine Dinge, die bewohnt wirken lassen: Aufkleber, Klebeband, Plakate mit codiertem Text, Pfützenflecken, ein angelehntes Fahrrad, Wäsche, eine Katze, eine Kaffeetasse. Strebe 3 oder mehr pro Oberfläche an, auf der die Kamera verweilt.
- Eckansichten-Prüfung. Rendere für jede Eckansicht das weite Standbild und fülle, was dort kahl wirkt. Meistens sind das Gebäudeseiten, die Rückseiten von Schildern, Dächer (Tanks, Geländer, Antennen) und der Boden zwischen Objekten.
- Licht und Schatten. Stelle sicher, dass jedes Objekt einen lesbaren Schatten wirft, füge Berührungsschatten unter geparkten Dingen hinzu und bekomme die Schattenseite jeder Hauptform in den Zweiton-Schritt. Schatten, die nur in einer Ansicht auftauchen, sind eine Belohnung für die Kamerafahrt.
- Leben (optional) . Gehende Personen, ein Zug oder Autos mit konstanter Geschwindigkeit, Bildschirme, Vögel, Fahnen. Alles loopt eine ganze Anzahl von Malen.
- Feinschliff. Palettenharmonie, Linienstärke, Himmel, Vignette und Grading. Dann die Blink-Prüfung am Finalen.
Dichteregel: Aus der flachen Tele-Einstellung sollte das Bild von Rand zu Rand voll wirken, wie eine dichte Illustration, ohne große leere Wand- oder Bodenfläche, es sei denn, sie dient als bewusste Ruhepause für das Auge.
Die Qualitätslatte
Prüfe vor jedem vollständigen Render Einzelbilder zu jedem Key-Zeitpunkt (und auf halbem Weg zwischen Bewegungen) anhand dieser Liste. Behebe alles, was nicht besteht, und prüfe dann erneut.
- Die flache Einstellung wirkt als flache Zeichnung. Nichts verrät die Tiefe.
- Jede Bewegung endet mit einer Enthüllung: einer versteckten Diagonale, einer Seitenstraße, Objekten, die weit auseinander lagen.
- Eckansichten beginnen flach und öffnen sich, und die erste wendet sich vom Helden-Objekt ab.
- Linien sind scharf und gleichmäßig, keine Doppellinien, keine Tusche auf leuchtenden Flächen, kein Rauschen auf fernen Texturen.
- Zweiton-Schattierung ist deutlich auf jeder Hauptform, und Schraffur erscheint nur in Schlagschatten.
- Nichts wirkt wie ein nackter Grundkörper. Jeder Kasten hat Verkleidung, Nähte, einen Sockel oder eine Beschriftung.
- Text ist lesbar und richtig geschrieben, in echten Schriften, mit erfundenen Marken.
- Der Himmel passt zur Lichtrichtung und -farbe der Szene.
- Die Palette ist begrenzt und harmonisch, mit einem klaren Akzent.
- Die Loop-Naht ist unsichtbar (Frame 0 gleich letztem Frame), und nichts springt.
- Die Vorschau hat kein Ruckeln. Im Finale ist die
blink_map-Zahl niedrig, und was blinkt, ist beabsichtigte Bewegung (Regen, Bildschirme).
Wenn du Einzelbilder überprüfst, betrachte sie wie ein Illustrator: Benenne die drei schwächsten Bereiche jedes Bildes, behebe sie und wiederhole. Höre nicht bei „es funktioniert“ auf.
Schritt 5: Rendern und prüfen
render_fast.mjsrendert jedes Bild auf der GPU als Mittelwert mehrerer verrauschter Unter-Renderings: 4 für Anti-Aliasing und bis zu 32 bei schnellen Bewegungen für lückenlose Bewegungsunschärfe, dimensioniert danach, wie weit sichwindow.probe-Punkte auf dem Bildschirm bewegen. Verwende--previewfür halbe Größe bei 30 fps.--from,--tound--fpssind ebenfalls verfügbar.render_smooth.mjsliefert die gleiche Qualität für Seiten ohnerenderMany, mittelt aber in ffmpeg, daher mehrere Male langsamer.- Statische Meshes nach Material zusammenführen (
BufferGeometryUtils.mergeGeometries). Eine Szene ging von 7.654 Draw-Calls auf 530, etwa 7× schneller. Alles, was sich bewegt, von der Zusammenführung ausnehmen. - Das Finale prüfen mit
motion_check.py(nichts friert ein) undblink_map.py, das Pixel zählt, die in nahezu unbewegten Bildern flackern. Vergleiche den Wert vor und nach einer Änderung. Vorschauen schimmern konstruktionsbedingt – beurteile Blinken nur im Finalen.
Der Seitenvertrag
Jede Szenenseite funktioniert mit den Renderern, wenn sie Folgendes bereitstellt:
| Hook | Funktion |
|---|---|
#frame | Die Canvas, die screenshottet wird. |
window.READY | Gesetzt, sobald Assets und Schriften geladen sind. |
window.render(ms) | Zeichnet den Frame zur Zeit ms. |
window.probe(ms) | Gibt Bildschirmpositionen einiger Weltpunkte zurück, verwendet zur Dimensionierung der Bewegungsunschärfe. |
window.setJitter(x, y) | Subpixel-Linsenversatz in Pixeln, gesetzt über projectionMatrix.elements[8] und [9]. |
window.renderMany([[ms, jx, jy], ...]) | Mittelt mehrere Renderings in einem Aufruf (erforderlich für render_fast). |
?scale=.5 | Rendert bei halber Größe. |
Alles Animierte muss eine reine Funktion von ms sein, periodisch in der Loop-Länge. Verwende ganze Zahlen von Zyklen pro Loop für Schritte, Krabbelbewegungen und Regen.
Blinken und Störungen
| Ursache | Behebung |
|---|---|
| Alles im Bildschirmraum (Wackeln, Schraffur, Rauschen, Dunst nach Kameradistanz) | Verankere es an der Welt oder an der Blickrichtung. |
| Oberflächen wenige cm auseinander | Fasse sie zu einer Oberfläche zusammen oder halte sie mindestens 0,4 m auseinander. |
| Überlappende koplanare Ebenen (Fahrbahnmarkierungen, Bodenplatten) | Male Markierungen auf eine Ebene und lasse Platten Kante an Kante treffen. |
| Subpixel-Details (Schienen, Gitter, Kabel, Regen) | Jitter-Anti-Aliasing auf jedem Frame und blasse dichte Linien nach Metern pro Pixel ausblenden. |
| Ein Render pro Frame | Niemals im Finalen tun. |
| Harte Kanten an Wolken oder Reflexionen | Weichzeichnen und Effekte an Bildrändern ausblenden. |
| Bewegte Dinge, die wickeln oder springen | Wickel sie während der schnellsten Kamerabewegung des Loops, ermittelt mit window.probe. |
Fallstricke
- Ein
//-Kommentar, der zu einzeiligem Code hinzugefügt wird, verschluckt den Rest der Zeile (einscene.add, ein Licht). Verwende/* */. - Helfer, die zur Szenenwurzel hinzufügen (wie
plane()), ignorieren die aktuelle Gruppe und können oben auf Dingen landen. Baue innerhalb von Gruppen. - Halte emissive Flächen (Bildschirme, Schildtafeln) aus dem Flachfarb- und Schattendurchgang heraus, sonst werden alle Zeichen eingetuscht und abgedunkelt.
ffmpeg -stream_loop ... -c copyerzeugt Schleifen, die einige Player früh beenden. Stattdessen neu encodieren.- Für X sind Side-by-Sides bei 32:9 zu breit zum Hochladen. Staple sie vertikal.
- Verwende nur Originalcharaktere und erfundene Marken. Kopiere die Technik einer Referenz, niemals ihr Kunstwerk.