Estou tentando permitir que o usuário do meu aplicativo gire um objeto 3D desenhado no centro da tela, arrastando o dedo na tela. Um movimento horizontal na tela significa rotação em torno de um eixo Y fixo, e um movimento vertical significa rotação em torno do eixo X. O problema que estou tendo é que, se eu permitir a rotação em torno de um eixo, o objeto gira bem, mas assim que introduzo uma segunda rotação, o objeto não gira conforme o esperado.
Aqui está uma imagem do que está acontecendo:
O eixo azul representa meu eixo fixo. Imagine a tela com este eixo azul fixo. É para isso que eu quero que o objeto gire em relação a. O que está acontecendo está em vermelho.
Aqui está o que eu sei:
- A primeira rotação em torno de Y (0, 1, 0) faz com que o modelo se mova do espaço azul (chame este espaço A) para outro espaço (chame este espaço B)
- Tentar girar novamente usando o vetor (1, 0, 0) gira em torno do eixo x no espaço B NÃO no espaço A, o que não é o que pretendo fazer.
Aqui está o que eu tentei, considerando o que eu (acho) eu sei (deixando de fora o W coord por brevidade):
- Primeiro gire em torno de Y (0, 1, 0) usando um Quaternion.
- Converta a rotação Y Quaternion em uma matriz.
- Multiplique a matriz de rotação Y pelo meu eixo fixo x Vetor (1, 0, 0) para obter o eixo X em relação ao novo espaço.
- Gire em torno deste novo vetor X usando um Quaternion.
Aqui está o código:
private float[] rotationMatrix() {
final float[] xAxis = {1f, 0f, 0f, 1f};
final float[] yAxis = {0f, 1f, 0f, 1f};
float[] rotationY = Quaternion.fromAxisAngle(yAxis, -angleX).toMatrix();
// multiply x axis by rotationY to put it in object space
float[] xAxisObjectSpace = new float[4];
multiplyMV(xAxisObjectSpace, 0, rotationY, 0, xAxis, 0);
float[] rotationX = Quaternion.fromAxisAngle(xAxisObjectSpace, -angleY).toMatrix();
float[] rotationMatrix = new float[16];
multiplyMM(rotationMatrix, 0, rotationY, 0, rotationX, 0);
return rotationMatrix;
}
Isso não está funcionando como eu espero. A rotação parece funcionar, mas em algum momento o movimento horizontal não gira em torno do eixo Y, ele parece girar em torno do eixo Z.
Não tenho certeza se meu entendimento está errado ou se alguma outra coisa está causando um problema. Eu tenho algumas outras transformações que estou fazendo no objeto além da rotação. Movo o objeto para o centro antes de aplicar a rotação. Giro-o usando a matriz retornada da minha função acima e depois o traduzo -2 na direção Z para que eu possa ver o objeto. Eu não acho que isso está atrapalhando minhas rotações, mas aqui está o código para isso de qualquer maneira:
private float[] getMvpMatrix() {
// translates the object to where we can see it
final float[] translationMatrix = new float[16];
setIdentityM(translationMatrix, 0);
translateM(translationMatrix, 0, translationMatrix, 0, 0f, 0f, -2);
float[] rotationMatrix = rotationMatrix();
// centers the object
final float[] centeringMatrix = new float[16];
setIdentityM(centeringMatrix, 0);
float moveX = (extents.max[0] + extents.min[0]) / 2f;
float moveY = (extents.max[1] + extents.min[1]) / 2f;
float moveZ = (extents.max[2] + extents.min[2]) / 2f;
translateM(centeringMatrix, 0, //
-moveX, //
-moveY, //
-moveZ //
);
// apply the translations/rotations
final float[] modelMatrix = new float[16];
multiplyMM(modelMatrix, 0, translationMatrix, 0, rotationMatrix, 0);
multiplyMM(modelMatrix, 0, modelMatrix, 0, centeringMatrix, 0);
final float[] mvpMatrix = new float[16];
multiplyMM(mvpMatrix, 0, projectionMatrix, 0, modelMatrix, 0);
return mvpMatrix;
}
Estou preso nisso há alguns dias. A ajuda é muito apreciada.
==================================================== ================
ATUALIZAR:
Fazer isso funcionar no Unity é simples. Aqui está um código que gira um cubo centrado na origem:
public class CubeController : MonoBehaviour {
Vector3 xAxis = new Vector3 (1f, 0f, 0f);
Vector3 yAxis = new Vector3 (0f, 1f, 0f);
// Update is called once per frame
void FixedUpdate () {
float horizontal = Input.GetAxis ("Horizontal");
float vertical = Input.GetAxis ("Vertical");
transform.Rotate (xAxis, vertical, Space.World);
transform.Rotate (yAxis, -horizontal, Space.World);
}
}
A parte que faz as rotações se comportarem como eu espero é o Space.World
parâmetro para a Rotate
função na transformação.
Se eu pudesse usar o Unity, infelizmente, tenho que codificar esse comportamento.
Respostas:
O problema que você tem é chamado de bloqueio flexível . Eu acho que o que você quer fazer é chamado rotação de arcball . A matemática para o arcball pode ser um pouco complicada.
Uma maneira mais simples de fazer isso é encontrar um vetor 2D perpendicular ao furto 2D na tela.
Pegue o vetor e projete-o na câmera perto do avião para obter um vetor 3d no espaço do mundo. Espaço da tela para o espaço do mundo .
Em seguida, crie um quaternion com esse vetor e multiplique-o para o objeto game. Provavelmente com alguma transição slurp ou lerp.
Editar:
Exemplo de unidade: No exemplo de unidade, o estado interno da rotação dos objetos de jogo é um quaternion e não uma matriz. O método transform.rotation gera um quaternion com base no vetor e no ângulo fornecido e multiplica esse quaternion com o quaternion de rotação de objetos do jogo. Ele gera apenas a matriz de rotação para renderização ou física posteriormente. Os quaternions são aditivos e evitam o bloqueio flexível.
Seu código deve ser algo como isto:
Tutorial Opengl de rotação do ArcBall
fonte
Consegui obter as rotações esperadas girando uma matriz de rotação acumulada.
fonte
Sua imagem corresponde ao seu código rotationMatrix. Ao girar o eixo x com a rotação y anterior, você obtém o eixo x local, quando gira o objeto ao redor para obter o resultado exibido na imagem. Para que a rotação seja lógica do ponto de vista do usuário, você deseja girar o objeto usando o eixo de coordenadas do mundo.
Se você deseja que seu usuário possa girar seu objeto várias vezes, faria sentido armazenar sua rotação em um quaternion em vez de em uma matriz, com o tempo várias rotações (e imprecisões de ponto flutuante) farão com que a matriz pareça cada vez menos uma matriz de rotação, o mesmo acontece em um quaternion, é claro, mas apenas normalizar o quaternion traz de volta a uma boa rotação.
Simplesmente use o quaternion de identidade como valor inicial e, toda vez que o usuário passar o dedo na tela, você girará seu quaternion com o
Quaternion.fromAxisAngle(yAxis, -angleX)
código. Sempre usando (1,0,0,1) para rotações x e (0,1,0,1) para rotações y.Como você não mencionou o idioma ou qualquer estrutura específica, os métodos no Quaternion podem ser chamados de algo diferente, é claro, e normalizar não é necessário chamar isso com frequência, mas como a rotação vem de um usuário passando a tela, eles não desacelera muito as coisas e, dessa forma, não há chance do Quaternion escapar de um quaternion da unidade.
fonte
myRotation = rotationX.rotate(rotationY).rotate(myRotation).normalize()
eles não são comutativos, portanto a ordem que você faz conta. Adicione outro comentário com sua estrutura / idioma, se isso não funcionou, e eu vou cavar um pouco mais.