为什么我的FPS相机是滚动的,一劳永逸?

2024-05-14 13:06:19 发布

您现在位置:Python中文网/ 问答频道 /正文

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

为什么我的相机会一劳永逸地滚动!?:)

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

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

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

模型矩阵

为了正确应用转换,我将矩阵乘以如下:

PxVxM x v

首先是模型,然后是视图,最后是投影。在

式中,通过首先应用旋转,然后应用平移来计算M:

^{pr2}$

代码如下:

^{3}$

视图矩阵

我根据相机的位置和方向计算视图矩阵,如下所示:

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

如果我错了,请纠正我。因为我们只能移动和旋转世界几何体(与移动/旋转相机相反),我必须以相反的顺序乘以矩阵和oposite变换(实际上是每个变换矩阵的逆)。换言之,将摄影机移离对象也可以视为将对象移离摄影机。在

旋转输入

现在,我将如何将键盘输入转换为摄像机旋转。当我按下右/左/上/下箭头键时,我正在调用以下方法,并使用一些俯仰/偏航角度:

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 rot matrix is correct

behaves wrong but rot matrix is correct

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

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

我可以使摄影机的行为与FPS摄影机一样正确,但旋转矩阵似乎不正确。在

behaves well but rot matrix is wrong

enter image description here

有人能给我点启示吗? 提前谢谢。在


Tags: 对象方法模型selfview视图transdef
2条回答

关于你的问题,我告诉过你为什么重用你的视图矩阵不是一个好主意,因为投球和偏航不能通勤。您现在使用的是四元数,但同样,俯仰和偏航四元数不会通勤。只需存储俯仰值和偏航值,然后重新计算 从俯仰和偏航的方向,只要你需要它。在

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

向存储在结构中的现有旋转添加旋转的每个帧。在

^{pr2}$

在一个例子中,你用deltaYaw的新3D帧旋转已经旋转的对象。你现在应用的偏航将考虑到你以前的横摇。在

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

另一方面,从网格位置到所需的euler角度构建旋转。在

是的,你是对的,这不是一个方便的方式来处理相机,保持偏航,俯仰和滚转变量将导致以后的问题(万向锁,相机动画将变得棘手…)。我建议你看看弧形摄像机https://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_Arcball

相关问题 更多 >

    热门问题