¿Cómo puedo orbitar una cámara sobre su punto objetivo?

18

Estoy dibujando una escena en la que la cámara se mueve libremente por el universo. La clase de cámara realiza un seguimiento del punto de vista (o vista ), la posición de la cámara y el vector hacia arriba. Estos vectores / puntos se pasan a gluLookAt.

Pan y zoom son casi triviales de implementar. Sin embargo, yo estoy encontrando rotación alrededor del vistazo a punto de ser mucho más de un problema. Quiero escribir una función Camera.rotate que toma 2 ángulos, uno que gira hacia arriba / abajo y otro que gira hacia la izquierda / derecha a lo largo de una esfera imaginaria que se centra en el punto de mira .

¿Hay una forma fácil de hacer esto?

Leí (brevemente) sobre los cuaterniones, pero quería ver si había una solución más fácil dada la construcción relativamente simple de mi escena.

Luke
fuente
¿Tiene algo en su caja de herramientas para rotar un punto alrededor del origen, dado un eje y un ángulo?
sam hocevar
Nada más que mi comprensión de Quaternions, no. Cuanto más estoy mirando esto, creo que Quaternions podría ser la respuesta. Sin embargo, no estoy seguro de cómo calcular los valores del eje x, y y z para usar en las fórmulas de Quaternion.
Luke
No te diré que los cuaterniones no son el camino a seguir. Son una muy buena solución a su problema. Pero necesitará una clase de cuaternión y formas de interactuar con GL y GLU, y creo que primero debe familiarizarse con las matrices de transformación. Otros pueden estar en desacuerdo.
sam hocevar
considere en qué orden realiza las rotaciones. La aplicación de rotaciones para moverse al espacio mundial o para moverse al espacio de la cámara es diferente
Charlie

Respuestas:

25

Lo que está pidiendo se llama rotación de Arcball. Los cuaterniones son la solución fácil solo si comprende cómo funcionan. Sin embargo, puede lograr lo mismo sin cuaterniones.

Prerrequisitos

¿Sabes cómo rotar objetos en general? Digamos que tienes un objeto en el origen. ¿Sabes cómo lo giras (pista: multiplica por una matriz de rotación)? En caso afirmativo, supongo que sabe lo que sucederá si traduce el objeto primero y luego lo gira.

Debe saber cómo calcular una matriz de rotación a partir del eje angular (fácil como un pastel, mire la miríada de ecuaciones en línea, muchas de ellas también le dan el código)

Solución

  • Obtener de la cámara hacia arriba y la derecha vectores. Tenga en cuenta que deben normalizarse.
  • Obtenga el vector desde el punto de enfoque a la cámara (camPosition - Focus). Este es el vector que vas a rotar. Llamemos esto camFocusVector .
  • Decida cuánto desea rotar en guiñada / inclinación en relación con la cámara
  • Crea dos matrices de rotación. La primera matriz de rotación usará la parte superior de la cámara como el eje y el ángulo de guiñada que usted decidió. La segunda matriz de rotación usará la derecha de la cámara como eje y paso de ángulo que se decidió.
  • Ahora gire el camFocusVector con las nuevas matrices de rotación. Esta es ahora su nueva posición de la cámara en relación con el origen. Por supuesto, queremos que sea relativo al punto de enfoque ...
  • Agregue la posición del punto de enfoque a camFocusVector . Esta es ahora la nueva posición de su cámara. Traduce tu cámara en consecuencia.
  • Finalmente, pídale a la cámara que enfoque el punto de enfoque llamando a su función lookAt ()

Advertencias

Tendrá que buscar ciertos casos o singularidades en los que su cámara dejará de funcionar. Mirando hacia abajo / arriba, por ejemplo. Te dejaré descubrir cómo lidiar con eso.

EDITAR1: Cómo recalcular los vectores ortonormales de la cámara

Ya conoce la dirección de la cámara ((cameraPos - focusPoint) .normalize ()). Ahora suponga que su cámara está arriba + Y (o w / e el eje arriba actual de su mundo es ... eso depende de usted). Ahora simplemente cruce la dirección con la flecha hacia arriba para obtener la derecha . ¿Hecho? No! Su vector ascendente ya no es ortogonal a los otros dos. Para corregir esto, cruz derecha con dirección y se obtiene su nuevo arriba .

Tenga en cuenta que el Gram-Schmidt es realmente lo que debe usarse para ortonormalizar vectores.

Nuevamente, tome nota de las advertencias ya que esto no funcionará en algunos casos (la dirección es paralela a arriba, por ejemplo).

Samaursa
fuente
Tenga en cuenta que el vector correcto se puede obtener usando normalize(up ^ camFocusVector)(o su opuesto si es zurdo).
sam hocevar
Se ve bien hasta ahora, funcionó muy bien para la rotación izquierda / derecha (tengo el vector arriba, así que es fácil). ¿Qué significa '^' en tu comentario @SamHocevar? ¿Es ese producto cruzado? Además, ¿cómo puedo volver a calcular el vector una vez que he realizado las traducciones?
Luke
@Luke: Revisa mi EDIT1
Samaursa
1
@Samaursa Muchas gracias. ¡Su solución funciona perfectamente y aprendí mucho en el proceso!
Lucas,
2
Esta es una EXCELENTE respuesta, muchas gracias por su explicación. En mi caso, quería que la cámara girara solo sobre el punto objetivo en el plano XY, y después de hacer todos los cálculos, siempre configuré cameraRight.z en 0 y luego calculé el vector cameraUp. Esto dio el efecto deseado. Solo pensé en compartir
codemonkey
1

Lo que necesita es una cámara ArcBall típica, que es básicamente una cámara que mantiene una ubicación de destino y le permite mover la cámara de forma "esférica" ​​alrededor de ese objetivo.

Está en XNA pero en IIRC. He usado esta implementación antes, y funcionó bastante bien:

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

David Gouveia
fuente
Esa implementación parece moverse solo directamente a lo largo del vector correcto, lo que se aproximará a una bola de arco para pequeños valores de cantidad . También está moviendo el punto LookAt, como donde quiero mover la cámara (cerca, pero sutilmente diferente). Creo que Samaursa ha proporcionado la forma más simple y completa de lograr esto.
Lucas,
Gracias por tus comentarios. Debo admitir que usé esta implementación un poco como una "caja negra", pero no noté ningún problema. Para aclarar, ¿quizás hablaste de los métodos MoveCameraRight / MoveCameraForward? Esos métodos se agregaron para desplazar la cámara, pero no son parte de la interfaz de Arcball. Si desea rotar la cámara alrededor del objetivo, simplemente cambie las propiedades de guiñada o inclinación.
David Gouveia
Lo siento, tienes razón, esos son métodos de panorámica. No me di cuenta de cómo él puso una bandera sucia para la matriz cuando se cambiaron la inclinación y el tono.
Lucas
0

Aquí está mi código para girar alrededor de un punto, me encontré reutilizándolo mucho.

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
Stephen Tierney
fuente
1
Esa es básicamente la forma en que lo hice inicialmente. Hay muchos problemas para hacerlo de esta manera (al menos para mí). Básicamente, esta implementación gira solo alrededor de 2 ejes fijos. Digamos que gira hasta casi hasta el polo norte . Luego gire a la derecha . Te encontrarás girando en un pequeño círculo en lugar de seguir el vector correcto de la cámara en todo el mundo .
Lucas,
Ahora que mencionó eso, creo que hay dos formas diferentes de ver el problema. En mi aplicación, utilicé la cámara ArcBall para girar alrededor de una isla objetivo en el mar, y si usara 3 ejes de libertad, parecería completamente incorrecto (como ver la isla al revés o de lado).
David Gouveia
¿Cómo obtengo los ángulos de la cámara aquí?
rafvasq
0

Este algoritmo muy simple para lograrlo es bastante impresionante:

Siendo P el punto central sobre el que desea rotar (el punto "mirar" u "objetivo"):

translate(-P)

rotate horizontal
rotate vertical

translate(P)

Lo usé y es agradable.

Fuente encontrada en el stackoverflow del sitio hermano: /programming/287655/opengl-rotating-a-camera-around-a-point

diosney
fuente