Animação de ossos - matrizes e cálculos

7

Estamos 'no final' quando se trata de terminar o projeto, mas pouco antes de implementar o sistema de animação.

Nosso cliente decidiu escolher "Bones Animation" - que é o de exportar cada Transformation Matrix (rotação matricial 4 x 4 + tradução) para cada quadro e para cada osso que esse objeto animado tiver.

Os objetos do nosso jogo são animados com o 3DS Max Physique Modifier, então teremos dados de ossos / ponderação por vértice. Mas vou simplificar as coisas aqui apenas para esclarecer um pouco esse assunto.

Gostaria de dividir este post em 2 pontos, onde:

  1. Exportando matrizes de ossos para cada quadro

    • trata sobre o método correto de exportar as posições dos ossos para fins de animação posterior, onde eu tenho que 'mover' e 'girar' todo vértice influenciado por esse osso para a posição do osso no quadro X.
  2. Cálculo da posição final do vértice

    • trata sobre operações de matrizes apropriadas para calcular a nova posição do vértice de acordo com a transformação óssea no quadro X.

1. EXPORTAÇÃO DE MATÉRIAS DE OSSOS PARA CADA QUADRO

Entendo corretamente que, ao exportar o objeto animado, devo:

  1. Pegue a matriz de transformação BONE no quadro 0 e inverta esta matriz

  2. Pegue a matriz de transformação BONE na FRAMEx

  3. Multiplique 1 * 2 para obter o deslocamento da transformação do BONE na FRAMEx

[pseudocode]
// Animation export

// For each frame, export bone transformation offset
for(int iFrame = 0; iFrame < vFrames.size(); iFrame++)
{
    // For every bone in the object
    for(int iBone = 0; iBone < vBones.size(); iBone++)
    {
        // Grab transformation matrix for this bone at frame 0 and inverse it
        Matrix3 matBoneMatrixAtStart = pNode->GetObjectTMAfterWSM( 0 );
        matBoneMatrixAtStart.Inverse();

        // Grab transformation matrix for this bone at frame iFrame
        Matrix3 matBoneMatrixAtCurrentFrame = pNode->GetObjectTMAfterWSM( iFrame );

        // Multiply Inversed Transformation Matrix of this bone at frame 0 - with
        //  current frame transformation matrix
        Matrix3 matBoneTransformationOffset = matBoneMatrixAtStart
                                              * matBoneMatrixAtCurrentFrame ;

        // Save matBoneTransformationOffset - vertex will be multiplied by this
        //  matrix for animation purposes
        fwrite(.....)
    }

}
[/pseudocode]

Isso será suficiente? Ou há algo que estou perdendo aqui?

2. CÁLCULO DE NOVAS POSIÇÕES DE VERTICES (POSIÇÃO FINAL DE VERTEX NO QUADRO X)

Posteriormente, ao renderizar, os vértices do objeto serão multiplicados pela matriz de transformação óssea exportada para o quadro de animação real e depois multiplicados por toda essa matriz de transformação do modelo para colocar o objeto na posição correta dentro do nível:

[pseudocode]

Update()
{
    // The model transformation matrix describing the position of
    //  the model in the level
    matModelTransformationMatrix


    // Calculate new vertex position according to it's bone transformation offset
    NewVertexPosition = (OriginalVertexPosition * matBoneTransformationOffset[iFrame])
                           * matModelTransformationMatrix;

    // Increment the frame for testing purposes
    iFrame++;
}

[/pseudocode]

Estou pensando correto aqui? Portanto, tendo a transformação óssea deslocada para o quadro X, multiplicar todos os vértices afetados por esse osso por esse deslocamento deve resultar em um vértice transformado exatamente como esse osso, certo?

PeeS
fonte

Respostas:

8

É melhor ler as páginas da nVidia que contêm alguns artigos sobre GPU Gems. Existe a fórmula-chave que explicarei brevemente na seguinte resposta pseudo:insira a descrição da imagem aqui

É aqui que você encontrará o artigo completo e já é um recurso clássico. Só assumirei que você deseja uma explicação desse processo, feita de maneira simplista (o máximo que puder).

A fórmula e seu significado:

Você começa com uma malha de vértices, ou seja, um conjunto {Pk} de vértices com conectividade. Você não se importa muito com a topologia; portanto, desconsidere a conectividade (isso é algo que não deve deteriorar-se, deve manter os coletores com aparência de coletores, preservar a topologia etc.). Nessa fórmula, o vBindpose fornece a posição de descanso do vértice do personagem. Isso está no espaço do objeto, ou seja, o espaço "mundo" da estrutura do personagem. Agora, cada osso tem sua própria estrutura, e a maneira de transformar um vetor gravado na estrutura do osso [i] na estrutura global do objeto é multiplicando-o com matrixBindpose [i]. (imagine que você precise montar um robô, e seus vértices do antebraço sejam dados em sua estrutura de referência do cotovelo .. e o cotovelo esteja conectado ao úmero / osso do ombro .. é por isso que você precisa dessas transformações, principalmente para montar conjuntos de vértices em uma malha)

Então, o que INV (matrixBindpose [i]) x vBindpose significa então? Isso significa que você obtém as coordenadas do vértice v na estrutura local desse osso [i] osso. Por quê? Como cada osso pode memorizar sua própria versão de onde um vértice afeta é com relação ao seu próprio quadro de coordenadas. Isso significa que cada osso pode fornecer a sua própria visão relativa de onde um ponto afeta, independentemente de como outros ossos veem esse ponto.

Agora, o que acontece quando você se multiplica com a nova matriz de transformação óssea [i] ? Lembre-se do último ponto que agora você tem uma versão local do v vértice, ou seja, como o osso [i] pensa que o vértice se parece com relação à sua própria estrutura. Ao multiplicar com a matriz [i] , você acaba com um vetor / vértice situado no quadro de coordenadas global / objeto.

Em seguida, você multiplica esse vértice pela contribuição escalar / peso do osso. O que você tem então? Você acaba com uma soma ponderada de vetores que estão no mesmo quadro de coordenadas. É por isso que você pode adicioná-los / ou seja, assumir essa soma sobre todos os ossos que influenciam um vértice.

Seu código não reflete realmente que você entendeu completamente o procedimento. O artigo da nVidia se concentra mais em como fazer isso por meio de shaders, é por isso que eles assumem que existem no máximo quatro ossos que podem afetar um vértice da malha inicial / de vinculação.

Como você disse, se você fizer sua pose inicial, o bindpose e a matriz [i] x matrix [i] ^ - 1 é a matriz de identidade. Tudo bem, porque a soma será igual a _sum (w_i) * vBindpose_. Como o vBindpose é o vetor gravado no quadro global / objeto, ele já está montado e em posição. A soma desses pesos, por construção, é 1 (combinação convexa resultante de um processo de normalização de peso - normalmente feito pelo sistema depois que pesos são atribuídos a cada vértice). Portanto, essa fórmula pode ser usada para verificar se um modelo pode ser montado corretamente (normalmente era feito pelo MD5 Doom3carregadores de modelos de animação, se bem me lembro). Resumindo, isso significa que a cada passo você precisa otimizar um pouco os cálculos para calcular o resultado dessa fórmula lil'ol lá. Feliz codificação :)

teodron
fonte
Oh, isso é um monte de coisas que eu tenho que ler algumas vezes agora - eu não suspeito que é tão complicado .. Obrigado por sua ajuda, este deve se me correr ...
Pees
Bem-vindo, na verdade, é um tópico bastante simples, eu fiz um tutorial sobre isso, mas como não estava em inglês, não ajudaria a fornecer o pdf para isso. Pretendo fazer isso em um vídeo, em inglês puro e publicá-lo em um canal do youtube, mas atualmente não tenho tempo. Faça suas perguntas sobre esse tópico aqui no futuro próximo. É bom debater esses tópicos.
Teodron 26/04/12