【问题标题】:OpenGL camera - View matrix from quaternion behaves incorrectly and pitch is too restrictedOpenGL 相机 - 来自四元数的视图矩阵行为不正确且音高太受限制
【发布时间】:2012-08-21 09:49:10
【问题描述】:

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

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;

【问题讨论】:

    标签: c++ opengl camera rotation quaternions


    【解决方案1】:

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

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

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

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

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

    如果我们把 U 放在 T 的左边,那么相机会围绕世界坐标轴围绕世界原点旋转。这可能更奇怪,因为它使相机飞越世界越快,相机离原点越远。然而,因为旋转是围绕原点进行的,如果相机恰好在看原点,那里的物体就会看起来像是在转动。这就是您所看到的,因为您使用点积来旋转相机的位置。

    您检查以确保 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); 并完全覆盖旧的方向。

    【讨论】:

    • 您检查以确保 pitchAccum 保持在 [-90,90] 范围内,但您已注释掉将利用该事实的部分。这对我来说似乎很奇怪。 在您写回复之前,我已取消注释此代码并声明 pitchAccum 确实保持在 [-90, 90] 范围内。也许您的浏览器没有收到它?
    • 提供视图、投影和模型矩阵(因此不需要标识矩阵)并在顶点着色器中相乘(转换)。 gl_Position = MVP * vec4(vertexPosition_modelspace, 1);
    • MVP制服设置如下glm::mat4 MVP = g_activeCamera-&gt;getProjectionMatrix() * g_activeCamera-&gt;getViewMatrix();glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &amp;MVP[0][0]);
    • @user1423893 我可以看到pitchAccum 将保持在您规定的范围内,但您实际上并没有使用pitchAccum;所以没关系。当您夹住pitchAccum 时,您保持pitchDegrees 不变。然后你使用那​​个值。因此,如果您达到了音高限制,您仍然会继续投球,即使 pitchAccum 没有变大/变小。
    • 你是绝对正确的。那是我的疏忽。谢谢你:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-19
    • 2017-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多