Como posso orbitar uma câmera sobre seu ponto alvo?


18

Estou desenhando uma cena em que a câmera se move livremente sobre o universo. A classe câmera mantém o controle do ponto de vista (ou olhada ponto), a posição da câmera, eo vetor para cima. Esses vetores / pontos são passados ​​para o gluLookAt.

A panorâmica e o zoom são quase triviais de implementar. No entanto, eu estou achando rotação sobre o olhar ponto a ser muito mais de um problema. Eu quero escrever uma função Camera.rotate que tem 2 ângulos, um que gira para cima / baixo e outro que gira para a esquerda / direita ao longo de uma esfera imaginária que é centralizada na aparência do ponto.

Existe uma maneira fácil de fazer isso?

Eu (brevemente) li sobre quaternions, mas queria ver se havia uma solução mais fácil, dada a construção relativamente simples da minha cena.


Você tem algo em sua caixa de ferramentas para girar um ponto em torno da origem, dados um eixo e um ângulo?
Sam Hocevar

Nada além da minha compreensão frouxa de Quaternions, não. Quanto mais eu estou olhando para isso, acho que Quaternions pode ser a resposta. No entanto, não tenho certeza de como calcular os valores dos eixos x, ye z para usar nas fórmulas do Quaternion.
Lucas

Não vou lhe dizer que os quaternions não são o caminho a seguir. Eles são uma solução muito boa para o seu problema. Mas você precisará de uma aula de quaternário e maneiras de interagir com a GL e a GLU, e acho que você deve primeiro tentar se familiarizar com as matrizes de transformação. Outros podem discordar.
sam hocevar

considerar que ordem você faz as rotações na aplicação rotações de transição para espaço do mundo ou de transição para difere espaço da câmera.
Charlie

Respostas:


25

O que você está pedindo é chamado rotação de Arcball. Quaternions são a solução fácil somente se você entender como eles funcionam. Você pode conseguir o mesmo sem quaternions.

Pré-requisitos

Você sabe como girar objetos em geral? Digamos que você tenha um objeto na origem. Você sabe como você o gira (dica: multiplique por alguma matriz de rotação)? Se sim, suponho que você saiba o que acontecerá se você traduzir o objeto primeiro e depois girá-lo?

Você deve saber como calcular uma matriz de rotação a partir do eixo angular (fácil como torta, veja a infinidade de equações on-line, muitas delas fornecem o código também)

Solução

  • Obter da câmera para cima e certos vetores. Observe que eles devem ser normalizados.
  • Obtenha o vetor do ponto de foco para a câmera (camPosition - Focus). Este é o vetor que você vai girar. Vamos chamar isso de camFocusVector .
  • Decida quanto você deseja girar em guinada / inclinação em relação à câmera
  • Crie duas matrizes de rotação. A primeira matriz de rotação utilizará a parte superior da câmera como o eixo e o ângulo de guinada que você decidiu. A segunda matriz de rotação usará o lado direito da câmera como o eixo e o ângulo de inclinação que você decidiu.
  • Agora gire o camFocusVector com as novas matrizes de rotação. Esta é agora a sua nova posição da câmera em relação à origem. É claro que queremos que seja relativo ao ponto de foco ...
  • Adicione a posição do ponto de foco ao camFocusVector . Esta é agora a nova posição da sua câmera. Traduza sua câmera adequadamente.
  • Por fim, peça à câmera que foque no ponto de foco chamando sua função lookAt ()

Ressalvas

Você terá que procurar certos casos ou singularidades nas quais sua câmera irá parar de funcionar. Olhando para baixo / para cima, por exemplo. Vou deixar você descobrir como lidar com isso.

EDIT1: Como recalcular os vetores ortonormais da câmera

Você já sabe a direção da câmera ((cameraPos - focusPoint) .normalize ()). Agora suponha que a câmera esteja acima de + Y (ou seja, o eixo superior atual do seu mundo é ... depende de você). Agora simplesmente cruze a direção com a seta para cima para acertar . Feito? Não! Seu vetor ativo não é mais ortogonal aos outros dois. Para corrigir isso, cruz direita com direção e você obter o seu novo -se .

Observe que o Gram-Schmidt é realmente o que deve ser usado para ortonormalizar vetores.

Novamente, tome nota das advertências, pois isso não funcionará em alguns casos (a direção é paralela à alta, por exemplo).


Observe que o vetor direito pode ser obtido usando normalize(up ^ camFocusVector)(ou seu oposto se for canhoto).
Sam Hocevar

Até agora, com uma boa aparência, funcionou muito bem para a rotação esquerda / direita (eu tenho o vetor up, então é fácil). O que significa '^' no seu comentário @SamHocevar? Isso é produto cruzado? Além disso, como posso recalcular o vetor up depois de fazer as traduções?
Lucas

@Luke: Verifique meu EDIT1
Samaursa

11
@Samaursa Muito obrigado. Sua solução funciona perfeitamente e eu aprendi muito no processo!
Lucas

2
Esta é uma excelente resposta, muito obrigado pela sua explicação. No meu caso, eu queria que a câmera girasse apenas sobre o ponto de destino no plano XY e, depois de fazer todos os cálculos, sempre defino cameraRight.z como 0 e calculei o vetor cameraUp. Isso deu o efeito desejado. Apenas pensei em compartilhar #
31712

1

O que você precisa é de uma câmera ArcBall típica, que é basicamente uma câmera que mantém um local de destino e permite que você mova a câmera de uma maneira "esférica" ​​em torno desse alvo.

Está no XNA, mas no IIRC já usei essa implementação antes e funcionou muito bem:

http://roy-t.nl/index.php/2010/02/21/xna-simple-arcballcamera/


Essa implementação parece se mover apenas diretamente ao longo do vetor certo, o que aproximará uma bola de arco para pequenos valores de quantidade . Ele também está movendo o ponto LookAt, como para onde eu quero mover a câmera (perto, mas sutilmente diferente). Acredito que Samaursa forneceu a maneira mais simples e completa de conseguir isso.
Lucas

Obrigado pelo seu feedback. Devo admitir que usei essa implementação um pouco como uma "caixa preta", mas não notei nenhum problema. Para esclarecer, você talvez estivesse falando sobre os métodos MoveCameraRight / MoveCameraForward? Esses métodos foram adicionados para movimentar a câmera, mas não fazem parte da interface do Arcball. Se você quiser girar a câmera em torno do alvo, basta alterar as propriedades de guinada ou inclinação.
David Gouveia

Desculpe, você está certo, esses são métodos de panning. Eu não percebi como ele definiu uma bandeira suja para a matriz quando a guinada e o tom foram alterados.
Lucas

0

Aqui está o meu código para girar em torno de um ponto, encontrei-me reutilizando-o bastante.

float distance;      // Straight line distance between the camera and look at point

// Calculate the camera position using the distance and angles
float camX = distance * -sinf(camAngleX*(M_PI/180)) * cosf((camAngleY)*(M_PI/180));
float camY = distance * -sinf((camAngleY)*(M_PI/180));
float camZ = -distance * cosf((camAngleX)*(M_PI/180)) * cosf((camAngleY)*(M_PI/180));

// Set the camera position and lookat point
gluLookAt(camX,camY,camZ,   // Camera position
          0.0, 0.0, 0.0,    // Look at point
          0.0, 1.0, 0.0);   // Up vector

11
Foi basicamente assim que fiz inicialmente. Há muitos problemas em fazer dessa maneira (pelo menos para mim). Basicamente, essa implementação gira apenas cerca de 2 eixos fixos. Vamos dizer que você gire até quase ao pólo norte . Em seguida, gire para a direita . Você se verá girando em um pequeno círculo em vez de seguir o vetor certo da câmera em todo o mundo .
Lucas

Agora que você mencionou isso, acho que existem duas maneiras diferentes de analisar o problema. No meu aplicativo, usei a câmera ArcBall para girar em torno de uma ilha de destino no mar, e se eu usasse 3 eixos de liberdade, pareceria errado (como ver a ilha de cabeça para baixo ou de lado).
David Gouveia

Como obtenho os ângulos da câmera aqui?
Rafvasq 11/11

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.