Usando Quaternions: O que posso fazer com eles? (sem a matemática)

24

Sou desenvolvedor de jogos e não estudei matemática. Então, eu só quero usar o Quaternions como uma ferramenta. E para poder trabalhar com rotação 3D, é necessário usar Quaternions (ou matrizes, mas vamos ficar em Quaternions aqui nesta pergunta). Eu acho que é importante para muitos desenvolvedores usá-los. É por isso que quero compartilhar meu conhecimento e espero preencher os buracos que tenho. Agora....

Então, tanto quanto eu entendi:

Um Quaternion pode descrever duas coisas:

  1. A orientação atual de um objeto 3d.
  2. A transformação de rotação que um Objeto poderia fazer. (rotationChange)

Você pode fazer com um Quaternion:

Multiplicações:

  1. Quaternion endOrientation = Quaternion rotationChange * Quaternion currentOrientation;

    Por exemplo: Meu objeto 3D é girado 90 ° para a esquerda - e minha rotação é uma rotação de 180 ° para a direita e, no final, meu objeto 3D 90 ° é girado para a direita.

  2. Quaternion rotationChange = Quaternion endRotation * Quaternion.Inverse (startRotation);

    Com isso, você obtém um rotationChange, que pode ser aplicado a outra orientação.

  3. Vector3 endPostion = Quaternion rotationChange * Vector3 currentPosition;

    Por exemplo: Meu objeto 3D está na posição (0,0,0) e minha rotação é multiplicada por uma rotação de 180 ° para a direita, minha posição final é algo como (0, -50,0). Dentro desse Quaternion existe um eixo - e uma rotação em torno desse eixo. Você gira seu ponto em torno desse eixo Y graus.

  4. Vector3 rotatedOffsetVector = Quaternion rotationChange * Vector3 currentOffsetVector;

    Por exemplo: Minha direção inicial está aparecendo PARA CIMA - (0,1,0), e minha rotação multiplicada é uma rotação 180 ° para a direita, minha direção final está aparecendo. (0, -1,0)

Mistura (Lerp e Slerp):

  1. Quaternion currentOrientation = Quaternion.Slerp (startOrientation, endOrientation, interpolator)

    se o interpolador for 1: currentOrientation = endOrientation

    se o interpolador for 0: currentOrientation = startOrientation

    Slerp interpola mais preciso, Lerp interpola mais desempenho.

Minhas perguntas):

Tudo o que expliquei até agora está correto?

Isso é "tudo" que você pode fazer com os Quaternions? (obv. não)

O que mais você pode fazer com eles?

Para que servem os produtos Dot e Cross entre 2 Quaternions?

Editar:

Pergunta atualizada com algumas respostas

OC_RaizW
fonte
Digamos que você não tenha 2, mas norientações diferentes (atitudes, poses, etc.). Então você pode calculá-las usando pesos, generalizando efetivamente slerp / lerp. Você também pode converter um quaternion em um rotor, o que equivale a aplicar uma velocidade angular por um certo período de tempo a um corpo rígido. Portanto, você também pode descrever a integração da velocidade angular com os quaternions. Você também pode estimar quão diferentes são as duas orientações (calcule o comprimento do arco medido pelos dois quaterniões na hiperesfera).
Teodron #
E sim, à primeira vista, sua lógica está correta (sua compreensão dos quaternions é muito boa para uma pessoa não técnica). Isso é inapropriado para um comentário, mas parabéns! Nem mesmo os talentosos tecnicamente conhecem todos os usos do quaternion, embora eles os usem apenas como ferramentas de engenharia de software para um propósito.
Teodron #
4
"E para poder trabalhar com a rotação 3D, é necessário usar o Quaternions." Não posso enfatizar o suficiente o quão falsa essa frase é. Você pode usar os ângulos de Euler ou Tait-Bryan para o desenvolvimento do jogo, o único problema é a trava do cardan. Se você quer ser desenvolvedor de jogos, precisará de matemática em um ponto, aprenda-a.
Bálint
11
"desenvolvedor de jogos" e "não estuda matemática" é um paradoxo.
Margaret Bloom
2
Aprecio o que você está tentando fazer com a pergunta, mas as respostas devem estar em uma resposta, não na pergunta. Faça uma resposta "sumária" se achar que vale a pena agrupá-las.
Basic

Respostas:

23

Multiplicação

Pelo menos em termos da implementação de Quaternions pela Unity, a ordem de multiplicação descrita na pergunta não está correta. Isso é importante porque a rotação 3D não é comutativa .

Então, se eu quiser rotacionar um objeto, rotationChangecomeçando pelo seu, currentOrientationeu o escreveria assim:

Quaternion newOrientation = rotationChange * currentOrientation;

(ie. As transformações se acumulam à esquerda - o mesmo que a convenção matricial do Unity. A rotação mais à direita é aplicada primeiro / no final "mais local")

E se eu quisesse transformar uma direção ou deslocar um vetor por uma rotação, escreveria assim:

Vector3 rotatedOffsetVector = rotationChange * currentOffsetVector;

(O Unity gerará um erro de compilação se você fizer o oposto)

Misturando

Na maioria dos casos, você pode se safar das rotações de leitura. Isso ocorre porque o ângulo usado "sob o capô" em um quaternion é metade do ângulo de rotação, tornando-o substancialmente mais próximo da aproximação linear de Lerp do que algo como uma Matrix (que geralmente não funciona bem com Lerp!). Confira cerca de 40 minutos deste vídeo para obter mais explicações .

O único caso em que você realmente precisa do Slerp é quando você precisa de velocidade consistente ao longo do tempo, como a interpolação entre quadros-chave em uma linha do tempo da animação. Nos casos em que você apenas se importa que uma saída seja intermediária entre duas entradas (como mesclar camadas de uma animação), geralmente o Lerp serve muito bem.

O quê mais?

O produto escalar de dois quaternions de unidades fornece o cosseno do ângulo entre eles, para que você possa usar o produto escalar como uma medida de similaridade, se precisar comparar rotações. Porém, isso é um pouco obscuro; portanto, para um código mais legível, eu usaria frequentemente o Quaternion.Angle (a, b) , que expressa mais claramente que estamos comparando ângulos, em unidades familiares (graus).

Esses tipos de métodos de conveniência que o Unity fornece para o Quaternions são super úteis. Em quase todos os projetos, uso este aqui pelo menos algumas vezes :

Quaternion.LookRotation(Vector3 forward, Vector3 up)

Isso cria um quaternion que:

  • gira o eixo z + local para apontar exatamente ao longo do forwardargumento do vetor
  • gira o eixo y + local para apontar o mais próximo possível do upargumento do vetor, se fornecido, ou para (0, 1, 0)se omitido

A razão pela qual o "up" fica "o mais próximo possível" é que o sistema está sobredeterminado. Enfrentar z + para forwardusa até dois graus de liberdade (ou seja, guinada e inclinação), então temos apenas um grau de liberdade restante (rolagem).

Acho que muitas vezes quero algo com as propriedades de exatidão opostas: quero que o local y + aponte exatamente ao longo up, e o local z + fique o mais próximo possível forwardda liberdade restante.

Isso ocorre, por exemplo, ao tentar formar um quadro de coordenadas relativo à câmera para entrada de movimento: eu quero que minha direção local acima permaneça perpendicular ao chão ou à superfície inclinada normal, para que minha entrada não tente encapsular o caractere no terreno ou levite-os para fora dele.

Você também pode conseguir isso se quiser que o compartimento da torre de um tanque enfrente um alvo, sem sair do corpo do tanque ao mirar para cima / para baixo.

Podemos criar nossa própria função de conveniência para fazer isso, usando LookRotationpara o trabalho pesado:

Quaternion TurretLookRotation(Vector3 approximateForward, Vector3 exactUp)
{
    Quaternion rotateZToUp = Quaternion.LookRotation(exactUp, -approximateForward);
    Quaternion rotateYToZ = Quaternion.Euler(90f, 0f, 0f);

    return rotateZToUp * rotateYToZ;
}

Aqui, giramos primeiro o local y + para z + e o local z + para y-.

Em seguida, rotacionamos o novo z + em nossa direção para cima (para que o resultado líquido seja local y + aponta diretamente ao longo exactUp) e o novo y + o mais próximo possível da direção para frente negada (para que o resultado líquido seja local z + pontos o mais próximo possível ao longo approximateForward)

Outro método prático de conveniência é o Quaternion.RotateTowardsque costumo usar da seguinte maneira:

Quaternion newRotation = Quaternion.RotateTowards(
                             oldRotation, 
                             targetRotation,
                             maxDegreesPerSecond * Time.deltaTime
                         );

Isso nos permite chegar targetRotationa uma velocidade consistente e controlável, independentemente da taxa de quadros - importante para rotações que afetam o resultado / imparcialidade da mecânica de jogo (como girar o movimento de um personagem ou rastrear a torre do jogador). Ingenuamente Ler / Slerping nessa situação pode facilmente levar a casos em que o movimento fica mais rápido em altas taxas de quadros, afetando o equilíbrio do jogo. (Isso não quer dizer que esses métodos estejam errados - há maneiras de usá-los corretamente sem alterar a imparcialidade, apenas requer cuidado. RotateTowardsFornece um atalho conveniente que cuida disso para nós)

DMGregory
fonte
Dica: adicione & t = 40m ao final do URL do vídeo para que ele salte direto para lá (opcionalmente, por exemplo, 40m5s). Os produtos de ponto de Quaternion também são úteis quando se lida também com os mundos esféricos de jogos - ou mais amplamente ao orientar pedaços de esferas rotativas.
Luke Briggs
@ Luke Briggs: O ponto esférico do mundo do jogo parece que valeria a pena ser elaborado em sua própria resposta (especialmente com diagramas), se você quiser. :)
DMGregory
Grande ideia - é 03:00 aqui (então eu acho que iria sair um pouco sem sentido!), Mas eu ficaria feliz em puxar algo juntos amanhã (Se eu me lembro!)
Luke Briggs
11
Edit: Eu me empolguei pensando em uma resposta, então o desafio foi aceito! Eu vou pelo menos marcá-lo como um corte brusco que as pessoas possam estar cientes da tarde da noite queimadura que foi para ele: P
Luke Briggs
Aqui vamos nós! Tentei cobri-lo em um sentido gráfico, com base em que sua resposta já cobre muito bem as funções subjacentes. Hora de dormir, eu acho!
Luke Briggs
14

Onde é usado o produto escalar?

No Unity, um dos usuários mais comuns do produto escalar é sempre que você verifica se dois quaternions são iguais via ==ou !=. O Unity calcula o produto escalar para verificar a similaridade em vez de comparar diretamente os valores internos x, y, z, w. Vale a pena manter isso em mente, pois torna a ligação mais cara do que você imagina.

Também o usamos em um caso de uso interessante também ..

Diversão com produtos quaternion dot - Mundos esféricos e orbitais

Simulações de planetas inteiros e até sistemas solares inteiros estão se tornando cada vez mais comuns. Para fazer isso em tempo real, precisamos também do produto quaternion dot. Muitos deles. O produto quaternion dot é muito subutilizado, mas certamente tem seus usos - vamos dar uma olhada!

Em primeiro lugar, temos toda uma série de rotações a considerar:

  1. (Opcionalmente) A estrela ao redor do centro galáctico
  2. O planeta ao redor da estrela
  3. A inclinação do planeta
  4. A rotação do planeta
  5. A posição das células da grade próximas (giradas em torno do núcleo dos planetas) *
  6. Vários planos orbitais

Combine-os todos juntos e você terá muita complexidade (e muitos números enormes!). Quando o espectador está de pé na superfície do planeta, não queremos que ele se apresse a uma velocidade louca pelo espaço no mundo dos jogos. Na verdade, preferimos que eles estejam imóveis e em algum lugar perto da origem - mova o universo ao redor do jogador.

Planeta em rotação

É importante ressaltar que, para que possamos obter a rotação e a inclinação do planeta corretas nesse cenário, precisamos travar o polo no eixo para que ele possa apenas subir / descer na imagem acima (por exemplo, balançar "para cima" enquanto o jogador viaja norte). É aí que entra um produto de ponto de quaternário. Se não usássemos um produto de ponto aqui e, em vez disso, apenas multiplicássemos a inclinação, isso aconteceria:

'Planetas' incorretamente inclinados

Observe como os pólos de nossos 'planetas' orbitais estão sempre se inclinando em direção à estrela. Não é isso que acontece na realidade - a inclinação está em uma direção fixa .

Sem ir muito longe do tópico, aqui está um breve resumo:

  • Em uma esfera, uma orientação também descreve claramente uma posição da superfície.
  • Temos muitas rotações para combinar.
  • Descreva tudo como rotação; a posição do espectador também. Isso ajuda a aumentar o desempenho, pois acabamos fazendo menos operações.
  • O ângulo entre as rotações (nosso produto escalar) ajuda a medir a longitude e funciona particularmente bem ao lidar com inclinações.

Ao obter apenas o ângulo, eliminamos parte dessa rotação indesejada . Ao mesmo tempo, também terminamos com uma medida de longitude útil para a navegação e para o clima local.

* Os planetas são construídos a partir de muitas células da grade . Somente os próximos são realmente exibidos.

Luke Briggs
fonte
2
Isso faz um ótimo trabalho definindo a cena e motivando o problema, mas ainda estou um pouco confuso sobre como a matemática do produto de ponto de quaternião (ou seja, o produto escalar dot(a, b) = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.wem oposição à composição de quaternário que usamos para encadear rotações) nos ajudaria a resolver esse problema. Eu ficaria feliz em votar se você puder elaborar isso um pouco mais tarde (não pretendo mantê-lo longe do seu pijama ... quero dizer, durma!)
DMGregory
@DmGregory, a resposta curta é a inclinação é a saída ímpar; tudo compõe bem, exceto aquele (o planeta pareceria oscilar em torno de sua estrela). Vou (espero!) Adicionar um pouco mais de contexto amanhã!
Luke Briggs
@DMGregory Adicionei algumas informações adicionais (não consegui dormir!) - espero que isso fique mais claro.
Luke Briggs
11
Desculpe se estou sendo um pouco densa, mas, depois de reler várias vezes, ainda não sei como usaria o produto dot em uma fórmula para conseguir a transformação que você está descrevendo. Você seria capaz de adicionar um pequeno pseudocódigo explicando as operações que você executa explicitamente?
DMGregory
@DMGregory Eu não estou familiarizado com os quaternions, mas se isso é rotações em uma esfera, então não são composições de rotação. Isso está usando geometria esférica com vetores para calcular o vetor normal para uma "linha" na superfície de uma esfera AKA qualquer circunferência. Mais uma vez, a resposta faz pouco sentido, nem essa pergunta, mas acredito que eles estejam usando geometria esférica.
The Great Duck