O que Ilusão de Perspectiva faz?
O estilo, em um parágrafo
Uma câmera nivelada com uma lente muito longa (cerca de 2° a partir de 400 m de distância) achata uma cena profunda no que parece um desenho plano. Então ela faz um dolly zoom: a lente abre enquanto a câmera se aproxima, de modo que o assunto permanece do mesmo tamanho, mas o espaço atrás dele se estica e se abre. A câmera também gira para vistas de canto. Cada uma começa plana e se abre em 3D. Enquanto a vista está plana, objetos deslizam em profundidade onde ninguém pode ver, então quando a profundidade volta, uma fileira se tornou uma diagonal. Tudo é desenhado como arte de linha a tinta limpa com sombreamento de dois tons planos, sombras projetadas hachuradas e um céu pintado, e o loop se une perfeitamente.
Início rápido
mkdir -p ~/Movies/edits/<data>-<nome>/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 o template mais próximo (veja abaixo), depois edite
node render.mjs scene.html x --stills=0.3,1.5,3,6,8.5 # stills de cada cena, em stills/
node render_fast.mjs scene.html preview.mp4 --preview # loop completo, metade do tamanho, cerca de 1,5 min
node render_fast.mjs scene.html final.mp4 # qualidade total, cerca de 30 min para 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
Corrija coisas nos stills e previews. Renderize em qualidade total apenas depois que o preview for aprovado. Defina $FFMPEG se o ffmpeg não estiver no PATH.
Templates
Comece a partir do template mais próximo da nova cena. Mantenha seu rig de câmera, timeline, pós-passe e hooks de renderização, e substitua apenas a seção da cena (tudo entre // ---------- kit e // ---------- cameras).
| Template | Cena | Renderizador |
|---|---|---|
street.html, street_plus.html | Rua diurna com postes, cones, uma faixa de pedestres e um céu vermelho. street_plus adiciona detalhes e vistas de canto. | render_smooth.mjs |
market.html, market_real.html | Um corredor de supermercado, desenhado e realista. | render_smooth.mjs (market.html: render.mjs) |
scramble_night.html | Um grande cruzamento à noite na chuva, com uma multidão de pedestres, reflexos molhados e telas de anúncio. Ele tem todas as otimizações de velocidade. | render_fast.mjs |
Para uma nova cena, scramble_night.html é a melhor base para o motor, porque é o único com os hooks do renderizador rápido. Remova seus extras noturnos (veja reference.md) para uma aparência diurna.
Passo 1: Construa uma cena que pode enganar o olho
Qualquer lugar funciona se tiver estes elementos:
- Um eixo longo. Disponha a cena ao longo de -z (uma rua, um corredor, uma plataforma, um corredor), com a câmera olhando ao longo dele. A profundidade ao longo deste eixo é o que a teleobjetiva esconde.
- Coisas repetidas em diferentes profundidades. Postes, lâmpadas, prateleiras, colunas, placas, árvores, pessoas. Com uma lente longa, eles se empilham em um plano. Postes alinhados se fundem em um, e pilhas de placas em fachadas distantes se acumulam em uma parede.
- Objetos de troca. 3 a 6 objetos semelhantes (cones, pessoas, lixeiras, cadeiras, caixas) cujo arranjo muda entre uma fileira e uma diagonal. Eles também podem ser desenhados maiores na vista plana (
cs), para que a escala pareça correta quando achatada. - Escala do mundo real em metros. As lentes e planos de foco só fazem sentido em escala real: um cone de 0,75 m, pessoas de 1,7 m, um andar de 3,2 m.
- Detalhe onde as vistas de canto olham. Cada vista de canto revela lados de edifícios, a parte de trás dos objetos e o chão. Preencha-os: janelas laterais, ralos, cabos, meios-fios, a sombra de contato sob um carro. Vistas planas escondem desleixo e vistas de canto o expõem.
- Faça cada detalhe parte de uma superfície (texturas, mangas empilhadas), ou mantenha superfícies separadas a pelo menos 0,4 m de distância. De 400 m de distância, superfícies a alguns centímetros de distância piscam.
Passo 2: Coreografe a câmera
A câmera nunca é inclinada. Ela permanece nivelada para que as verticais permaneçam verticais, como um desenho de arquiteto. Cada tomada é um objeto simples:
{ x, y, zf, W, fov, H, el, yaw, lay, cs }
| Chave | Significado |
|---|---|
x, y | Posição lateral e altura da câmera (m) |
zf, W | O plano de foco e a largura enquadrada ali (m). Manter W enquanto muda fov é o dolly zoom. |
fov | Ângulo horizontal da lente. Cerca de 2° é plano, 25 a 90° é profundo. |
H | Deslocamento da lente: onde o horizonte fica (-1 inferior, 0 meio, 1 superior). |
el | Olhe para baixo levantando a câmera e deslocando a lente, nunca inclinando. |
yaw | Gire em torno do ponto de foco para vistas de canto. |
lay, cs | Layout do objeto de troca (0 fileira, 1 diagonal) e sua escala na vista plana. |
Vocabulário de tomadas que funciona:
| Tomada | Configurações |
|---|---|
| Plana | fov cerca de 2, H 0. Toda a cena se lê como uma elevação. |
| Olho de minhoca | Altura do joelho, fov 90, horizonte empurrado para baixo (H cerca de -0,7). |
| Alta | Câmera no alto com uma lente média, horizonte acima do quadro. |
| Vista de canto | yaw ±18 a 35°, el 9 a 50°. Vem como um par: primeiro uma versão teleobjetiva (fov 4), depois a mesma vista aberta (fov 25 a 45). |
| Diagonal e baixa | Lentes médias que mostram os objetos de troca em profundidade. |
Estrutura do loop. Plano, depois uma vista profunda, depois uma vista de canto (plano depois 3D), de volta ao plano (troque os objetos aqui), uma diagonal alta, a outra vista de canto, uma tomada baixa, depois plano novamente. O loop termina na tomada em que começa.
Tempo. Todos os parâmetros percorrem uma curva cúbica monotônica (Fritsch-Carlson) sobre os tempos-chave, para que a velocidade nunca salte. Interpole W e fov no espaço logarítmico. Adicione pequenas tomadas de "deriva" (W × 0,96) para que as pausas respirem em vez de congelar. 10 s parecem suaves, enquanto 7 s parecem apressados. Para alterar a duração, dimensione os tempos-chave em vez de reescrevê-los.
Trapaceie enquanto plano. Mude lay apenas durante as pausas planas. Se a câmera estiver se movendo em profundidade enquanto os objetos se movem, o truque aparece.
A primeira vista de canto deve ir para o lado que o objeto herói (um carro, uma placa) não cobre. Dessa forma, a revelação mostra algo novo.
Passo 3: A aparência
A página renderiza quatro passes: cor, normais com profundidade, cor plana (sem iluminação) e uma máscara de sombra apenas do sol. Um shader final os combina.
- Linhas a tinta onde a profundidade (segunda diferença), normais ou cor plana mudam. Faça linhas mais pesadas perto. Marcações no chão recebem linhas finas cinza. Desbote linhas de cor onde um pixel cobre muitos metros, ou texturas distantes se transformam em ruído.
- Sombreamento de dois tons. Luz versus sombra, sem gradientes. Defina a luz da máscara para
2*PI, porque o three.js divide Lambert por pi, e sem isso tudo se transforma em sombra. - Hachure apenas sombras projetadas, com hachura ancorada no espaço do mundo (nunca no espaço da tela) e desbotada por
fwidth. - Pinte o céu pela direção da vista, não pela posição da tela, para que as nuvens fiquem paradas enquanto a câmera se move. Mantenha as bordas das nuvens suaves.
- Paleta. 6 a 10 cores planas mais um acento (um céu vermelho, uma placa amarela). Para combinar com uma referência, pegue as cores de seus quadros com k-means.
- Luz. Um sol baixo e duro colocado de modo que os assuntos principais permaneçam iluminados e projetem sombras longas no chão. Um sol do lado alto enterra a cena em uma grande sombra.
Para uma variante noturna (luar, janelas iluminadas, reflexos molhados, brilho, chuva, correção de cor), uma multidão de andarilhos, ou telas e placas de anúncio, veja reference.md.
Para obter uma aparência diurna de scramble_night.html:
- Troque a lua por um sol quente e aumente a luz ambiente.
- Exclua o loop de luz pontual.
- Remova o bloco de chão molhado, a linha de névoa e a correção de cor noturna do shader de pós-processamento.
- Remova a chuva e defina
emissiveIntensitypara 0 nas janelas. - Pinte um
skyAtdiurno.
Passo 4: Passes de detalhes (o que torna ótimo)
As primeiras versões sempre parecem vazias. As versões ótimas vêm de passes deliberados, cada um verificado em stills antes do próximo começar.
- Formas. Massas principais em escala real, o eixo longo, os objetos de troca e a timeline da câmera. Verifique se a tomada plana realmente parece plana e se cada movimento revela algo.
- Estrutura. Tudo o que um lugar real tem, construído a partir da lógica do mundo real. Para uma rua, isso significa meios-fios, ralos, tampas de bueiro, postes com seus braços e caixas, placas em ambas as faces, cercas, lixeiras, máquinas de venda, racks de bicicletas, fios aéreos e molduras de janelas com peitoris e unidades de ar condicionado. Olhe fotos do tipo real de lugar e liste o que está lá antes de construir.
- Desgaste e história. Pequenas coisas que o fazem parecer vivido: adesivos, fita adesiva, pôsteres com texto desenhado em código, manchas de poças, uma bicicleta deixada inclinada, roupa estendida, um gato, uma xícara de café. Mire em 3 ou mais por superfície em que a câmera se demora.
- Auditoria de vistas de canto. Para cada vista de canto, renderize o still aberto e preencha o que parecer vazio ali. Geralmente são lados de edifícios, partes de trás de placas, telhados (tanques, grades, antenas) e o chão entre objetos.
- Luz e sombra. Certifique-se de que cada objeto projete uma sombra legível, adicione sombras de contato sob coisas estacionadas e coloque o lado sombreado de cada forma principal na etapa de dois tons. Sombras que aparecem apenas em uma vista são uma recompensa pelo movimento da câmera.
- Vida (opcional). Andarilhos, um trem ou carros se movendo a velocidade constante, telas, pássaros, bandeiras. Tudo faz um loop um número inteiro de vezes.
- Polimento. Harmonia da paleta, peso da linha, céu, vinheta e correção de cor. Em seguida, a verificação de pisca no final.
Regra de densidade: da tomada teleobjetiva plana, o quadro deve parecer cheio de ponta a ponta, como uma ilustração densa, sem grandes áreas de parede ou chão vazias, a menos que seja uma pausa deliberada para os olhos.
O padrão de qualidade
Antes de qualquer renderização completa, verifique stills em cada tempo-chave (e no meio dos movimentos) contra esta lista. Corrija qualquer coisa que falhar, depois verifique novamente.
- A tomada plana parece um desenho plano. Nada revela a profundidade.
- Cada movimento termina em uma revelação: uma diagonal oculta, uma rua lateral, objetos que estavam distantes.
- As vistas de canto começam planas e se abrem, e a primeira se afasta do objeto herói.
- As linhas são nítidas e uniformes, sem linhas duplas, sem tinta em faces brilhantes e sem ruído em texturas distantes.
- O sombreamento de dois tons é claro em cada forma principal, e a hachura aparece apenas em sombras projetadas.
- Nada parece um primitivo nu. Cada caixa tem guarnição, costuras, uma base ou um rótulo.
- O texto é legível e escrito corretamente, em fontes reais, com marcas inventadas.
- O céu corresponde à direção da luz e à cor da cena.
- A paleta é limitada e harmoniosa, com um acento claro.
- A costura do loop é invisível (quadro 0 é igual ao último quadro) e nada estala.
- O preview não tem gagueira. No final, a contagem de
blink_mapé baixa, e o que pisca é movimento intencional (chuva, telas).
Ao revisar stills, olhe para eles como um ilustrador: nomeie as três áreas mais fracas de cada quadro, corrija-as e repita. Não pare em "funciona".
Passo 5: Renderize e verifique
render_fast.mjsrenderiza cada quadro na GPU como uma média de sub-renderizações com jitter: 4 para anti-aliasing e até 32 em movimentos rápidos para motion blur sem lacunas, dimensionado por quão longe os pontoswindow.probese movem na tela. Use--previewpara metade do tamanho a 30 fps.--from,--toe--fpstambém estão disponíveis.render_smooth.mjsfornece a mesma qualidade para páginas semrenderMany, mas faz a média no ffmpeg, então é várias vezes mais lento.- Mescle malhas estáticas por material no carregamento (
BufferGeometryUtils.mergeGeometries). Uma cena foi de 7.654 chamadas de draw para 530, cerca de 7x mais rápida. Mantenha qualquer coisa que se mova fora da mesclagem. - Verifique o final com
motion_check.py(nada congela) eblink_map.py, que conta pixels que piscam em quadros quase parados. Compare a contagem antes e depois de uma alteração. Previews brilham por design, então julgue o pisca apenas no final.
O contrato da página
Qualquer página de cena funciona com os renderizadores se fornecer:
| Hook | O que faz |
|---|---|
#frame | O canvas que é capturado de tela. |
window.READY | Definido uma vez que ativos e fontes são carregados. |
window.render(ms) | Desenha o quadro no tempo ms. |
window.probe(ms) | Retorna posições na tela de alguns pontos do mundo, usados para dimensionar o motion blur. |
window.setJitter(x, y) | Deslocamento de lente sub-pixel em pixels, definido através de projectionMatrix.elements[8] e [9]. |
window.renderMany([[ms, jx, jy], ...]) | Faz a média de várias renderizações em uma chamada (necessário para render_fast). |
?scale=.5 | Renderiza a metade do tamanho. |
Tudo animado deve ser uma função pura de ms, periódica no comprimento do loop. Use números inteiros de ciclos por loop para passos, rastejos e chuva.
Pisca e falhas
| Causa | Correção |
|---|---|
| Qualquer coisa no espaço da tela (oscilação, hachura, ruído, névoa por distância da câmera) | Ancora no mundo ou na direção da vista. |
| Superfícies a poucos cm de distância | Mescle-as em uma superfície, ou mantenha-as a 0,4 m ou mais de distância. |
| Planos coplanares sobrepostos (marcações de estrada, peças de piso) | Pinte marcações em um plano e faça as peças se encontrarem borda a borda. |
| Detalhes sub-pixel (trilhos, grades, cabos, chuva) | Jitter anti-aliasing em cada quadro e desbote linhas densas por metros por pixel. |
| Uma renderização por quadro | Nunca faça isso em finais. |
| Bordas duras em nuvens ou reflexos | Suavize-as e desbote efeitos nas bordas da tela. |
| Coisas em movimento que se enrolam ou estalam | Enrole-as durante o movimento de câmera mais rápido do loop, encontrado com window.probe. |
Armadilhas
- Um comentário
//adicionado a um código de uma linha engole o resto da linha (umscene.add, uma luz). Use/* */. - Helpers que adicionam à raiz da cena (como
plane()) ignoram o grupo atual e podem aterrissar em cima das coisas. Construa dentro de grupos. - Mantenha faces emissivas (telas, painéis de placa) fora dos passes de cor plana e sombra, ou cada glifo fica com tinta e escurecido.
ffmpeg -stream_loop ... -c copyfaz loops que alguns players param cedo. Re-codifique em vez disso.- Para X, side-by-sides a 32:9 são muito largos para enviar. Empilhe-os verticalmente.
- Use caracteres originais e marcas inventadas apenas. Copie a técnica de uma referência, nunca sua arte.