OpenGL相机 - 来自四元数的视图矩阵行为不正确且音高太受限制

时间:2012-08-26 18:57:13

标签: c++ opengl camera rotation quaternions

我正在使用当前方向(四元数)及其当前位置为我的相机创建视图矩阵。

void Camera::updateViewMatrix()
{
    view = glm::gtx::quaternion::toMat4(orientation);

    // Include rotation (Free Look Camera)
    view[3][0] = -glm::dot(glm::vec3(view[0][0], view[0][1], view[0][2]), position);
    view[3][1] = -glm::dot(glm::vec3(view[1][0], view[1][1], view[1][2]), position);
    view[3][2] = -glm::dot(glm::vec3(view[2][0], view[2][1], view[2][2]), position);

    // Ignore rotation (FPS Camera)
    //view[3][0] = -position.x;
    //view[3][1] = -position.y;
    //view[3][2] = -position.z;

    view[3][3] = 1.0f;
}

这有一个问题,我不相信矩阵计算的四元数给出了正确的答案。翻译相机按预期工作,但旋转它会导致不正确的行为。

我使用当前鼠标位置和屏幕中心之间的差异旋转相机(每帧重置鼠标位置)

int xPos;
int yPos;
glfwGetMousePos(&xPos, &yPos);

int centreX = 800 / 2;
int centreY = 600 / 2;

rotate(xPos - centreX, yPos - centreY);

// Reset mouse position for next frame
glfwSetMousePos(800 / 2, 600 / 2);

旋转发生在此方法

void Camera::rotate(float yawDegrees, float pitchDegrees)
{
    // Apply rotation speed to the rotation
    yawDegrees *= lookSensitivity;
    pitchDegrees *= lookSensitivity;

    if (isLookInverted)
    {
        pitchDegrees = -pitchDegrees;
    }

    pitchAccum += pitchDegrees;

    // Stop the camera from looking any higher than 90 degrees
    if (pitchAccum > 90.0f)
    {
        //pitchDegrees = 90.0f - (pitchAccum - pitchDegrees);
        pitchAccum = 90.0f;
    }

    // Stop the camera from looking any lower than 90 degrees
    if (pitchAccum < -90.0f)
    {
        //pitchDegrees = -90.0f - (pitchAccum - pitchDegrees);
        pitchAccum = -90.0f;
    }

    yawAccum += yawDegrees;

    if (yawAccum > 360.0f)
    {
        yawAccum -= 360.0f;
    }

    if (yawAccum < -360.0f)
    {
        yawAccum += 360.0f;
    }

    float yaw = yawDegrees * DEG2RAD;
    float pitch = pitchDegrees * DEG2RAD;

    glm::quat rotation;

    // Rotate the camera about the world Y axis (if mouse has moved in any x direction)
    rotation = glm::gtx::quaternion::angleAxis(yaw, 0.0f, 1.0f, 0.0f);

    // Concatenate quaterions
    orientation = orientation * rotation;

    // Rotate the camera about the world X axis (if mouse has moved in any y direction)
    rotation = glm::gtx::quaternion::angleAxis(pitch, 1.0f, 0.0f, 0.0f);

    // Concatenate quaternions
    orientation = orientation * rotation;
}

我是否正确连接四元数以获得正确的方向?

音高积累也存在问题,因为它将我的视野限制在〜±5度而不是±90度。可能是什么原因?

编辑:

我已经解决了音高积累的问题,因此它的范围是[-90,90]。事实证明,glm使用度数而不是轴角度的向量,并且四元数级联的乘法顺序不正确。

// Rotate the camera about the world Y axis
// N.B. 'angleAxis' method takes angle in degrees (not in radians)
rotation = glm::gtx::quaternion::angleAxis(yawDegrees, 0.0f, 1.0f, 0.0f);

// Concatenate quaterions ('*' operator concatenates)
// C#: Quaternion.Concatenate(ref rotation, ref orientation)
orientation = orientation * rotation;

// Rotate the camera about the world X axis
rotation = glm::gtx::quaternion::angleAxis(pitchDegrees, 1.0f, 0.0f, 0.0f);

// Concatenate quaterions ('*' operator concatenates)
// C#: Quaternion.Concatenate(ref orientation, ref rotation)
orientation = rotation * orientation;

剩下的问题是视图矩阵旋转似乎旋转绘制的对象而不像普通的FPS相机一样环顾四周。

我已将视频上传到YouTube以演示此问题。我移动鼠标来改变相机的方向,但三角形似乎旋转了。

YouTube video demonstrating camera orientation problem

编辑2:

void Camera::rotate(float yawDegrees, float pitchDegrees)
{
    // Apply rotation speed to the rotation
    yawDegrees *= lookSensitivity;
    pitchDegrees *= lookSensitivity;

    if (isLookInverted)
    {
        pitchDegrees = -pitchDegrees;
    }

    pitchAccum += pitchDegrees;

    // Stop the camera from looking any higher than 90 degrees
    if (pitchAccum > 90.0f)
    {
        pitchDegrees = 90.0f - (pitchAccum - pitchDegrees);
        pitchAccum = 90.0f;
    }
    // Stop the camera from looking any lower than 90 degrees
    else if (pitchAccum < -90.0f)
    {
        pitchDegrees = -90.0f - (pitchAccum - pitchDegrees);
        pitchAccum = -90.0f;
    }

    // 'pitchAccum' range is [-90, 90]
    //printf("pitchAccum %f \n", pitchAccum);

    yawAccum += yawDegrees;

    if (yawAccum > 360.0f)
    {
        yawAccum -= 360.0f;
    }
    else if (yawAccum < -360.0f)
    {
        yawAccum += 360.0f;
    }

    orientation = 
        glm::gtx::quaternion::angleAxis(pitchAccum, 1.0f, 0.0f, 0.0f) * 
        glm::gtx::quaternion::angleAxis(yawAccum, 0.0f, 1.0f, 0.0f);
}

EDIT3:

以下相乘顺序允许相机绕自己的轴旋转但面向错误的方向:

    glm::mat4 translation;
translation = glm::translate(translation, position);

view = glm::gtx::quaternion::toMat4(orientation) * translation;

EDIT4:

以下内容将起作用(根据旋转后的位置应用平移矩阵)

// Rotation
view = glm::gtx::quaternion::toMat4(orientation); 

// Translation
glm::mat4 translation;
translation = glm::translate(translation, -position);

view *= translation;

我无法获得每个方向轴的点积,但

// Rotation
view = glm::gtx::quaternion::toMat4(orientation); 

glm::vec3 p(
    glm::dot(glm::vec3(view[0][0], view[0][1], view[0][2]), position),
    glm::dot(glm::vec3(view[1][0], view[1][1], view[1][2]), position),
    glm::dot(glm::vec3(view[2][0], view[2][1], view[2][2]), position)
    );

// Translation
glm::mat4 translation;
translation = glm::translate(translation, -p);

view *= translation;

1 个答案:

答案 0 :(得分:4)

为了给你一个明确的答案,我认为我们需要的代码显示你实际上如何向OpenGL提供view矩阵和顶点。但是,症状听起来非常典型,不正确的矩阵顺序。

考虑一些变量:
V 表示相机当前方向(四元数)的倒数 T 表示保持相机位置的平移矩阵。这应该是一个单位矩阵,否定相机在第四列的位置(假设我们是右向乘以列向量)。
U 表示方向变化的倒数 p 表示世界空间中的顶点 注意:所有矩阵都是逆矩阵,因为变换将应用于顶点,而不是摄像机,但最终结果是相同的。

默认情况下,OpenGL摄像头位于负z轴向下的原点。当视图不变( U == I )时,顶点从世界坐标到相机坐标的变换应为: p'= TVp 。首先定位摄像机(通过沿相反方向旋转世界),然后将摄像机平移到位(通过向相反方向移动世界)。

现在有几个地方可以放 U 。如果我们将 U 放在 V 的右侧,那么我们就会得到第一人称视角的行为。向上移动鼠标时,当前视图中的任何内容都会在相机周围向下旋转。向右移动鼠标时,视图中的任何内容都会在相机周围向左旋转。

如果我们在 T V 之间放置 U ,则相机会相对于世界的轴而不是相机进行转动。这是一种奇怪的行为。如果 V 碰巧将相机关闭到一侧,那么上下移动鼠标将使世界看起来“滚动”而不是“俯仰”或“偏航”。

如果我们在 T 左侧放置 U ,则相机会绕着世界各地的世界轴旋转。这甚至可能更奇怪,因为它使摄像机离原点越远,相机飞得更快。但是,由于旋转位于原点附近,如果相机恰好看着原点,那么物体就会出现转动。这是你所看到的,因为您正在采取旋转相机位置的点积。

您检查以确保pitchAccum保持在[-90,90]范围内,但您已注释掉将利用该事实的部分。这对我来说似乎很奇怪。

你离开的方式 - 乘以音高但右向乘以偏航使得你的四元数对你没有太大作用。他们只是抱着你的欧拉角。除非来自其他地方的方向更改,否则您可以简单地说orientation = glm::gtx::quaternion::angleAxis(pitchAccum*DEG2RAD, 1.0f, 0.0f, 0.0f) * glm::gtx::quaternion::angleAxis(yawAccum*DEG2RAD, 0.0f, 1.0f, 0.0f);并完全覆盖旧方向。