Pré-renderizar: visuais gerados e filmagens estabilizadas
Cinco rotas, um conjunto de regras comuns. Uma peça desenhada por código torna-se filmagem, colocada como um video: leia-a primeiro. Um SVG estático torna-se um PNG, colocado como uma image: pule para a rota SVG. Uma animação Lottie torna-se filmagem também, desenhada com um player novo por quadro: pule para a rota Lottie. Uma cena 3D torna-se filmagem transparente ou um PNG estático: pule para a rota de cena 3D. A estabilização de vídeo-fonte cria um novo clipe: pule para a rota de estabilização. Tudo executa fora da renderização, então cada pintor decodifica os mesmos pixels.
O Montagent não possui arquivo de shader ou script (ADR-0017). Uma aparência fora da lista de effects é desenhada em seu próprio código, codificada uma vez como filmagem e colocada como um video comum. O formato, o esquema e o pintor permanecem como estão (ADR-0156 §7, ADR-0233).
Quando usar
Use a rota nomeada primeiro. Leia a lista effects em montagent://schema/index.json: quando um membro compõe a aparência (desfoque, sombra, matiz, um blend, uma mask com chave), use-o. Pré-renderize apenas quando nenhum membro, pintura ou blend compõe a aparência: distorções, mosaico, flare de lente, partículas, um padrão procedural ou um modelo/cena 3D externo.
Use a rota SVG quando receber um desenho vetorial (um logotipo, um ícone, uma ilustração) como arquivo SVG, ou quando uma imagem estática for mais fácil de desenhar como SVG. Nenhum elemento aceita um arquivo SVG (ADR-0171). Um SVG que anima (SMIL ou CSS) não é desta rota: ele rasteriza como um único quadro estático e o movimento é perdido. Em vez disso, anime a própria scale, rotation e opacity da imagem colocada.
Use a rota de cena 3D quando receber um ativo 3D (.glb, .gltf, .obj, .fbx, .blend, .c4d) ou uma cena 3D. Nenhum elemento aceita um modelo 3D ou passagem multicanal (ADR-0167, ADR-0233). A renderização headless produz filmagens ProRes 4444 / PNG transparentes fora da renderização, prontas para colocação direta em 2D.
Use a rota Lottie quando receber um arquivo JSON Lottie (uma exportação do After Effects, uma animação do LottieFiles), ou quando um movimento for mais fácil de obter de um. Nenhum elemento aceita um arquivo Lottie (ADR-0171). Isso custa três coisas. Uma resolução fixa: a filmagem é raster no tamanho em que você a renderiza. Uma cadeia de ferramentas de receita: um player Lottie instalado para reproduzir a peça (nunca para renderizar o projeto). Tamanho da filmagem: PNG em um MOV varia de cerca de 6 MB por segundo para arte suave a 141 MB por segundo para arte ruidosa, a 1080p e 25 fps, então renderize no tamanho exibido e não maior. Se o movimento for de algumas formas, animar elementos nativos rect, ellipse e path é menor e permanece nítido.
Uma peça pré-renderizada é filmagem fixa. Seu tamanho, fps e duração são definidos quando você a renderiza. Mudar a aparência significa renderizar novamente, então defina primeiro o tamanho, fps e duração do briefing e corresponda ao fps do projeto.
Regras comuns
Cada rota lê estas. Elas são escritas uma vez.
- Tamanho. Rasterize ou desenhe no maior tamanho que a peça é exibida, após
fit,scalee qualquer zoom com chave, como umawidtheheightliterais de pixel. Nunca derive de umviewBoxdo SVG: umviewBoxdefine as proporções do desenho, não seus pixels, e as ferramentas discordam sobre um arquivo sem ele. Escreva o tamanho na receita. Um zoom posterior além disso reamostra um raster, então execute a receita novamente em um tamanho maior em vez de aumentar. - Texto. Apenas fontes vendidas (vendored) ou texto convertido em contornos. Nunca uma fonte do sistema: uma máquina sem ela desenha algo diferente. Uma fonte que não pode ser encontrada é uma falha de construção, não um span ignorado.
usvg, por exemplo, registra a falta e pula o texto, entãobuildlê os valoresfont-familydo SVG em relação aos arquivos de fonte que você lista emfonts, e falha no próprio aviso de fonte do rasterizador também. - Receita. Cópia da fonte, comandos exatos, versões das ferramentas, tamanho e um hash dos pixels decodificados, obtido através da mesma leitura que o decodificador usa. A reconstrução relata uma correspondência ou a primeira diferença, e nunca compara bytes de arquivo. A receita diz onde ela reside.
Os passos
Execute cada passo, em ordem. A peça está colocada quando o passo 5 passar.
- Desenhe. Escreva código, em qualquer ferramenta, que produza quadros RGBA de uma contagem fixa
width,heighteframes: transparente onde nada é desenhado. Ele escreve RGBA bruto no stdout, ou arquivos PNGf00000.png,f00001.pnge assim por diante na pasta nomeada porPRERENDER_FRAMES_DIR. Leia seu tamanho dePRERENDER_WIDTH,PRERENDER_HEIGHT,PRERENDER_FPSePRERENDER_FRAMES. Use aritmética inteira ou uma semente fixa, para que uma segunda execução desenhe os mesmos bytes. A peça de exemplo é a menor funcional. - Especificação. Escreva uma especificação de construção ao lado do código:
name,out(a pasta de mídia do projeto),width,height,fps,frames,render(o argv que executa seu código),files(todos os arquivos que o código precisa) einputde"png"quando ele escrever PNGs. Executepython3 scripts/prerender.py --helppara cada chave. Adicioneversionspara qualquer biblioteca cuja versão mude os pixels (uma biblioteca de canvas, um navegador headless). - Construa.
python3 scripts/prerender.py build <spec>. Ele executa o código, codifica um<name>.mov, o decodifica e falha no primeiro quadro que difere do que seu código desenhou. Leia sua última linha:N frames, decoded = drawn. - Coloque. Adicione a filmagem ao projeto como um elemento
videosimples (abaixo) ou executepython3 scripts/prerender.py place <project> <placing>, onde<placing>é um arquivo JSON{"source": "media/<name>.mov"}mais qualquer um deid,at,x,y,origin,width,height,fit,track,layer. Ele imprime o projeto com o elemento adicionado, então redirecione isso para um novo arquivo. - Verifique.
validateo projeto e corrija cada erro. Em seguida,montagent probea filmagem e confirme que ela relata alpha (carries: true). Só entãoframeum tile onde a peça está e verifique se as partes transparentes mostram o que está atrás.
{
"frame": {"width": 640, "height": 360}, "fps": 25, "background": "#101418", "duration": 480, "output": "out/pulse.mp4",
"tracks": [
{"name": "prerender", "layer": 10, "elements": [
{"id": "pulse", "type": "video", "start": 0, "end": 480, "source": "media/pulse.mov", "source_start": 0, "source_end": 480, "volume": 0, "x": 320, "y": 180, "origin": "center", "width": 320, "height": 240, "fit": "contain"}
]}
]
}
O elemento é um video comum: anime opacity, scale e position, adicione effects e um blend, e coloque-o em uma faixa como qualquer clipe. A filmagem não tem som, então volume é 0. Seu end menos start é a duração da filmagem; source_end é a mesma duração.
A rota SVG
Entrada: um arquivo SVG e o tamanho em pixels das regras comuns.
- Venda as fontes que o texto do SVG usa, com
montagent fonts vendor, e defina cada<text>nessa família com um atributofont-family. Ou converta o texto em contornos em sua ferramenta de desenho e não use nenhuma fonte. - Especificação. Escreva uma especificação de construção ao lado do SVG com
"input": "svg":name,out,svg, awidtheheightliterais,render(o argv do rasterizador, com{width},{height}e{out}onde eles vão),files,fontseversions. Não há fps nem contagem de quadros. - Rasterize.
python3 scripts/prerender.py build <spec>executa o rasterizador uma vez, fora da renderização. Qualquer ferramenta que escreva um PNG RGBA serve: a linha de comandoresvg, um navegador headless, Inkscape. Pule fontes do sistema e entregue as vendidas, como na especificação abaixo. Ele escreve<name>.pnge sua receita, e falha em uma fonte ausente. - Coloque o PNG como um elemento
imagesimples (abaixo) ou executepython3 scripts/prerender.py place <project> <placing>com{"source": "media/<name>.png", "duration": <ms>}mais qualquer um deid,at,x,y,origin,width,height,fit,track,layer. - Verifique. Execute
validatee corrija cada erro, entãoframeum tile onde a imagem está e verifique se as partes transparentes mostram o que está atrás.
A especificação de exemplo, para o SVG de exemplo (formas, um gradiente e texto definido em uma fonte vendida), rasterizado em três vezes seu viewBox:
{
"name": "mark", "out": "media", "input": "svg", "svg": "sample_art.svg", "width": 480, "height": 270,
"render": ["resvg", "--skip-system-fonts", "--use-font-file", "Inter-Bold.ttf", "-w", "{width}", "-h", "{height}", "sample_art.svg", "{out}"],
"files": ["sample_art.svg"], "fonts": ["Inter-Bold.ttf"],
"versions": {"resvg": ["resvg", "--version"]}
}
O PNG tem dimensões de pixel intrínsecas, então ele entra como uma image comum, e as regras fit de ADR-0015 se aplicam como para qualquer PNG: contain com a própria proporção da caixa, ou literal. Colocá-lo em seu próprio tamanho, com fit de contain, é um desenho 1:1. Mostrá-lo maior do que o tamanho que você rasterizou e o raster é reamostrado: execute a receita novamente no tamanho maior.
{
"frame": {"width": 640, "height": 360}, "fps": 25, "background": "#101418", "duration": 2000, "output": "out/mark.mp4",
"tracks": [
{"name": "art", "layer": 10, "elements": [
{"id": "mark", "type": "image", "start": 0, "end": 2000, "source": "media/mark.png", "x": 320, "y": 180, "origin": "center", "width": 480, "height": 270, "fit": "contain"}
]}
]
}
Execute validate. A imagem é uma comum: anime opacity, scale e position, adicione effects, coloque-a em uma faixa.
A rota Lottie
Entrada: um arquivo JSON Lottie, um tamanho, um fps e um intervalo de quadros. Os próprios w, h e fr do Lottie são entradas para escolher, não regras: o tamanho segue as regras comuns (o maior tamanho mostrado, como literais) e você escreve o fps e a contagem de quadros na especificação.
- Escolha um player que possa escrever quadros RGBA e registre seu nome e versão em
versions. A skill não escolhe um: a pré-renderização executa uma vez fora da renderização e os pintores nunca veem o player. O player de exemplo é ThorVG através depip install thorvg-python==1.1.3. - Especificação. Escreva uma especificação de construção ao lado do Lottie com
"input": "lottie":name,out,lottie, awidtheheightliterais,fps,frames,render,files,fontseversions.renderé o argv do player, com{width},{height},{frame}e{out}onde eles vão: ele desenha um quadro como RGBA bruto em{out}.startestepopcionais definem qual quadro do Lottie cada quadro da filmagem é (quadroiéstart + i * step;steppadrão é ofrdo Lottie dividido porfps). Executepython3 scripts/prerender.py --helppara cada chave. - Construa.
python3 scripts/prerender.py build <spec>primeiro lê o Lottie e o recusa, nomeando cada caso, se ele usar uma expressão, um efeito de camada que o player não está registrado como desenhando (liste aqueles que estão emallow_effects), ou texto cuja fonte não tem nem caminhos de glifo incorporados nem um arquivo vendido emfonts. Um player descarta todos estes sem som, então uma construção que continuasse enviaria uma animação diferente. Em seguida, executarenderuma vez por quadro, um novo processo cada vez, lê o próprio relatório do player sobre o que ele pulou (qualquer linha dizendo skipped, ignored ou unsupported é uma falha de construção: trate-a como tal), codifica um<name>.movcomo o codec diz, e falha no primeiro quadro que difere do que o player desenhou. - Coloque a filmagem como um elemento
videosimples, ou complacecomo em os passos. Executevalidateemontagent probeo MOV: confirme que ele relata alpha (carries: true). Em seguida,frameum tile onde a peça está e verifique se as partes transparentes mostram o que está atrás.
Por que um player novo por quadro. O harness nunca avança um player. ThorVG ignora uma mudança de quadro abaixo de 0.001, então onde um seek termina depende do seek anterior, e o seek do Skottie modifica seu objeto de animação. Um quadro desenhado após outros pode diferir do mesmo quadro desenhado primeiro, e a reconstrução não corresponderia.
O codec
Um MOV com codec ProRes 4444, que carrega um canal alfa sem perdas e é decodificado quadro a quadro sem dependência de quadro futuro. Nenhum outro codec e nenhum contêiner diferente de .mov. Especifique em scripts/prerender.py como --codec prores_4444. Os pintores decodificam um quadro como RGBA, então o hash da reconstrução é tomado sobre esses bytes decodificados, não sobre os bytes do arquivo ou do codec.
A receita
Uma pasta _recipes/<name>/ ao lado da especificação, com estes arquivos:
spec.json— a especificação completa, como foi passada parabuild.decoded_hash.txt— o hash SHA-256 dos pixels decodificados do primeiro quadro, codificado em hexadecimal.decoded_frames_hash.txt— o hash SHA-256 de todos os pixels decodificados, concatenados.- (Apenas SVG)
raster_command.txt— o comando de rasterização exato que foi executado. - (Apenas Lottie)
player_log.txt— o log completo do player da última execução debuild.
A receita está em .gitignore por padrão. Inclua _recipes/ no controle de versão se você precisar reconstruir ou auditar; omita-o para construções descartáveis.
Execute python3 scripts/prerender.py rebuild <name> para verificar a reconstrução: ele relata "RECONSTRUÇÃO: CORRESPONDE" ou "RECONSTRUÇÃO: DIFERE no quadro N (hash X, esperado Y)". Ele não executa o renderizador: apenas verifica o hash.
A rota de cena 3D
Entrada: um arquivo de cena 3D (.glb, .gltf, .obj, .fbx, .blend, .c4d) e o tamanho em pixels das regras comuns. A cena é renderizada headless (sem janela) para produzir filmagens transparentes ou um PNG estático.
- Escolha um renderizador que possa produzir quadros RGBA de um formato 3D suportado, com fundo transparente. Registre seu nome e versão em
versions. Renderizadores possíveis incluem Blender (viablender -b), Three.js (via Puppeteer/Playwright) ou ferramentas CLI comogltf-transform. O exemplo de referência usa Blender em modo background com saída PNG. - Especificação. Escreva uma especificação de construção ao lado do ativo 3D com
"input": "3d":name,out,scene(caminho para o arquivo da cena), awidtheheightliterais,fps,frames,render(o argv do renderizador, com{width},{height},{scene},{frame}e{out}),files,fontseversions. Para cenas estáticas, definaframescomo 1 e omitafps; a saída será um PNG. Para cenas animadas, produza um MOV comframes> 1.renderé chamado uma vez por quadro, cada vez em um novo processo, escrevendo RGBA bruto ou PNG em{out}. - Construa.
python3 scripts/prerender.py build <spec>executa o renderizador para cada quadro, codifica um<name>.mov(ou<name>.pngpara imagens estáticas) com ProRes 4444, e verifica a saída. Para cenas animadas, ele falha se algum quadro diferir da saída do renderizador. A cena é renderizada em 32 bits por canal, com alfa, e o codec preserva isso. - Coloque o MOV ou PNG como um elemento
videoouimagesimples, ou complacecomo em os passos. Executevalidateemontagent probepara confirmar alpha, depoisframepara verificar a transparência.
Por que renderizar quadro a quadro. Um único comando que produz um filme completo pode usar um codec ou configuração que o harness não pode verificar quadro a quadro. Renderizar cada quadro separadamente permite reconstrução e verificação determinísticas, e garante que cada quadro seja independente (sem compensação de movimento temporal).
A rota de estabilização
Entrada: um clipe de vídeo-fonte (qualquer formato que o Montagent aceite como fonte) e os parâmetros de estabilização. A saída é um novo clipe com movimento suavizado e, se solicitado, travamento de quadro de referência, ping-pong ou câmera lenta.
- Escolha uma ferramenta de estabilização que possa analisar e estabilizar o movimento da câmera, e registre seu nome e versão em
versions. Ferramentas possíveis incluem FFmpeg comvid.stab,melt(MLT), ou scripts Python com OpenCV. O exemplo de referência usa FFmpegvid.stabpara detecção e estabilização de transformação. - Especificação. Escreva uma especificação de construção ao lado da fonte com
"input": "stabilize":name,out,source(caminho para o clipe de origem), awidtheheightliterais (opcionais; se omitido, a largura e altura da fonte são usadas),fps(opcional; se omitido, o fps da fonte é usado),frames(opcional; se omitido, a duração da fonte é usada),render(o argv da ferramenta de estabilização, com{source},{width},{height},{fps},{frames},{out}e{tune}onde eles vão),files, eversions.tuneé opcional e pode incluirlock(travamento de quadro de referência),pingpong(reprodução para frente e para trás) eslowdown(câmera lenta; o fator é especificado separadamente, por exemplo,"slowdown": 2para metade da velocidade). - Construa.
python3 scripts/prerender.py build <spec>executa a ferramenta de estabilização, que produz um único arquivo de filme (<name>.mov) com o codec ProRes 4444. Setuneincluirpingpong, a saída será estendida para a duração do ping-pong (por exemplo, o dobro da duração para uma repetição). Seslowdownfor definido, a saída será mais longa e a taxa de quadros será mantida; o movimento adapta-se à câmera lenta. O build falha se a ferramenta relatar um erro. - Coloque o MOV como um elemento
videosimples, ou complacecomo em os passos. O elemento deve ter a mesma duração que a filmagem estabilizada. Setuneincluirlock, o primeiro quadro da filmagem estabilizada é um quadro de referência fixo, e a estabilização mantém esse quadro estacionário (útil para composições com texto ou gráficos sobrepostos). - Verifique. Execute
validatee corrija cada erro. Em seguida,montagent probea filmagem para confirmar que ela tem a duração esperada.frameum tile em vários pontos para verificar se a estabilização está correta e se o travamento, ping-pong e câmera lenta funcionam como esperado.
Ajustes avançados. A especificação pode incluir um campo "tune": {} com parâmetros específicos da ferramenta de estabilização. Por exemplo, para FFmpeg vid.stab, tune pode incluir "shakiness": 5, "accuracy": 15, "stepsize": 6, "mincontrast": 0.3, "tripod": true (equivalente a travamento de quadro de referência). Esses valores são passados para a ferramenta conforme documentado em sua especificação.
Ping-pong e câmera lenta. Quando tune inclui "pingpong": true, a filmagem estabilizada é reproduzida para frente até o fim, depois para trás até o início, repetindo uma vez. Quando tune inclui "slowdown": N (onde N é um inteiro positivo), a filmagem é desacelerada por um fator de N: para cada quadro de entrada, N quadros de saída são gerados (interpolados ou duplicados, dependendo da ferramenta). Ping-pong e câmera lenta podem ser combinados; a duração da saída é frames * (pingpong ? 2 : 1) * slowdown.
{
"name": "stable_clip",
"out": "media",
"input": "stabilize",
"source": "source_video.mp4",
"width": 1920,
"height": 1080,
"fps": 25,
"render": ["ffmpeg", "-i", "{source}", "-vf", "vidstabdetect=shakiness=5:accuracy=15,vidstabtransform=tripod=true:zoom=0", "-an", "-y", "{out}"],
"files": ["source_video.mp4"],
"versions": {"ffmpeg": ["ffmpeg", "-version"]},
"tune": {"lock": true, "pingpong": true, "slowdown": 2}
}
Neste exemplo, a fonte de 10 segundos a 25 fps (250 quadros) produz uma saída de 20 segundos (500 quadros) a 25 fps: 250 quadros para frente, depois 250 quadros para trás (ping-pong), em câmera lenta (slowdown 2 significa 500 quadros no total, mas a câmera lenta é aplicada ao movimento; a saída real é de 500 quadros à velocidade de quadro original).
A rota de peça desenhada por código
Entrada: código que desenha quadros RGBA. O código deve ser executável e produzir saída reproduzível.
- Escreva o código. Crie um script (Python, Node.js, etc.) que produz quadros RGBA de uma contagem fixa
width,heighteframes. Use as variáveis de ambientePRERENDER_WIDTH,PRERENDER_HEIGHT,PRERENDER_FPS,PRERENDER_FRAMESePRERENDER_FRAMES_DIRpara configurar a saída. Escreva PNGs emPRERENDER_FRAMES_DIRnomeados comof00000.png,f00001.png, etc., ou escreva RGBA bruto no stdout. Use aritmética inteira ou uma semente fixa para garantir a reproduzibilidade. - Especificação. Escreva uma especificação de construção ao lado do código:
name,out,inputcomo"code",width,height,fps,frames,render(o argv que executa seu código),files(todos os arquivos que o código precisa) eversions(para bibliotecas que afetam a saída). Executepython3 scripts/prerender.py --helppara detalhes. - Construa.
python3 scripts/prerender.py build <spec>. Ele executa seu código, codifica um<name>.movcom ProRes 4444, decodifica e verifica quadro a quadro contra a saída do código. Falha no primeiro quadro que difere. - Coloque. Adicione o MOV como um elemento
videosimples, ou useplace. Executevalidateemontagent probepara confirmar alpha. - Verifique.
validatee corrija erros.montagent probepara confirmar alpha.frameum tile para verificar a transparência.
Resumo das rotas
| Rota | Entrada | Saída | Uso principal |
|---|---|---|---|
| Peça desenhada por código | Código executável | MOV (ProRes 4444) | Visuais generativos, efeitos personalizados |
| SVG | Arquivo SVG | PNG | Logotipos, ícones, ilustrações estáticas |
| Lottie | Arquivo JSON Lottie | MOV (ProRes 4444) | Animação vetorial do After Effects |
| Cena 3D | Arquivo de cena 3D | MOV ou PNG | Modelos 3D, cenas com transparência |
| Estabilização | Clipe de vídeo | MOV (ProRes 4444) | Estabilização de movimento, travamento de referência, ping-pong, câmera lenta |
Solução de problemas
buildfalha com "quadro N difere": A saída do seu código é não determinística. Verifique sementes aleatórias, tempo de sistema, ou estado global. Use aritmética inteira e sementes fixas.buildfalha com "fonte não encontrada": Certifique-se de que as fontes estão vendidas e listadas emfontsna especificação. Usemontagent fonts vendor.buildfalha com "player pulou efeito": O player Lottie não suporta o efeito. Useallow_effectspara listar efeitos suportados, ou remova o efeito do Lottie.probenão mostra alpha: O codec ou a saída não tem canal alfa. Use ProRes 4444 e certifique-se de que seu código desenha transparência onde necessário.placeadiciona o elemento na posição errada: Verifique as coordenadasx,y,origine as chavesat(tempo). Usemontagentpara inspecionar o projeto.
Referências
- ADR-0017: Esquema fechado, sem escotilha de escape
- ADR-0156: Quatro efeitos nomeados se juntam à lista de efeitos
- ADR-0171: SVG e Lottie não entram como fontes
- ADR-0167: Um elemento pode ser projetado, nunca colocado
- ADR-0233: Modelos 3D e passagens multicanal são recusados como fontes nativas
- Peça de código de exemplo
- SVG de exemplo
- Player Lottie de exemplo
- Renderizador de cena 3D de exemplo
- Ferramenta de estabilização de exemplo