Arquivo mensais:outubro 2014

Animação de esqueletos em 2D

Continuando com a série de posts sobre animação de esqueletos em 2D, vou produzir uma pequena animação com um modelo aprimorado do esqueleto que usei no último artigo.

Foi usado o programa VideoPoint, que normalmente é usado em aplicações científicas. Ele permite que se marque a posição de um objeto ao longo de vários frames de vídeo, e nos dá uma lista de pontos e suas posições no intervalo com que se trabalhou. Você pode fazer um vídeo lateral como este no endereço https://www.youtube.com/watch?v=G8Veye-N0A4 e repetir o mesmo processo.

Foram marcados três pontos. Um na bacia, outro no joelho e outro no calcanhar. Dessa maneira, foi possível determinar os ângulos da coxa e da canela. Fiz um gráfico desse movimento e escolhi um trecho que parecia periódico. Este trecho está no gráfico abaixo. Os ângulos estão em graus. No programa, tive que convertê-los para radianos.

 

Caminhada

 

 

O resultado da animação é este:

Tem um pequeno lag causado pelo software de captura de vídeo. Espero que o leitor possa relevar esta pequena falha.

Foram necessárias poucas alterações no código do programa anterior. Basicamente, adicionei uma rotina que, dado um instante, retorna os ângulos das articulações correspondentes. Também adicionei código para iniciar a animação.

O código mais importante é este:

Na falta de um nome melhor, chamei a rotina de ‘movimento’. Ela está mal escrita, e se você fizer código sério, por favor, não a reproduza; não é um bom exemplo de boa programação. No entanto, é bem simples entender o que está acontecendo. Há uma sequencia de 21 instantes, e quatro ângulos correspondentes, dois para a perna esquerda e dois para a direita. As duas pernas são simétricas, então só precisei fazer um “shift” dos pontos. Usei o módulo Math::Spline, que, depois de inicializado, é capaz de interpolar os pontos e retornar um valor razoável para qualquer instante dentro do intervalo.

O código completo do programa está aqui:

A descrição do esqueleto usada para construir esta animação é este:

Concluindo, este é um exemplo bastante simples de como se faz uma animação usando dados obtidos a partir de movimentos reais. Eu usei interpolação com splines, mas para que a animação fique perfeita, o ideal é estabelecer o framerate que será usado e capturar o movimento com o intervalo entre frames correspondente. Dessa forma não é necessário gastar processamento fazendo cálculos. Também é possível recorrer a bibliotecas de animação, como as listadas na página http://alastaira.wordpress.com/2013/07/24/using-free-mocap-data-for-game-character-animation-in-unity/, mas há dois passos para usá-las: o primeiro é entender o(s) formato(s) de arquivo em que a animação está salva; o segundo é converter o movimento de 3D para 2D, já que em geral são encontrados em três dimensões.

Talvez fosse de interesse descrever o funcionamento da mistura de movimentos, como por exemplo juntar o movimento de levantar a mão com o de pular. Mas isso foge um pouco da finalidade desses textos, que é de introduzir o assunto.

Com esse artigo praticamente se fecha o assunto. Acredito que ele seja didático o suficiente para entender a ideia da animação de esqueletos. A partir de agora, é possível começar a imaginar como tudo funciona em três dimensões.

Desenho de esqueletos

Deve haver muitas maneiras de descrever a estrutura dos ossos de uma personagem para animação. Eu vou descrever algo que desenvolvi que considero simples de explicar e também de implementar.

Um osso é caracterizado por uma origem, e uma rotação. Ele pode estar ligado a outros ossos que se movimentam junto. Então podemos pensar em uma estrutura da seguinte forma:

A posição de um osso inferior é determinada em relação à origem do osso superior na estrutura. Então, ao contrário de um esqueleto orgânico, não é necessário que eles se toquem.

Também vou adicionar dois outros parâmetros, que serão uma lista de vértices de um polígono, além da cor com que devem ser desenhados. Quando se aplicar uma rotação ao osso, é necessário rotacionar todos os vértices do polígono, de maneira a que estes acompanhem o movimento do membro.

Para facilitar as coisas, escrevi uma rotina que pega essas informações de um arquivo e criam a estrutura de dados. Não vou entrar em detalhes de seu funcionamento, e não garanto que não haja problemas em sua implementação. Todavia, se o arquivo de entrada estiver no formato esperado, ela vai funcionar.

A rotina para desenhar o osso é bem simples:

É uma função recursiva, que atua sobre o osso passado como parâmetro e sobre os seus descendentes. Ela pede, como parâmetros, o osso, a posição de origem em que deve ser desenhado, e a rotação que deve ser aplicada.

Ela começa rotacionando a posição do osso em relação à origem passada como parâmetro, repete então o processo em todos os vértices do polígono que será desenhado, e então o desenha usando a função draw_polygon_filled, do SDL, com a cor definida.

Em seguida, cada osso descendente repete esse mesmo procedimento, com a diferença de que a rotação que foi recebida como parâmetro é somada com a rotação inicial do osso, e a origem é a posição do osso atual.

Para fazer a animação, basta definir programaticamente os ângulos de rotação na estrutura de dados.

O programa de exemplo completo está abaixo:

O arquivo esqueleto_exemplo.txt com a estrutura do esqueleto está abaixo:

O funcionamento da função de rotação já foi explicado em um artigo anterior. O programa é razoavelmente inteligível, e talvez não precise de maiores explicações para quem entendeu como funciona o SDL.

Depois de executá-lo, uma janela será mostrada, e basta pressionar as teclas j e k para movimentar a coxa, e i e u para movimentar a canela. A animação é toda determinada pelo usuário.

Nos próximos artigos pretendo mostrar como aplicar movimentos autônomos a essa animação, de maneira que, por exemplo, possamos replicar uma caminhada.

Rotação em 2D

Par chegar mais perto da meta de escrever um sistema de animação de ossos em duas dimensões, vai ser necessário rotacionar partes do corpo ao redor de um ponto.

A rotação em 2D é muito mais simples que em 3D. A redução da complexidade torna fácil explicar o processo, e por isso vou deixar para outra oportunidade as explicações de como fazer o mesmo em um ambiente de três dimensões.

Se você não sabe como funcionam matrizes, provavelmente não chegou ainda ao terceiro ano do ensino médio. Eu recomendo que dê uma olhada nos livros que tratem desse assunto para entender o significado do que escrevo nas próximas linhas.

A matriz de rotação em duas dimensões é

\(Rot(\theta)=\left(\begin{array}{ccc} cos \theta & -sin \theta \\ sin \theta & cos \theta \end{array}\right)\)

 

A função abaixo traduz essa conta para Perl. A função rotaciona um vetor posicionado na origem do plano (0, 0).

Isso é só a implementação da multiplicação de uma matriz de rotação por um vetor. No entanto, é possível generalizá-la, adicionando um outro vetor que indica em relação a que ponto do plano a rotação vai acontecer. Então podemos reescrevê-la passando um parâmetro que indica a posição do vetor de origem:

É simples testar. Aplicando rotações de noventa graus em vetores alinhados aos eixos, os resultados devem ser vetores alinhados aos eixos perpendiculares.

Se a variável $r for igual a [1, 2], a rotina estará correta.

Agora é possível movimentar partes do corpo de uma personagem. A ideia é bem simples. Tudo o que é necessário é determinar a posição de origem e orientação do desenho de cada membro do corpo. É disso que vou tratar no próximo artigo.