简体   繁体   中英

Why is my FPS Camera rolling? Using euler angles (not quaternions) Implementation in Python

I am trying to generate a view matrix that would behave as an FPS camera, but instead I am getting roll. Sharing my python code in case someone can spot what the problem is:

import numpy as np

class Camera():
    def __init__(self):
        self.sens = np.array([0.002, 0.002])  # mouse sensitivity (x, y)
        self.angpos = np.array([0.0, 0.0])  # (pitch, yaw)

    def mouse_move(self, pos, rel):
        self.angpos = rel[::-1]*self.sens  # mouse relative motion (delta-x, delta-y)
        ya = self.angpos[1]
        yrot = np.array([
            [ np.cos(ya),  0.0,    np.sin(ya),   0.0],
            [ 0.0,         1.0,    0.0,          0.0],
            [-np.sin(ya),  0.0,    np.cos(ya),   0.0],
            [0.0,          0.0,    0.0,          1.0]
        ])

        xa = self.angpos[0]
        xrot = np.array([
            [ 1.0,    0.0,          0.0,          0.0 ],
            [ 0.0,    np.cos(xa),  -np.sin(xa),   0.0 ],
            [ 0.0,    np.sin(xa),   np.cos(xa),   0.0 ],
            [ 0.0,    0.0,          0.0,          1.0 ],
        ])

        return yrot @ xrot  # view matrix


# this is the callback for mouse movement. `pos` is absolute mouse 
# position in the screen and `rel` is the position relative to previous call
# def mouseMotionEvent(self, pos, rel):
#     view = self.cam.mouse_move(pos, rel)  # self.cam is an instance of Camera
#     self.view = view @ self.view
#     return True

Follows a gif of the behavior I am getting: 在此处输入图片说明

Abstractly, your problem is that rotations induced by pitch and yaw are not closed under composition.

More concretely: Imagine controlling a first-person camera. Look down, then turn to the left and the right. Your view will move in a very different way than if you were to turn while looking straight ahead. Your calculations, however, act like the camera will behave equally.

Each tick, you multiply view from the right with a yaw matrix, then a pitch matrix. This means that after a while your view matrix will be an alternating product of a lot of pitch and yaw matrices. However, pitch and yaw matrices do not commute. What you actually want is to have all the pitch matrices to the left of all the yaw matrices (or to the right, depending on whether you let your view matrix operate from the left or the right, and on whether your matrix represents view-to-global or global-to-view transform).

So a quick fix to this would be to write view = yrot @ view @ xrot . That way, all your y rotations end up to the left of your x rotations, and everything will be fine. Well, at least for a while, but then your view matrix may end up accumulating rounding errors, and the view may become rolled or skewed or worse.

What I recommend is that you don't reuse the view matrix at all. Instead, just store the player's current pitch and yaw, updating it on mouse motion, and then recalculate the view matrix directly from pitch and yaw every time. This way you can also use pitch and yaw in other ways (for example you'll want to clamp pitch to a certain range to prevent your player from doing handstands or somersaults), and you also won't accumulate errors in your matrix.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM