¿Por qué mi cámara FPS está rodando, de una vez por todas?

Si ignoro los detalles sórdidos del álgebra de los cuaterniones, creo que entiendo las matemáticas detrás de la rotación y las transformaciones de la traducción. Pero todavía no entiendo lo que estoy haciendo mal.

¿Por qué mi cámara está rodando de una vez por todas? 🙂

Y para ser un poco más específico, ¿cómo debo calcular la matriz de visualización de la cámara desde su orientación (matriz de rotación)?

Estoy escribiendo un motor 3D minimalista en Python con una clase de Nodo de escena que maneja la mecánica de rotación y traducción de objetos 3D. Tiene métodos que exponen las matrices de Rotación y Traducción, así como la Matriz del Modelo.

También hay una clase CameraNode, una subclase de Nodo, que también expone las matrices de Vista y Proyección (la proyección no es el problema, así que podemos ignorarlo).

Matriz Modelo

Para aplicar correctamente las transformaciones multiplico las matrices de la siguiente manera:

PxVxM xv 

es decir, primero el modelo, luego la vista y finalmente la proyección.

Donde M se calcula aplicando primero la rotación y luego la traducción:

 M = TxR 

Aquí está el código:

 class Node(): # ... def model_mat(self): return self.translation_mat() @ self.rotation_mat() @ self.scaling_mat() def translation_mat(self): translation = np.eye(4, dtype=np.float32) translation[:-1, -1] = self.position # self.position is an ndarray return translation def rotation_mat(self): rotation = np.eye(4, dtype=np.float32) rotation[:-1, :-1] = qua.as_rotation_matrix(self.orientation) # self.orientation is a quaternion object return rotation 

Ver matriz

Estoy calculando la matriz de Vista según la posición y orientación de la cámara, de la siguiente manera:

 class CameraNode(Node): # ... def view_mat(self): trans = self.translation_mat() rot = self.rotation_mat() trans[:-1, 3] = -trans[:-1, 3] # <-- efficient matrix inversion rot = rot.T # <-- efficient matrix inversion self.view = rot @ trans return self.view 

Por favor, corríjame si estoy equivocado. Como solo podemos mover y rotar la geometría del mundo (en lugar de mover / rotar la cámara), tengo que multiplicar las matrices en el orden inverso y también la transformación opuesta (de hecho, la inversa de cada matriz de transformación). En otras palabras, alejar la cámara de un objeto también se puede ver como alejar el objeto de la cámara.

Entrada a rotación

Ahora, así es como convierto la entrada del teclado a la rotación de la cámara. Cuando presiono las teclas de flecha derecha / izquierda / arriba / abajo, estoy llamando a los siguientes métodos con algún ángulo de inclinación / giro:

 def rotate_in_xx(self, pitch): rot = qua.from_rotation_vector((pitch, 0.0, 0.0)) self.orientation *= rot def rotate_in_yy(self, yaw): rot = qua.from_rotation_vector((0.0, yaw, 0.0)) self.orientation *= rot 

Se comporta mal pero la matriz de rotación es correcta

Y esto es lo que obtengo:

se comporta mal pero la matriz de podredumbre es correcta

se comporta mal pero la matriz de podredumbre es correcta

Ahora, confusamente, si cambio los métodos anteriores a:

 class CameraNode(Node): def view_mat(self): view = np.eye(4) trans = self.translation_mat() rot = self.rotation_mat() trans[:-1, 3] = -trans[:-1, 3] # rot = rot.T # <-- COMMENTED OUT self.view = rot @ trans return self.view def rotate_in_xx(self, pitch): rot = qua.from_rotation_vector((pitch, 0.0, 0.0)) self.orientation = rot * self.orientation # <-- CHANGE 

Puedo hacer que la cámara se comporte correctamente como una cámara FPS, pero la matriz de rotación no parece correcta.

se comporta bien pero la matriz de podredumbre está mal

introduzca la descripción de la imagen aquí

Por favor, ¿podría alguien arrojar algo de luz? Gracias por adelantado.

En mi última respuesta a su problema, le dije por qué no es una buena idea reutilizar su matriz de vista, porque el lanzamiento y la orientación no conmutan. Ahora está utilizando los cuaterniones, pero nuevamente, los cuaterniones de cabeceo y desvío no se conmutan. Solo almacene el valor de tono y el valor de guiñada, y vuelva a calcular la orientación a partir del tono y guiñe cuando lo necesite.

 def rotate_in_xx(self, pitch): self.pitch += pitch def rotate_in_yy(self, yaw): self.yaw += yaw def get_orientation(): pitch_rotation = qua.from_rotation_vector((self.pitch, 0.0, 0.0)) yaw_rotation = qua.from_rotation_vector((0.0, self.yaw, 0.0)) return yaw_rotation * pitch_rotation 

Una nota sobre cómo en su última captura de pantalla la matriz de rotación de la cámara y la matriz de rotación del objeto no son idénticas: las matrices de rotación y traslación del objeto (junto con la matriz del modelo) describen la transformación de las coordenadas del objeto a las coordenadas del mundo , mientras que la matriz de la vista describe la transformación de coordenadas mundiales a coordenadas de cámara .

Por lo tanto, para que el trípode se muestre alineado con el eje en relación con su ventana gráfica, la matriz de rotación de la vista debe ser la inversa de la matriz de rotación del modelo.

No debe acumular todos sus angularjs de Euler en una rotaciónMatriz, direcciónVector o Cuaternión.

Haciendo cosas como:

 vec3 euler = vec3(yaw, pitch, roll); rot *= quaternion(euler) or self.orientation = quaternion(euler) * self.orientation 

Cada cuadro agrega una rotación a una rotación ya existente almacenada en su estructura.

 float deltaYaw = getInput(); float deltaPitch = getInput(); m_rotation = m_rotation * euler(deltaYaw, deltaRoll, 0.0f) is not equal to m_rotation = rotationMatrix(m_yaw + deltaYaw, m_pitch + deltaRoll, 0.0f); 

En un caso, gira un objeto ya girado con su nuevo marco 3D de deltaYaw. La guiñada que aplique ahora tomará en cuenta la tirada que hizo anteriormente.

 oldYaw * oldRoll * deltaYaw != (oldYaw * deltaYaw) * oldRoll 

En el otro, construye la rotación desde la posición de la malla hasta los angularjs de euler solicitados.

Sí, tienes razón, no es una forma conveniente de manejar la cámara, ya que mantener las variables de desvío, inclinación y balanceo generará problemas más adelante (locking de gimBall, animación de la cámara puede ser difícil …). Recomendaría mirar la cámara arcBall https://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_Arcball