If I ignore the sordid details of quaternions algebra I think I understand the maths behind rotation and translation transformations. But still fail to understand what I am doing wrong.
Why is my camera rolling once and for all!? :)
And to be a bit more specific, how should I compute the camera View matrix from its orientation (rotation matrix)?
I am writing a minimalistic 3d engine in Python with a scene Node class that handles the mechanics of rotation and translation of 3d objects. It has methods that expose the Rotation and Translation matrices as well as the Model matrix.
There is also a CameraNode class, a subclass of Node, that also exposes the View and Projection matrices (projection is not the problem, so we can ignore it).
Model Matrix
In order to correctly apply the transformations I multiply the matrices as follows:
PxVxM x v
i.e first the Model, then the View and finally the Projection.
Where M is computed by first applying the rotation and then the translation:
M = TxR
Here's the code:
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
View Matrix
I am computing the View matrix based on the camera position and orientation, as follows:
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
Please correct me if I am wrong. Since we can only move and rotate the world geometry (as opposed to moving/rotating the camera) I have to multiply the matrices in the reverse order and also the oposite transformation (effectively the inverse of each transformation matrix). In other words, moving the camera away from an object can also be seen as moving the object away from the camera.
Input to rotation
Now, here's how I convert keyboard input into camera rotation. When I press the right/left/up/down arrow keys I am calling the following methods with some pitch/yaw angle:
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
Behaves wrong but rotation matrix is correct
And this is what I get:
Now, confusingly, if I change the above methods to:
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
I can make the camera behave correctly as an FPS camera, but the rotation matrix does not seem right.
Please could someone shed some light? Thanks in advance.