我一直在遵循位于https://paroj.github.io/gltut/的教程学习OpenGL。
通过基础知识,我有点坚持理解四个四元组及其与空间取向和转换的关系,尤其是从世界到摄像机空间到摄像机,反之亦然。在相机相关方向的章节中,作者制作了一个相机,该相机在 world 中相对于相机方向旋转模型。引用:
我们想应用方向偏移(R(,该偏置在相机空间中取得点数。如果我们想将其应用于摄像机矩阵,它将仅乘以相机矩阵:r * c * o * p。这很好,但是我们想将转换应用于o,而不是c。
我没有受过教育的猜测是,如果我们将偏移量应用于相机空间,我们将获得第一人称相机。它是否正确?取而代之的是,将偏移量应用于 world 空间中的模型,使太空飞船相对于该空间而不是相机空间。我们只是从相机空间观察到它。
至少受到对四局的了解(或我认为(的启发,我试图实施第一人称摄像机。它有两个属性:
struct Camera{
glm::vec3 position; // Position in world space.
glm::quat orientation; // Orientation in world space.
}
对键盘动作的反应进行了修改,而由于屏幕上的鼠标运动而导致的方向发生了变化。
注意:GLM过载*
glm::quat * glm::vec3
操作员与通过Quaternion旋转向量的关系(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));
组合模型,视图和投影矩阵并将结果传递给顶点着色器可以显示一切正常 - 人们期望从第一人称POV中看到事物的方式。但是,当我添加鼠标运动时,事情会变得凌乱,以跟踪x和y方向的运动量。我想围绕 world y轴和 local 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
计算视图矩阵:
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;
}
glm::lookAt
用于构建视图矩阵的使用情况。相反,我现在像这样构造视图矩阵:
auto rotate = glm::mat4_cast(entity->orientation);
auto translate = glm::mat4(1.0f);
translate = glm::translate(translate, -entity->position);
viewMatrix = rotate * translate;
用于翻译,我现在以方向的倒数而不是方向乘以。
glm::quat invOrient = glm::conjugate(orientation);
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {
worldOffset = invOrient * (axis_vectors[AxisVector::AXIS_Z_NEG]);
position += worldOffset * scaleFactor;
}
...
其他所有内容都是相同的,除了鼠标移动代码中的一些进一步的偏置四元素正常化。
相机现在的行为和感觉就像是第一人称摄像机。
如果有的话,我仍然不正确理解视图矩阵和Lookat矩阵之间的区别。但这是另一个问题的话题。