Em uma tentativa de resolver essa questão , decidi descobrir as direções absolutas (espaço do mundo) de cada articulação na posição original (como vetores unitários normalizados) e depois girar as articulações do alvo para corresponder a essa direção. O problema é: como faço para calcular o quaternion de rotação de modo que o osso (o vetor da articulação para o filho) esteja na direção especificada no espaço do mundo.
ATUALIZAÇÃO: Aqui está o meu pensamento atual. Para cada osso, a matriz de transformação será:
(1) absolute transform = local rotation * relative transform to parent in bind pose * parent absolute trasform
Então, digamos que temos 3 articulações: Ombro -> Cotovelo -> Pulso. Já temos uma transformação absoluta para Shoulder, e estamos tentando calcular a rotação para Elbow, de modo que Elbow -> Wrist vá na mesma direção que na animação de origem. A partir de (1), as transformações absolutas serão:
(2) ELBOW absolute = ELBOW local * SHOULDER to ELBOW relative * SHOULDER absolute
(3) WRIST absolute = WRIST local * ELBOW to WRIST relative * ELBOW absolute
Por substituição, obtemos:
(4) WRIST absolute = WRIST local * ELBOW to WRIST relative * ELBOW local *
SHOULDER to ELBOW relative * SHOULDER absolute
Nossos constantes são: ELBOW to WRIST relative
, SHOULDER to ELBOW relative
, e SHOULDER absolute
. WRIST local
consistirá apenas em rotação, sem translação / escala. O problema é encontrar um valor para ELBOW local
tal que ...
(5) normalize(position(ELBOW absolute) - position(WRIST absolute)) = desired direction
(6) ELBOW local consists only of rotations (no translation/scale)
Agora, como sabemos o comprimento do osso e a direção desejada, é possível estimar a parte da posiçãoWRIST absolute
, mas a rotação será desconhecida (e esse problema parece impossível de fazer recursivamente, AFAICT).
Meu pensamento inicial era que, como WRIST local
é somente rotação, e eu o solucionarei na próxima iteração do loop, eu poderia ignorá-lo completamente no cálculo de ELBOW local
. A resposta de Steven Stadnicki abaixo sugeriu que pode não ser tão fácil, uma vez que a orientação do osso é importante para a esfola. No entanto, se eu conseguir fazer isso funcionar em um boneco, posso atravessar a ponte mais tarde.
fonte
M = inv(childRelative) * childAbsolute * inv(parentAbsolute) * inv(selfRelative)
como faria com essas equações matriciais? Quais são as matrizes selfRelative e childRelative? A maneira usual de fazer o encadeamento de transformação é: childTransform = parentToChild * parentTransform . As outras matrizes não são tão óbvias quanto ao seu propósito. O problema é interessante e pode ser incrível descobrir o mistério :).elbow_local
introduz outras 8 .. ou seja, 4 equações com 8 incógnitas. Considere o uso de cadeias cinemáticas. Com menos graus de liberdade. Você já ouviu falar da convenção Denavit-Hartenberg ? Eu usei isso com resultados decentes bonitasRespostas:
A partir da descrição do seu problema, parece que o cerne da questão pode ser que sua representação esteja perdendo dados no momento. Um osso não é simplesmente o vetor de uma articulação para a próxima - é uma matriz de transformação completa que representa a orientação da estrutura local, seja com relação ao espaço do mundo ou com relação à estrutura local do próximo osso 'na cadeia' . Por exemplo, imagine um personagem com uma cicatriz no antebraço; ter o vetor do cotovelo do personagem até o pulso indica onde a mão será posicionada, mas não contém informações suficientes para que você saiba onde estaria a cicatriz - por exemplo, se está apontando para cima ou para baixo. Essas informações de orientação devem fazer parte do seu esqueleto de origem.
Depois de ter as coisas representadas em termos de quadros de orientação local completos e não simplesmente vetores direcionais, o restante das informações deve cair facilmente; exatamente como você sugeriu em seu post, você usaria inversos de matriz para 'descolar' as camadas de suas transformações e chegar à matriz suculenta M no meio. Por exemplo, se você tiver childAbs = childRel * M * selfRel * parentAbs, multiplique por childRel -1 à esquerda de ambos os lados e parentAbs -1 * selfRel -1 à direita de ambos os lados para obter M = childRel - 1 * childAbs * parentAbs -1 * selfRel -1. Observe que a inversão de um produto de matrizes altera a ordem em que seus inversos são multiplicados, de modo que a ordem é importante aqui - (A * B) -1 não é (em geral) igual a A -1 * B -1 , mas sim a B -1 * A -1 .
Depois de obter sua matriz M, deve haver muita informação na Web sobre como convertê-la em um quaternion; se você estiver procurando por ajuda com essa etapa específica, avise-me e eu posso detalhar isso com alguns links, mas parece que existem vários outros problemas que você precisará resolver agora mesmo antes de precisar considere isso.
Edição: Depois de alguns dias para pensar sobre isso, escrevi essa descrição aproximada do processo de passar do espaço mundial para as informações locais; espero que isso ajude a esclarecer um pouco mais.
Vou trabalhar em termos de um esquema dissociado em que cada osso consiste em uma matriz de rotação e um deslocamento; a matriz de rotação é aplicada na raiz do osso e o deslocamento é dado nas coordenadas locais do osso. Isso faz duas coisas importantes:
Essa não é necessariamente a abordagem que eu recomendaria para a implementação, mas é a mais direta para falar sobre como fazê-lo; Embora os resultados precisos não sejam diretamente aplicáveis à sua situação, o IMHO é realmente o melhor: oferece melhores chances de entender como a derivação funciona e como derivar as fórmulas precisas que serão aplicadas à sua própria versão do problema.
Algumas definições rápidas das quantidades com as quais estamos trabalhando: Um sobrescrito de 'w' designa um valor do espaço no mundo, enquanto 'l' indica um valor local. Usaremos P para posição e R para rotação; por exemplo, a P w osso seria a posição alvo espaço de mundo de um osso, (em oposição à sua posição de base, que, naturalmente, é a posição do alvo da sua mãe), enquanto que R l osso seria a orientação local do osso - que é como é girado em relação ao quadro de seu pai.
Agora, considerando isso, podemos derivar a posição de destino e a orientação de qualquer articulação - como alguns dos comentários sugerem, o relacionamento é recursivo. Em particular, temos
R w filho = R w pai * R l filho
e
P w filho = P w pai + R w filho * P l filho
(Eles dizem, respectivamente, 'A orientação mundial da criança é sua orientação local composta pela orientação mundial de seus pais' e 'A posição mundial da criança é a posição mundial de seus pais compensada por seu deslocamento local (conforme traduzido no apropriado coordenadas do worldspace) '.)
Diante disso, podemos agora resolver estas equações para encontrar os parâmetros do osso - ou seja, R l e P l - em termos dos dados WorldSpace que nos são dadas. Isso também dá outra razão pela qual precisamos não apenas da posição, mas também da orientação do espaço mundial de todas as juntas; é um componente essencial para a solução.
Encontrar a orientação local é simples; podemos apenas multiplicar ambos os lados da nossa equação de orientação pelo inverso da orientação do espaço no mundo dos pais, obtendo
R l filho = R w pai -1 * R w filho
O deslocamento local também é relativamente fácil de entender, já que sabemos o deslocamento no espaço mundial e a matriz de transformação para ir do espaço local para o espaço mundial (o que significa que sabemos que a matriz deve seguir o outro caminho):
P l filho = R w filho -1 * (P w filho - P w pai )
Observe uma coisa também: nenhuma dessas equações depende da posição ou orientação local do osso parental, apenas dos valores do espaço mundial para o pai e o filho - portanto, enquanto eles se sentem recursivos (e o cálculo para encontrar os valores do espaço mundial para os ossos) precisa ir da raiz do esqueleto até as folhas devido às dependências dos resultados) eles podem realmente ser feitos em qualquer ordem.
Espero que isso lhe dê uma idéia melhor do que está acontecendo - é fácil ficar atolado em todas as transformações que estão por aí. Avise-me se isso ajudar!
fonte
Se eu entendi direito, você está armazenando todos os vértices dos ossos do esqueleto em uma matriz. Acho que seu problema está mais relacionado à maneira como você está armazenando os Ossos do que aos próprios quaterniões.
Vou tentar explicar o que quero dizer:
Como você basicamente tem o resultado da rotação (um osso rotacionado para usar como modelo) e deseja conhecer o quaternion que geraria esse resultado para outros ossos, por que você não pode armazenar os ossos como vetores, em vez de gerar uma matriz?
Você pode armazenar um osso como um vetor composto de 2 pontos (Cabeça, Cauda). Como você está girando, a cabeça será o ponto de apoio e a cauda girará em torno de um eixo arbitrário. Esse é o trabalho de um quaternário.
Agora, como você tem o Osso A na posição 'descansada' e deseja corresponder à direção do Osso B, precisa saber a quantidade de rotação Alfa e o Eixo. Direita?
Ao obter o ângulo entre os 2 vetores ósseos A e B, você encontrará Alpha, e o resultado normalizado do produto cruzado do AxB obterá o eixo.
A única coisa que está ficando mais complicada para você é que você precisa extrapolar o vetor ósseo da matriz que está usando anteriormente, então talvez você possa armazenar os ossos como vetores (par de pontos) em vez de criar uma matriz.
Além disso, como você está basicamente girando um osso em relação à forma como seu pai foi rotacionado (foi o que obtive do seu código), provavelmente você poderia simplesmente ignorar tudo isso.
Como você deseja corresponder à posição de outro osso, você já sabe que essa posição é legítima; portanto, todo o material de rotação relativa não deve ser necessário.
fonte