Um filme de animação feito em 8 kB
(ctrl-alt-test.fr)- A equipe Ctrl-Alt-Test começou em novembro de 2022 um experimento para colocar um demo em tempo real com aparência de curta-metragem animado dentro de 8 kB e, em abril de 2023, lançou The Sheep and the Flower
- O resultado é um único
.exeque gera gráficos e música sem arquivos de recurso nem dependências externas, e as cenas são calculadas em tempo real na GPU com shaders GLSL e raymarching - A ovelha, as placas, a câmera e a linha do tempo não foram feitos com modelos ou texturas armazenados, mas com SDFs, texturas 3D procedurais e parâmetros de tempo codificados manualmente, com cerca de 25 tomadas de câmera ajustadas à mão
- A música foi feita com OpenMPT e 4klang, reduzindo o número de instrumentos de 16 para 13 e aumentando as repetições para melhorar a eficiência da compressão
- Os 8 kB finais só foram possíveis com compressão Crinkler e melhorias no Shader Minifier; o shader de 42 kB coube em cerca de 5 kB após minificação e compressão, deixando espaço para a música e o código em C++
Um único executável dentro de 8 kB
- The Sheep and the Flower é uma animação em tempo real no formato de executável para Windows, lançada em abril de 2023
- Um único arquivo
.exegera tudo, sem arquivos de recurso separados nem dependências externas- Para executar, é necessário Windows e drivers atualizados
- O objetivo era colocar gráficos, animação, direção, trabalho de câmera e música dentro de 8 kB
- O código-fonte está disponível no GitHub: ctrl-alt-test/mouton
Ferramentas usadas para gráficos, música e compressão
- Todos os elementos visuais são calculados em tempo real na GPU
- Os shaders GLSL incluem informações da timeline, configuração de câmera e até a representação da cena
- A renderização é feita com raymarching
- Os shaders foram minificados com Shader Minifier
- A música foi produzida com OpenMPT e o sintetizador 4klang
- Os instrumentos são descritos proceduralmente
- A lista de notas é comprimida
- O código foi escrito em C++ no Visual Studio 2022
- Os flags do compilador e a inicialização partiram do framework Leviathan
- O executável final foi comprimido com Crinkler
Por que 8 kB?
- Na demoscene, categorias com limite de tamanho como 4 kB e 64 kB são comuns
- Trabalhos anteriores costumavam mirar 64 kB, mas 8 kB oferece restrições e regras completamente diferentes
- As técnicas de intro em 4 kB são interessantes, mas foram consideradas limitadas demais para uma narrativa de verdade
- A demoparty Revision adicionou há alguns anos uma competição de 8 kB, o que virou uma boa oportunidade para experimentar
Um mundo desenhado com dois triângulos
- Como no método padronizado na demoscene desde 2008, desenha-se um retângulo cobrindo toda a tela com dois triângulos
- Sobre esse retângulo, o shader GLSL calcula a cor de cada pixel e de cada frame
- A função do shader recebe coordenadas e tempo como entrada e retorna uma cor
- O desafio central era como representar a ovelha e toda a cena dentro dessa função
Montando a cena com SDF
- Signed Distance Field (SDF) é uma função que calcula a distância de um ponto no espaço até o objeto mais próximo
- Na superfície, retorna 0
- Dentro do objeto, retorna um valor negativo
- Formas básicas como esfera ou caixa podem ser descritas com fórmulas simples
- A cena é construída combinando vários campos de distância
- A união de dois objetos pode ser feita com o menor valor de distância
- A interseção pode ser feita com o maior valor
- Uma união suave (smooth union) permite criar formas mais orgânicas
- A ovelha foi criada montando e fundindo formas simples como cones e esferas, enquanto o corpo e a lã foram feitos com funções de ruído 3D
Como o raymarching decide cada pixel
- Raymarching é uma técnica de renderização que usa SDF para avançar um raio aos poucos dentro de uma cena 3D
- Diferente do ray tracing tradicional, ela não calcula matematicamente o ponto de interseção de uma vez
- O SDF informa a distância que pode ser percorrida sem colisão, e o raio é avançado repetidamente por esse valor
- Quando a distância chega a 0 ou perto de 0, considera-se que a interseção foi encontrada
- Quando há interseção, dá para saber se o pixel atual corresponde à ovelha, ao céu ou a outro objeto
- A iluminação exige a normal da superfície, que é estimada calculando a inclinação ao redor
- As sombras são calculadas lançando outro raio do ponto de interseção em direção ao sol para verificar se há objetos no caminho
Como contar uma história com poucos recursos expressivos
- O demo não tem voz nem texto, e há apenas um personagem
- A ovelha só consegue andar, mover a cabeça e os olhos, e nem sequer pode girar
- Para transmitir emoção dentro dessas limitações, foram removidos elementos sem relação com a história
- No começo pensaram em deserto, dunas e céu, mas concluíram que isso não era necessário para a narrativa
- O fundo foi mantido em branco puro
- As texturas foram reduzidas, exceto as placas e os olhos, que transmitem significado
- Mantendo o escopo pequeno, foi possível focar em detalhe, polimento, trabalho de câmera, edição e sincronização com a música
- Cada tomada foi composta manualmente, e a animação foi ajustada repetidas vezes para que o fluxo parecesse natural
Câmera e direção
- A câmera foi usada como ferramenta para contar a história
- Em cenas em que a ovelha vaga por muito tempo, foram usados planos abertos para criar sensação de solidão
- No momento em que ela percebe a placa, usa-se um extreme close-up do olho
- Quando a ovelha olha para a placa, há um zoom lento em direção à cabeça para aumentar o foco
- A empolgação da ovelha é transmitida sobrepondo várias expressões
- Efeito 2D cartunesco
- Passos mais rápidos
- Movimento de cauda enfatizado
- Balanço exagerado da cabeça
- Mudança dramática na música
Forma de desenvolvimento para iteração rápida
- O código-fonte contém muitas constantes hardcoded
- Tamanho dos olhos da ovelha
- Velocidade de movimento da câmera
- Duração de cada tomada
- Valores como a cor da flor eram ajustados repetidamente
- Para feedback rápido, os shaders eram recompilados em runtime, atualizando os gráficos em menos de 1 segundo
- Era necessário um player para ajustar animação e câmera
- Pausar
- Reproduzir
- Controle de tempo
- Verificação imediata após live reload do shader
- Suporte a música também era essencial para sincronizar música e animação com precisão
- Os protótipos iniciais foram feitos no Shadertoy, depois passaram pelo KodeLife e por fim foram levados para um projeto em C++
Música feita com 4klang
- A música era um elemento importante para contar a história, e exigia várias atmosferas e transições em momentos específicos
- Foram usadas as mesmas ferramentas de intros de 4 kB, mas com mais espaço alocado para permitir uma composição mais complexa
- O 4klang synthesizer oferece um plugin utilizável em softwares de música e exporta um arquivo em assembly
- Esse arquivo em assembly é compilado e linkado junto com o demo
- Ao executar o demo, o synth gera proceduralmente o som em forma de onda em uma thread separada e o envia para a placa de som
- A música criada inicialmente ficou maior do que o esperado, então vários ajustes foram feitos para reduzir o tamanho
- Redução do número de instrumentos de 16 para 13
- Recomposição do tema final para combinar com o tempo de toda a música
- Aumento das repetições para favorecer a compressão
- Melhorias na taxa de compressão com mudanças quase imperceptíveis ao ouvido, como a duração das notas de fundo
- A estrutura geral da música foi mantida, minimizando perda de qualidade e economizando espaço
Animação e sincronização temporal
- O demo reavalia tudo a cada frame, sem pré-cálculo nem cache
- Isso é ruim para desempenho, mas favorece animação, porque qualquer elemento pode variar em função do tempo
- O demo inteiro é composto por cerca de 25 tomadas de câmera feitas à mão
- Cada tomada descreve como 18 parâmetros mudam ao longo do tempo
- Posição de cada objeto
- Estado da ovelha
- Posição da câmera
- Foco
- Alvo para o qual a câmera aponta, entre outros
- Por exemplo, uma única linha
camPos = vec3(22., 2., time*0.6-10.);já expressa o movimento linear da câmera em uma tomada timenão significa o tempo absoluto desde o início do demo, mas o tempo desde o início da tomada- Isso reduz o impacto no restante do demo ao inserir, remover ou ajustar tomadas
- Como uma interpolação linear simples pode parecer mecânica, em vários casos foi usado smoothstep
- O código da timeline fica dentro do vertex shader
- A definição das tomadas tem muito código repetido, mas essa repetição acaba ajudando na compressão
Texturas e materiais
- Em renderizadores tradicionais, texturas 2D são aplicadas sobre a superfície de modelos 3D
- No método com raymarching, é difícil calcular coordenadas de textura com facilidade, então em vez disso são calculadas texturas 3D na hora
- Quando o raymarcher encontra uma posição 3D para renderizar, essa coordenada 3D é passada para a função de textura
- As placas de trânsito são desenhadas com matemática para criar contornos triangulares ou retangulares, e o conteúdo interno é formado pela combinação de várias funções
- O símbolo de restaurante é composto por 4 formas ovais pretas e 2 formas brancas que criam os recortes
- Para dar variedade visual, não só as texturas como também os materiais foram configurados de forma diferente
- Por exemplo, os cascos da ovelha receberam reflexos diferentes usando coeficiente de Fresnel
Reflexos falsos para dar vida aos olhos
- Durante boa parte do desenvolvimento, os olhos pareciam sem vida e sem destaque
- Os olhos eram um elemento importante tanto no design do personagem quanto na transmissão da história
- Em imagens de referência de personagens cartunescos, observaram que a íris aparece bastante, mas não é indispensável, e que a pupila tende a ser sempre desenhada grande
- Para criar olhos brilhantes, era necessário incluir reflexos de luz dentro deles
- Com a equação de iluminação existente, os reflexos só apareciam quando o sol e a câmera estavam em posições específicas
- Para contornar isso, modificaram o vetor normal da superfície para aumentar a chance de aparecer reflexo do sol
- Além disso, usaram environment mapping
- Em jogos, isso normalmente serve para simplificar e otimizar reflexos do ambiente real
- Aqui, como o mundo real é quase totalmente branco, a textura foi usada para adicionar detalhes falsos
- O branco do olho e a pupila recebem reflexos muito mais complexos do que o mundo vazio ao redor
- Várias fontes de luz falsas
- Gradientes imitando chão escuro e céu azul
Pós-processamento e estilo visual
- A atmosfera final e a qualidade da imagem foram refinadas no pós-processamento
- Os efeitos usados foram:
- Color grading
- Correção de gama
- Vinheta leve
- Filtro FXAA em 2 passes para reduzir aliasing
- Alguns efeitos, como o brilho em forma de estrela nos olhos e o efeito da tela final, também foram implementados nessa etapa
- Esses efeitos são puramente 2D e não existem no mundo 3D
- Em certo momento também foi testado um estilo inspirado em cartoons antigos
- Detecção de contorno para parecer desenhado à mão
- Renderização em preto e branco
- Grain e ruído
- Após discussão, esse experimento foi abandonado em favor de um visual mais limpo e moderno
Compressão com Crinkler
- A ideia central é não armazenar os dados em si, mas o código que gera esses dados
- A música armazena a lista de notas a tocar e a lista de instruções de cada instrumento
- Crinkler é uma ferramenta de compressão projetada para a demoscene e intros de 1 kB a 8 kB
- Como o executável precisa conseguir se descomprimir sozinho, o Crinkler inclui um pequeno código em assembly que pode descompactar o restante do executável
- O Crinkler prioriza otimização de tamanho
- O algoritmo de compressão leva tempo
- A descompressão é relativamente lenta
- Usa centenas de MB de RAM
- Mas só o Crinkler não bastava, e foi necessária minificação adicional para encaixar 42 kB de código de shader dentro do executável
Melhorias no Shader Minifier
- O código-fonte do shader é incluído no binário final, então precisava ser o menor possível
- Seria possível minificar à mão, mas isso causaria problemas de manutenção e de iteração rápida
- Shader Minifier é uma ferramenta de minificação de shader desenvolvida desde 2011
- Ela remove espaços e comentários, encurta nomes de variáveis e faz outras transformações
- Como um intro de 8 kB contém muito mais código do que um de 4 kB, surgiram novos problemas, e o trabalho no demo chegou a parar por um mês para adicionar os recursos necessários
- Para uma boa minificação, era necessário não só um minifier simples, mas um compilador source-to-source parecido com o Closure Compiler
- As transformações feitas pelo Shader Minifier incluem:
- Renomeação de variáveis e funções
- Inlining de variáveis
- Avaliação de expressões aritméticas constantes
- Inlining de funções
- Eliminação de código morto
- Fusão de declarações
- Reduzir só o tamanho do código em si não era suficiente; era preciso gerar uma saída amigável à compressão
- Algumas transformações reduzem o tamanho do código, mas podem aumentar o tamanho depois da compressão
- Durante o desenvolvimento iterativo do demo, era preciso verificar continuamente o tamanho após compressão
- As novas melhorias no Shader Minifier economizaram cerca de 600 bytes no binário comprimido
- No fim, os 42 kB de shader couberam em cerca de 5 kB após minificação e compressão
- O espaço restante foi usado para a música e o código em C++
Um resultado construído sobre o trabalho de outros
- Este demo foi construído sobre o trabalho de outras pessoas, como técnicas de raymarching, software de geração musical e algoritmos de compressão
- Espera-se que os novos recursos adicionados ao Shader Minifier ajudem a criar demos ainda melhores no futuro
- A categoria de 8 kB oferece mais possibilidades do que a de 4 kB, e fica a expectativa de que ganhe mais popularidade
- Para comparar, o texto original tem cerca de 21.000 caracteres e, se fosse armazenado diretamente, exigiria 21 kB
1 comentários
Comentários do Hacker News
Sou o criador do filme original que inspirou este filme: https://www.youtube.com/watch?v=khWXdkryBE4
O original já ocupa MBs só com Blender, SVG e arquivos de áudio, então é realmente impressionante que o remake caiba em 8kB
Fico feliz em ver que a licença Creative Commons realmente serviu para alguma coisa, embora eu ache que teria sido ainda melhor se tivessem usado o mesmo personagem ouriço e a mesma melodia de áudio
Sloot voltou à vida!!!
https://en.m.wikipedia.org/wiki/Sloot_Digital_Coding_System
Junte um monte de demosceners, mostre o filme e tranque-os por um tempo numa sala com computadores
Funciona ainda melhor com tela grande, sistema de som grande, cerveja e junk food; depois é só pegar o executável resultante
aUm interpretador escrito em Rust para desempenho avalia o comando
ae imprime em STDOUT uma carga específica embutida em tempo de execuçãoUm azar realmente terrível para todos
Elas provavelmente não entenderam a) que é uma função de mão única e b) que, mesmo se não fosse de mão única, várias entradas podem mapear para o mesmo hash
Vídeos da demoscene são bem divertidos, mas absurdamente pouco conhecidos
https://www.youtube.com/playlist?list=PLRQwR4-_0PR9HBI_GZs1nTn7q5LTkGeCN
Seria legal ter um canal que explicasse como eles funcionam por dentro com bons recursos visuais, como retro game mechanics faz
Quando não havia vídeos, às vezes você tinha que esperar bastante pela descompactação e pelo processamento antes de poder se impressionar ou se decepcionar
Eu via muitas demos nos intervalos, e aquilo combinava perfeitamente com meu estado mental na época
https://en.wikipedia.org/wiki/.kkrieger
E indo além, quão pequeno dá para tornar um mundo composto por agentes inteligentes interagindo entre si?
PS: quem vota contra, vocês não sonham? Há um monte de coisas absolutamente insanas e fantásticas nos esperando pela frente
Sei que a tecnologia por trás é bem diferente, mas me lembrou o velho .kkrieger. Comprimir um FPS 3D inteiro em 96k foi realmente incrível
https://en.m.wikipedia.org/wiki/.kkrieger
Se você assistir muitas produções da demoscene e ler como são feitas, começa a entender os limites, e fica claro que elementos como a ovelha, especialmente as seções cônicas das pernas, podem ser descritos por equações bem compactas e animados de forma semelhante
Mas, pelo que eu saiba, a maioria das demos depende de GPU e de forte aceleração 3D, enquanto demos em estilo anime japonês 2D parecem raras ou quase inexistentes em tamanhos pequenos
Será que animação 3D é realmente mais fácil?
Aliás, “mouton” é “ovelha” em francês, daí “mutton”
Já a forma “matemática” de armazenar animação estilizada 2D seria algo como guardar contornos como curvas SVG e usar algoritmos de preenchimento de área para colorir
Fazendo uma conta de guardanapo, um único arco ou linha de uma curva pode consumir tanto espaço quanto a descrição de um volume geométrico inteiro, e para armazenar a animação dessas curvas você precisaria de muito mais espaço do que as pequenas matrizes de transformação de um volume 3D
A complexidade do algoritmo de renderização também provavelmente aumentaria, tanto na complexidade algorítmica real quanto na complexidade espacial
Observação: dá para argumentar que armazenar vários bitmaps e reproduzi-los como um flipbook também é “matemático” se você usar um algoritmo procedural de descompressão que gere quadros completos a partir de diferenças
Mas não acredito que exista algo assim, e, em comparação com a descrição de volumes 3D, as exigências de espaço seriam enormes
Um pouco maior que 8kB, mas, se você gosta de animações sobre ovelhas inesperadamente sombrias, recomendo o filme aberto Cosmos Laundromat, da Blender Foundation
Os filmes dessa fundação sempre tendem a ser estranhamente sombrios para demos técnicas, e achei que este em particular ficou especialmente bom
[0] https://www.youtube.com/watch?v=Y-rmzh0PI3c
Só para avisar de antemão: o filme termina com “to be continued”, e a página da Wikipedia diz o seguinte:
“Este filme foi originalmente concebido como o ponto de partida de um longa-metragem. Havia roteiro e design para uma continuação em curta-metragem, mas ela nunca entrou em produção. Em 2020, o [produtor] anunciou que esta obra única seria tudo o que restaria do projeto”
Se você não quiser passar pela frustração de nunca ver o desfecho de um conceito interessante, talvez seja melhor pular
Foi útil para fazer experimentos de HDR no DaVinci Resolve
[1] https://opencontent.netflix.com/#h.uyzoa2bivz2j
8KB é uma categoria da demoscene. Há um índice bem organizado dessas obras no Pouet
0. https://www.pouet.net/prodlist.php?type%5B%5D=8k&page=1
Razor 1911. Pura nostalgia
Trouxe muitas boas lembranças de décadas atrás :-)
É realmente impressionante, e gosto de como a história é tão engraçada e criativa mesmo com as limitações. Muito marcante
Queria ouvir mais detalhes técnicos sobre que tipo de truques e cortes foram usados para reduzir os bytes
Neste commit (https://github.com/ctrl-alt-test/mouton/commit/79d2d1eab7a222b434b7041d8fc9bce4e60524f8), removeram uma otimização de desempenho para economizar muitos bytes
Originalmente queriam mantê-la, mas faltavam bytes, e descobriram que em GPUs relativamente recentes essa otimização não era necessária
Engraçado e ao mesmo tempo totalmente surpreendente. Finalmente vi um filme cujo final eu não consegui prever