Que fait Illusion de perspective ?
Le style, en un paragraphe
Une caméra de niveau avec un très long objectif (environ 2° depuis 400 m) aplatit une scène profonde en ce qui ressemble à un dessin plat. Ensuite, elle effectue un dolly-zoom : l'objectif s'élargit tandis que la caméra avance, de sorte que le sujet reste de la même taille mais que l'espace derrière lui s'ouvre en profondeur. La caméra pivote également pour des vues de coin. Chacune commence plate et s'ouvre en 3D. Pendant que la vue est plate, les objets glissent en profondeur là où personne ne peut le voir, de sorte que lorsque la profondeur revient, une rangée est devenue une diagonale. Tout est dessiné comme un trait d'encre propre avec un ombrage bicolore plat, des ombres portées hachurées et un ciel peint, et l'ensemble boucle de manière transparente.
Démarrage rapide
mkdir -p ~/Movies/edits/<date>-<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 # ou le modèle le plus proche (voir ci-dessous), puis éditez
node render.mjs scene.html x --stills=0.3,1.5,3,6,8.5 # images de chaque plan, dans stills/
node render_fast.mjs scene.html preview.mp4 --preview # boucle entière, demi-taille, environ 1,5 min
node render_fast.mjs scene.html final.mp4 # pleine qualité, environ 30 min pour 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
Corrigez les choses sur les images et les prévisualisations. Ne rendez en pleine qualité qu'une fois la prévisualisation approuvée. Définissez $FFMPEG si ffmpeg n'est pas dans le PATH.
Modèles
Partez du modèle le plus proche de la nouvelle scène. Conservez son rig de caméra, sa timeline, son post-pass et ses hooks de rendu, et remplacez uniquement la section de scène (tout ce qui se trouve entre // ---------- kit et // ---------- cameras).
| Modèle | Scène | Rendu |
|---|---|---|
street.html, street_plus.html | Rue de jour avec poteaux, cônes, un passage piéton et un ciel rouge. street_plus ajoute des détails et des vues de coin. | render_smooth.mjs |
market.html, market_real.html | Une allée de supermarché, dessinée et réaliste. | render_smooth.mjs (market.html : render.mjs) |
scramble_night.html | Un grand carrefour de nuit sous la pluie, avec une foule qui marche, des reflets mouillés et des écrans pub. Il a toutes les accélérations. | render_fast.mjs |
Pour une nouvelle scène, scramble_night.html est la meilleure base pour le moteur, car c'est le seul qui a les hooks de rendu rapide. Supprimez ses extras nocturnes (voir reference.md) pour un look de jour.
Étape 1 : Construire une scène qui peut tromper l'œil
N'importe quel lieu fonctionne s'il possède ces éléments :
- Un long axe. Disposez la scène le long de -z (une rue, une allée, un quai, un couloir), la caméra regardant le long de celui-ci. La profondeur le long de cet axe est ce que le téléobjectif cache.
- Des choses répétées à différentes profondeurs. Poteaux, lampadaires, étagères, colonnes, panneaux, arbres, personnes. Avec un long objectif, ils s'empilent en un seul plan. Les poteaux alignés fusionnent en un seul, et les piles de panneaux sur les façades lointaines s'empilent en un mur.
- Objets échangeables. 3 à 6 objets similaires (cônes, personnes, poubelles, chaises, boîtes) dont la disposition passe entre une rangée et une diagonale. Ils peuvent également être dessinés plus grands dans la vue plate (
cs), de sorte que l'échelle soit correcte lorsqu'elle est aplatie. - Échelle réelle en mètres. Les objectifs et les plans de mise au point n'ont de sens qu'à l'échelle réelle : un cône de 0,75 m, des personnes de 1,7 m, un étage de 3,2 m.
- Des détails là où les vues de coin regardent. Chaque vue de coin révèle les côtés des bâtiments, l'arrière des objets et le sol. Remplissez-les : fenêtres latérales, drains, câbles, bordures de trottoir, l'ombre de contact sous une voiture. Les vues plates cachent le bâclé et les vues de coin l'exposent.
- Faites en sorte que chaque détail fasse partie d'une surface (textures, manchons empilés), ou gardez les surfaces séparées à au moins 0,4 m d'écart. Depuis 400 m, les surfaces distantes de quelques centimètres scintillent.
Étape 2 : Chorégraphier la caméra
La caméra n'est jamais inclinée. Elle reste de niveau pour que les verticales restent verticales, comme dans un dessin d'architecte. Chaque plan est un objet simple :
{ x, y, zf, W, fov, H, el, yaw, lay, cs }
| Clé | Signification |
|---|---|
x, y | Position latérale et hauteur de la caméra (m) |
zf, W | Le plan de mise au point et la largeur cadrée à cet endroit (m). Garder W tout en changeant fov est le dolly zoom. |
fov | Angle horizontal de l'objectif. Environ 2° est plat, 25 à 90° est profond. |
H | Décalage de l'objectif : où se trouve l'horizon (-1 en bas, 0 au milieu, 1 en haut). |
el | Regarder vers le bas en levant la caméra et en décalant l'objectif, jamais en inclinant. |
yaw | Pivoter autour du point de mise au point pour les vues de coin. |
lay, cs | Disposition des objets échangeables (0 rangée, 1 diagonale) et leur échelle dans la vue plate. |
Vocabulaire de plans qui fonctionne :
| Plan | Paramètres |
|---|---|
| Plat | fov environ 2, H 0. Toute la scène se lit comme une seule élévation. |
| Vue de ver | Hauteur du genou, fov 90, horizon poussé vers le bas (H environ -0,7). |
| Haut | Caméra en hauteur avec un objectif moyen, horizon au-dessus du cadre. |
| Vue de coin | yaw ±18 à 35°, el 9 à 50°. Elle se présente par paire : une version téléobjectif (fov 4) d'abord, puis la même vue en grand angle (fov 25 à 45). |
| Diagonale et basse | Objectifs moyens qui montrent les objets échangeables en profondeur. |
Structure de la boucle. Plat, puis une vue profonde, puis une vue de coin (plate puis 3D), retour au plat (échanger les objets ici), une diagonale haute, l'autre vue de coin, un plan bas, puis plat à nouveau. La boucle se termine sur le plan sur lequel elle a commencé.
Temporalisation. Tous les paramètres passent par une courbe monotone cubique (Fritsch-Carlson) sur les temps clés, de sorte que la vitesse ne saute jamais. Interpolez W et fov dans l'espace logarithmique. Ajoutez de minuscules plans de "dérive" (W × 0,96) pour que les arrêts respirent au lieu de geler. 10 s semblent fluides, tandis que 7 s semblent précipitées. Pour changer la longueur, mettez à l'échelle les temps clés plutôt que de les réécrire.
Tricher pendant le plat. Ne changez lay que pendant les arrêts plats. Si la caméra se déplace en profondeur pendant que les objets bougent, l'astuce se voit.
La première vue de coin doit aller du côté que l'objet héros (une voiture, un panneau) ne couvre pas. Ainsi, la révélation montre quelque chose de nouveau.
Étape 3 : Le look
La page rend quatre passes : couleur, normales avec profondeur, couleur plate (sans éclairage) et un masque d'ombre solaire uniquement. Un shader final les combine.
- Lignes d'encre là où la profondeur (seconde différence), les normales ou la couleur plate changent. Rendez les lignes plus lourdes de près. Les marquages au sol obtiennent de fines lignes grises. Atténuez les lignes de couleur là où un pixel couvre plusieurs mètres, ou les textures lointaines deviennent du bruit.
- Ombrage bicolore. Lumière contre ombre, sans dégradés. Réglez la lumière du masque sur
2*PI, car three.js divise Lambert par pi, et sans cela tout devient ombre. - Hachurer uniquement les ombres portées, avec des hachures ancrées dans l'espace monde (jamais l'espace écran) et atténuées par
fwidth. - Peignez le ciel par direction de vue, pas par position d'écran, de sorte que les nuages restent en place lorsque la caméra bouge. Gardez les bords des nuages doux.
- Palette. 6 à 10 couleurs plates plus une accentuation (un ciel rouge, un panneau jaune). Pour correspondre à une référence, prenez les couleurs de ses images avec k-means.
- Lumière. Un soleil bas et dur placé de sorte que les sujets principaux restent éclairés et projettent de longues ombres sur le sol. Un soleil venant du côté haut enterre la scène dans une grande ombre.
Pour une variante de nuit (clair de lune, fenêtres éclairées, reflets mouillés, lueur, pluie, étalonnage), une foule de marcheurs, ou des écrans pub et des panneaux, voir reference.md.
Pour obtenir un look de jour à partir de scramble_night.html :
- Remplacez la lune par un soleil chaud et augmentez la lumière ambiante.
- Supprimez la boucle de lumières ponctuelles.
- Supprimez le bloc de sol mouillé, la ligne de brume et l'étalonnage nocturne du shader de post-traitement.
- Supprimez la pluie et mettez
emissiveIntensityà 0 sur les fenêtres. - Peignez un
skyAtde jour.
Étape 4 : Passages de détail (ce qui rend génial)
Les premières versions semblent toujours vides. Les grandes versions viennent de passages délibérés, chacun vérifié sur des images avant que le suivant ne commence.
- Formes. Masses principales à l'échelle réelle, l'axe long, les objets échangeables et la timeline de la caméra. Vérifiez que le plan plat se lit vraiment comme plat et que chaque mouvement révèle quelque chose.
- Structure. Tout ce qu'un lieu réel a, construit à partir d'une logique du monde réel. Pour une rue, cela signifie bordures de trottoir, drains, regards, poteaux avec leurs bras et boîtiers, panneaux sur les deux faces, clôtures, poubelles, distributeurs automatiques, supports à vélos, câbles aériens, et cadres de fenêtres avec appuis et unités de climatisation. Regardez des photos du type de lieu réel et listez ce qui s'y trouve avant de construire.
- Usure et histoire. Petites choses qui le rendent habité : autocollants, ruban adhésif, affiches avec du texte généré par code, taches de flaques d'eau, un vélo laissé appuyé, du linge, un chat, une tasse de café. Visez 3 ou plus par surface sur laquelle la caméra s'attarde.
- Audit des vues de coin. Pour chaque vue de coin, rendez l'image large et remplissez ce qui semble nu là-bas. Habituellement, ce sont les côtés des bâtiments, l'arrière des panneaux, les toits (réservoirs, garde-corps, antennes) et le sol entre les objets.
- Lumière et ombre. Assurez-vous que chaque objet projette une ombre lisible, ajoutez des ombres de contact sous les choses garées, et intégrez le côté ombre de chaque forme principale dans l'étape bicolore. Les ombres qui n'apparaissent que dans une vue sont une récompense pour le mouvement de la caméra.
- Vie (optionnel). Marcheurs, un train ou des voitures se déplaçant à vitesse constante, écrans, oiseaux, drapeaux. Tout boucle un nombre entier de fois.
- Polissage. Harmonie de la palette, poids des lignes, ciel, vignette et étalonnage. Ensuite, la vérification des clignotements sur le final.
Règle de densité : depuis le plan téléobjectif plat, le cadre doit sembler plein de bord à bord, comme une illustration dense, sans grande zone de mur ou de sol vide, sauf s'il s'agit d'un repos délibéré pour l'œil.
La barre de qualité
Avant tout rendu complet, vérifiez les images à chaque temps clé (et à mi-chemin entre les mouvements) par rapport à cette liste. Corrigez tout ce qui échoue, puis vérifiez à nouveau.
- Le plan plat se lit comme un dessin plat. Rien ne révèle la profondeur.
- Chaque mouvement se termine par une révélation : une diagonale cachée, une rue latérale, des objets qui étaient éloignés.
- Les vues de coin commencent plates et s'ouvrent, et la première s'éloigne de l'objet héros.
- Les lignes sont nettes et régulières, sans doubles lignes, pas d'encre sur les faces lumineuses et pas de bruit sur les textures lointaines.
- L'ombrage bicolore est clair sur chaque forme principale, et les hachures n'apparaissent que dans les ombres portées.
- Rien ne se lit comme une primitive nue. Chaque boîte a une garniture, des joints, une base ou une étiquette.
- Le texte est lisible et correctement orthographié, dans de vraies polices, avec des marques inventées.
- Le ciel correspond à la direction de la lumière et à la couleur de la scène.
- La palette est limitée et harmonieuse, avec une accentuation claire.
- La jointure de la boucle est invisible (l'image 0 est égale à la dernière image) et rien ne saute.
- La prévisualisation n'a pas de bégaiement. Sur le final, le comptage
blink_mapest faible, et ce qui clignote est un mouvement prévu (pluie, écrans).
Lors de la révision des images, regardez-les comme un illustrateur : nommez les trois zones les plus faibles de chaque image, corrigez-les et répétez. Ne vous arrêtez pas à "ça marche".
Étape 5 : Rendu et vérification
render_fast.mjsrend chaque image sur le GPU comme une moyenne de sous-rendus avec jitter : 4 pour l'anti-aliasing, et jusqu'à 32 sur les mouvements rapides pour un flou de mouvement sans lacune, dimensionnés par la distance de déplacement des pointswindow.probeà l'écran. Utilisez--previewpour la demi-taille à 30 ips.--from,--toet--fpssont également disponibles.render_smooth.mjsdonne la même qualité pour les pages sansrenderMany, mais fait la moyenne dans ffmpeg, donc il est plusieurs fois plus lent.- Fusionnez les maillages statiques par matériau au chargement (
BufferGeometryUtils.mergeGeometries). Une scène est passée de 7 654 appels de dessin à 530, environ 7 fois plus rapide. Gardez tout ce qui bouge hors de la fusion. - Vérifiez le final avec
motion_check.py(rien ne gèle) etblink_map.py, qui compte les pixels qui scintillent dans les images presque fixes. Comparez le comptage avant et après un changement. Les prévisualisations scintillent volontairement, alors jugez le clignotement uniquement sur le final.
Le contrat de la page
Toute page de scène fonctionne avec les rendus si elle fournit :
| Hook | Ce qu'il fait |
|---|---|
#frame | Le canvas qui est capturé. |
window.READY | Défini une fois les actifs et les polices chargés. |
window.render(ms) | Dessine l'image au temps ms. |
window.probe(ms) | Retourne les positions à l'écran de quelques points du monde, utilisées pour dimensionner le flou de mouvement. |
window.setJitter(x, y) | Décalage sub-pixel de l'objectif en pixels, défini via projectionMatrix.elements[8] et [9]. |
window.renderMany([[ms, jx, jy], ...]) | Fait la moyenne de plusieurs rendus en un seul appel (nécessaire pour render_fast). |
?scale=.5 | Rend à demi-taille. |
Tout ce qui est animé doit être une fonction pure de ms, périodique dans la longueur de la boucle. Utilisez des nombres entiers de cycles par boucle pour les pas, les rampements et la pluie.
Clignotements et bugs
| Cause | Correction |
|---|---|
| Tout ce qui est dans l'espace écran (oscillation, hachures, bruit, brume par distance de la caméra) | Ancrez-le dans le monde ou dans la direction de la vue. |
| Surfaces distantes de quelques cm | Fusionnez-les en une seule surface, ou gardez-les à au moins 0,4 m d'écart. |
| Plans coplanaires qui se chevauchent (marquages routiers, pièces de sol) | Peignez les marquages sur un seul plan, et faites en sorte que les pièces se rejoignent bord à bord. |
| Détails sub-pixel (rails, grilles, câbles, pluie) | Anti-aliasing avec jitter sur chaque image et atténuation des lignes denses en mètres par pixel. |
| Un rendu par image | Ne faites jamais cela dans les finals. |
| Bords durs sur les nuages ou les reflets | Adoucissez-les et atténuez les effets aux bords de l'écran. |
| Choses en mouvement qui s'enroulent ou sautent | Enroulez-les pendant le mouvement de caméra le plus rapide de la boucle, trouvé avec window.probe. |
Pièges
- Un commentaire
//ajouté à une ligne de code engloutit le reste de la ligne (unscene.add, une lumière). Utilisez/* */. - Les helpers qui ajoutent à la racine de la scène (comme
plane()) ignorent le groupe actuel et peuvent atterrir au-dessus d'autres choses. Construisez à l'intérieur de groupes. - Gardez les faces émissives (écrans, panneaux) hors des passes de couleur plate et d'ombre, sinon chaque glyphe sera encré et assombri.
ffmpeg -stream_loop ... -c copycrée des boucles que certains lecteurs arrêtent tôt. Ré-encodez à la place.- Pour X, les côte à côte en 32:9 sont trop larges pour être téléchargés. Empilez-les verticalement.
- Utilisez des personnages originaux et des marques inventées uniquement. Copiez la technique d'une référence, jamais son œuvre.