我已经按照https://paroj.github.io/gltut/上的教程学习了OpenGL。

通过基础知识,我对四元数及其与空间方向和变换的关系有些了解,尤其是从世界空间到摄影机空间,反之亦然。在“相对于相机的方向”一章中,作者制作了一个相机,该相机相对于相机的方向在世界空间中旋转模型。报价单:



我没有根据的猜测是,如果将偏移量应用于摄影机空间,我们将获得第一人称摄影机。它是否正确?取而代之的是,将偏移量应用于世界空间中的模型,从而使飞船相对于该空间而不是相对于摄影机空间旋转。我们只是观察它从相机空间旋转。

受到至少对四元数的某种理解(或我认为)的启发,我尝试实现了第一人称摄影机。它具有两个属性:

struct Camera{
  glm::vec3 position; // Position in world space.
  glm::quat orientation; // Orientation in world space.
}

位置会根据键盘操作进行修改,而方向会由于鼠标在屏幕上的移动而发生变化。

注意:GLM重载了*glm::quat * glm::vec3运算符,并具有通过四元数旋转 vector 的关系(v' = qvq^-1的更紧凑形式)

例如,向前移动并向右移动:
glm::vec3 worldOffset;
float scaleFactor = 0.5f;
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {
    worldOffset = orientation * (axis_vectors[AxisVector::AXIS_Z_NEG]); // AXIS_Z_NEG = glm::vec3(0, 0, -1)
    position += worldOffset * scaleFactor;
}
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) {
    worldOffset = orientation  * (axis_vectors[AxisVector::AXIS_X_NEG]); // AXIS_Z_NEG = glm::vec3(-1, 0, 0)
    position += worldOffset * scaleFactor;
}

将方向和位置信息传递到glm::lookAt矩阵,以构造世界到相机的转换,如下所示:
auto camPosition = position;
auto camForward = orientation * glm::vec3(0.0, 0.0, -1.0);
viewMatrix = glm::lookAt(camPosition, camPosition + camForward, glm::vec3(0.0, 1.0, 0.0));

组合模型, View 和投影矩阵,并将结果传递给顶点着色器,可以显示一切正常-人们期望从第一人称POV看到事物的方式。但是,当我添加鼠标移动并跟踪x和y方向上的移动量时,事情会变得困惑。我想绕世界的y轴和局部x轴旋转:
auto xOffset = glm::angleAxis(xAmount, axis_vectors[AxisVector::AXIS_Y_POS]); // mouse movement in x-direction
auto yOffset = glm::angleAxis(yAmount, axis_vectors[AxisVector::AXIS_X_POS]); // mouse movement in y-direction
orientation = orientation * xOffset; // Works OK, can look left/right
orientation = yOffset * orientation; // When adding this line, things get ugly

这是什么问题?
我承认,我没有足够的知识来正确调试鼠标移动代码,我主要遵循以下内容:“向右乘以在世界空间中应用偏移,向左乘以在相机空间中进行偏移”。

我觉得我已经了解了一半,从关于该主题的大量电子资源中得出了结论,同时又受到了更多的教育和困惑。
感谢您的回答。

最佳答案

旋转表示方向的glm四元数:

//Precomputation:
//pitch (rot around x in radians),
//yaw (rot around y in radians),
//roll (rot around z in radians)
//are computed/incremented by mouse/keyboard events

要计算 View 矩阵:
void CameraFPSQuaternion::UpdateView()
{
  //FPS camera:  RotationX(pitch) * RotationY(yaw)
  glm::quat qPitch = glm::angleAxis(pitch, glm::vec3(1, 0, 0));
  glm::quat qYaw = glm::angleAxis(yaw, glm::vec3(0, 1, 0));
  glm::quat qRoll = glm::angleAxis(roll,glm::vec3(0,0,1));

  //For a FPS camera we can omit roll
  glm::quat orientation = qPitch * qYaw;
  orientation = glm::normalize(orientation);
  glm::mat4 rotate = glm::mat4_cast(orientation);

  glm::mat4 translate = glm::mat4(1.0f);
  translate = glm::translate(translate, -eye);

  viewMatrix = rotate * translate;
}

如果要存储四元数,则每当偏航,俯仰或横摇变化时,都要重新计算它:
void CameraFPSQuaternion::RotatePitch(float rads) // rotate around cams local X axis
{
  glm::quat qPitch = glm::angleAxis(rads, glm::vec3(1, 0, 0));

  m_orientation = glm::normalize(qPitch) * m_orientation;
  glm::mat4 rotate = glm::mat4_cast(m_orientation);

  glm::mat4 translate = glm::mat4(1.0f);
  translate = glm::translate(translate, -eye);

  m_viewMatrix = rotate * translate;
}

如果要给出围绕给定轴的旋转速度,请使用slerp:
void CameraFPSQuaternion::Update(float deltaTimeSeconds)
{
  //FPS camera:  RotationX(pitch) * RotationY(yaw)
  glm::quat qPitch = glm::angleAxis(m_d_pitch, glm::vec3(1, 0, 0));
  glm::quat qYaw = glm::angleAxis(m_d_yaw, glm::vec3(0, 1, 0));
  glm::quat qRoll = glm::angleAxis(m_d_roll,glm::vec3(0,0,1));

  //For a FPS camera we can omit roll
  glm::quat m_d_orientation = qPitch * qYaw;
  glm::quat delta = glm::mix(glm::quat(0,0,0,0),m_d_orientation,deltaTimeSeconds);
  m_orientation = glm::normalize(delta) * m_orientation;
  glm::mat4 rotate = glm::mat4_cast(orientation);

  glm::mat4 translate = glm::mat4(1.0f);
  translate = glm::translate(translate, -eye);

  viewMatrix = rotate * translate;
}

关于c++ - 基于四元数的第一人称视角相机,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49609654/

10-13 08:34