Python 为什么我的FPS相机一劳永逸地转动?

Python 为什么我的FPS相机一劳永逸地转动?,python,opengl,graphics,rotation,quaternions,Python,Opengl,Graphics,Rotation,Quaternions,如果我忽略了四元数代数的肮脏细节,我想我理解旋转和平移变换背后的数学。但还是不明白我做错了什么 为什么我的相机会一劳永逸:) 更具体地说,我应该如何从摄像机的方向(旋转矩阵)计算摄像机的视图矩阵 我正在用Python编写一个简约的3d引擎,其中包含一个场景节点类,用于处理3d对象的旋转和平移机制。它具有公开旋转和平移矩阵以及模型矩阵的方法 还有一个CameraNode类,一个Node的子类,它还公开了视图和投影矩阵(投影不是问题,所以我们可以忽略它) 模型矩阵 为了正确应用变换,我将矩阵相乘,如

如果我忽略了四元数代数的肮脏细节,我想我理解旋转和平移变换背后的数学。但还是不明白我做错了什么

为什么我的相机会一劳永逸:)

更具体地说,我应该如何从摄像机的方向(旋转矩阵)计算摄像机的视图矩阵

我正在用Python编写一个简约的3d引擎,其中包含一个场景节点类,用于处理3d对象的旋转和平移机制。它具有公开旋转和平移矩阵以及模型矩阵的方法

还有一个CameraNode类,一个Node的子类,它还公开了视图和投影矩阵(投影不是问题,所以我们可以忽略它)

模型矩阵 为了正确应用变换,我将矩阵相乘,如下所示:

PxVxM x v
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
i、 首先是模型,然后是视图,最后是投影

其中,首先应用旋转,然后应用平移计算M:

M = TxR
代码如下:

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
视图矩阵 我根据相机位置和方向计算视图矩阵,如下所示:

PxVxM x v
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
行为错误,但旋转矩阵正确 这就是我得到的:

现在,令人困惑的是,如果我将上述方法更改为:

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
类CameraNode(节点):
def视图_mat(自):
视图=np.眼睛(4)
trans=自译
旋转=自旋转
trans[:-1,3]=-trans[:-1,3]
#rot=rot.T#关于您的问题,我告诉过您为什么重用视图矩阵不是一个好主意,因为俯仰和偏航不会相互影响。您现在使用的是四元数,但俯仰和偏航四元数不能相互转换。只需存储俯仰值和偏航值,然后重新计算
需要时,从俯仰和偏航确定方向

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

关于上一个屏幕截图中摄影机旋转矩阵和对象旋转矩阵如何不同的说明:对象旋转矩阵和平移矩阵(连同模型矩阵)描述了从对象坐标到世界坐标的转换,而视图矩阵描述了从世界坐标摄影机坐标的转换


因此,为了使三脚架显示为相对于视口的轴对齐,则视图旋转矩阵必须是模型旋转矩阵的逆矩阵。

您不应在一个旋转矩阵、方向向量或四元数中累积所有欧拉角

做一些事情,比如:

vec3 euler = vec3(yaw, pitch, roll);
rot *= quaternion(euler)
or
self.orientation = quaternion(euler) * self.orientation
将旋转添加到存储在结构中的现有旋转中的每个帧

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);
在一种情况下,通过deltaYaw旋转已旋转的对象及其新3D帧。您现在应用的偏航将考虑到您之前所做的横摇

oldYaw * oldRoll * deltaYaw != (oldYaw * deltaYaw) * oldRoll
另一方面,构建从网格位置到所需euler角度的旋转


是的,你是对的,这不是一种处理相机的方便方法,保持偏航、俯仰和滚动的变量将导致以后的问题(万向节锁定,相机动画将变得棘手…)。我建议您看看arcBall camera

在您的上一个示例中,您只处理沥青,如果您在第一个示例中仅保留沥青,您是否会得到相同的结果?我不认为你想用你现在的方式(俯仰然后偏航)来控制你的旋转。那么你可能想要在arc上有一个OKball@Draykoon,谢谢你的评论。如果你的建议是:“不要设置
self.orientation=rot*self.orientation
inside
def rotate\u in_xx(self,pitch)
set
self.orientation=rot*self.orientation
inside
def rotate\u in_yy(self,yaw)
”那么我向你脱帽致敬。谢谢,这解决了我的问题:))但是我不完全明白为什么这解决了问题。我相信,一旦我增加更多的复杂性,这个问题或类似的问题最终会再次困扰我,例如添加子节点,而我现在还没有真正考虑过这个问题。可能会帮助你理解这个问题。所以现在你只处理偏航,对吗?您可能希望将俯仰和偏航存储在两个单独的变量上,并设置
self.orientation=quat.fromEuler(俯仰,偏航,0.0f)*m_initialForyardVector
您可能不想执行一些“*=”操作,因为在下一帧中,您的新偏航将影响previus滚动等…您为什么说我正在重用视图矩阵?我使用四元数来保持节点的方向。根据需要,从四元数计算视图矩阵。我不认为错误累积是这里的问题。这个问题与我之前的问题不同,因为我现在使用四元数,而不是保持Euler角度。在我的上一个屏幕截图中,相机旋转矩阵和对象旋转矩阵并不相同。事实上,我在图像本身中以绿色信息声明。这就是发布这个问题的全部原因。换句话说,第二个示例显示我能够实现FPS摄影机行为,但方向矩阵不正确,我显示将摄影机旋转矩阵与受相同旋转影响的参考对象进行比较。视图矩阵在
def view_mat()
中计算,我分别用
trans[:-1,3]=-trans[:-1,3]
rot=rot.T
反转平移和旋转矩阵。对,您的四元数不是视图矩阵,但与俯仰和偏航相对应的四元数也不会转换(I*j!=j*I),所以你会遇到和以前完全相同的问题,我不是用一个四元数来表示俯仰、偏航和角度,而是用一个四元数来表示节点的方向。然后根据用户输入生成旋转四元数,然后应用(乘以)相应的四元数。四元数乘法也不是co