Communitygithub.com

Ilusão de Perspectiva

Crie um vídeo 3D ilustrado em loop no estilo japonês de ilusão de perspectiva (ex.: @garments__) de qualquer lugar, como uma rua, cruzamento, estação, corredor de loja, sala, parque ou telhado.

O que é Ilusão de Perspectiva?

Esta skill permite gerar vídeos 3D em loop que exploram ilusões de perspectiva, inspirados no estilo japonês popularizado por artistas como @garments__. A técnica combina uma câmera nivelada com lente teleobjetiva (aproximadamente 2° de campo de visão) para achatar a cena em uma imagem que parece um desenho 2D, seguida por um dolly zoom que revela a profundidade 3D. O loop inclui movimentos de câmera que oscilam entre vistas planas e perspectivas profundas, com objetos que trocam de posição enquanto a vista está plana, criando uma ilusão óptica contínua. A skill suporta templates para diferentes cenários (rua, mercado, cruzamento noturno) e usa renderizadores otimizados (render_fast.mjs, render_smooth.mjs) que aproveitam a GPU para motion blur e anti-aliasing. O fluxo de trabalho envolve construir a cena com objetos repetidos em diferentes profundidades, coreografar a câmera com parâmetros como fov, yaw e lay, e passar por passes de detalhes (formas, estrutura, desgaste, luz e sombra) até atingir um alto padrão de qualidade, verificando com scripts de motion check e blink map.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/wsxiaolin/perspective-illusion/tree/main

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

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).

TemplateCenaRenderizador
street.html, street_plus.htmlRua 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.htmlUm corredor de supermercado, desenhado e realista.render_smooth.mjs (market.html: render.mjs)
scramble_night.htmlUm 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 }

ChaveSignificado
x, yPosição lateral e altura da câmera (m)
zf, WO 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.
HDeslocamento da lente: onde o horizonte fica (-1 inferior, 0 meio, 1 superior).
elOlhe para baixo levantando a câmera e deslocando a lente, nunca inclinando.
yawGire em torno do ponto de foco para vistas de canto.
lay, csLayout do objeto de troca (0 fileira, 1 diagonal) e sua escala na vista plana.

Vocabulário de tomadas que funciona:

TomadaConfigurações
Planafov cerca de 2, H 0. Toda a cena se lê como uma elevação.
Olho de minhocaAltura do joelho, fov 90, horizonte empurrado para baixo (H cerca de -0,7).
AltaCâmera no alto com uma lente média, horizonte acima do quadro.
Vista de cantoyaw ±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 baixaLentes 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 emissiveIntensity para 0 nas janelas.
  • Pinte um skyAt diurno.

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.

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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.mjs renderiza 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 pontos window.probe se movem na tela. Use --preview para metade do tamanho a 30 fps. --from, --to e --fps também estão disponíveis.
  • render_smooth.mjs fornece a mesma qualidade para páginas sem renderMany, 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) e blink_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:

HookO que faz
#frameO canvas que é capturado de tela.
window.READYDefinido 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=.5Renderiza 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

CausaCorreçã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ânciaMescle-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 quadroNunca faça isso em finais.
Bordas duras em nuvens ou reflexosSuavize-as e desbote efeitos nas bordas da tela.
Coisas em movimento que se enrolam ou estalamEnrole-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 (um scene.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 copy faz 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.

Habilidades Relacionadas