3 pontos por GN⁺ 2024-01-25 | 1 comentários | Compartilhar no WhatsApp
  • 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 .exe que 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 .exe gera 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
  • time nã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

 
GN⁺ 2024-01-25
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

    • Talvez o Sloot não funcione, mas o algoritmo de compressão abaixo foi bem eficaz para colocar um filme inteiro em poucos kB
      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
    • Isso não é nada. Apresento o programa de 1 byte que imprime a obra completa de Shakespeare:
      a
      Um interpretador escrito em Rust para desempenho avalia o comando a e imprime em STDOUT uma carga específica embutida em tempo de execução
    • “Em 1999, poucos dias antes de assinar um contrato para vender sua invenção, Sloot morreu subitamente de ataque cardíaco. O código-fonte não foi recuperado, e a tecnologia e as alegações nunca foram reproduzidas nem verificadas”
      Um azar realmente terrível para todos
    • Minha leitura rápida, depois de passar os olhos pelo artigo, é que ele explicou um algoritmo de hash errado para pessoas não técnicas
      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

    • Ver demos como vídeos no YouTube parece um pouco trapaça, mas hoje sou mais cauteloso para baixar e executar executáveis aleatórios do que há 20 anos, então ainda bem que os vídeos existem
      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
    • Parece uma boa chance de compartilhar minha demo favorita de todos os tempos. Intrinsic Gravity, da Still: https://youtu.be/-ZxPhDC-r3w
    • Concordo. Seria incrível poder ver a estrutura interna de algo como .kkrieger
      https://en.wikipedia.org/wiki/.kkrieger
    • Quando resolvermos AGI, quero ver uma demoscene para inteligência. Até quanto isso dá para comprimir?
      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
    • Isso me lembra shader wars
  • 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”

    • A própria forma como você descreveu isso parece mostrar por que 3D é comum nas demos. Formas geométricas 3D podem ser expressas com equações em forma fechada que exigem armazenamento mínimo
      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
    • Como a maior parte da animação 2D no fim das contas descreve acontecimentos em espaço 3D, em geral deve ser muito mais fácil descrever diretamente o evento 3D
  • 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

    • Como observação relacionada e meio aleatória, graças à Netflix o filme inteiro foi disponibilizado para download no S3 como uma sequência de 18.192 imagens EXR sem compressão
      Foi útil para fazer experimentos de HDR no DaVinci Resolve
      [1] https://opencontent.netflix.com/#h.uyzoa2bivz2j
    • A expressão “estranhamente sombrio” parece descrever bem o senso de humor holandês, e provavelmente essa é a principal razão
  • 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

    • Grupo mais conhecido por warez, cracking e pela arte ASCII em arquivos FILE_ID.DIZ e .nfo
      Trouxe muitas boas lembranças de décadas atrás :-)
    • Fiquei feliz em ver que eles ainda estão por aí fazendo arte incrível em algum lugar
  • É 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

  • Engraçado e ao mesmo tempo totalmente surpreendente. Finalmente vi um filme cujo final eu não consegui prever