Glm 四元数观察函数

jbi*_*lls 2 c++ opengl quaternions glm-math

我正在尝试编写一个使用 glm::quat 来表示旋转的观察函数,基于这个答案。然而,我在获得正确的角度时遇到了麻烦。这是我的观察功能:

void Camera::LookAt(float x, float y, float z) {
    glm::vec3 lookVector = glm::vec3(x, y, z);
    assert(lookVector != position);

    glm::vec3 direction = glm::normalize(lookVector-position);
    float dot = glm::dot(glm::vec3(0, 0, -1), direction);
    if (fabs(dot - (-1.0f)) < 0.000001f)
        rotation = glm::quat(RadiansToDegrees(M_PI), 0.0f, 1.0f, 0.0f);
    if (fabs(dot - (1.0f)) < 0.000001f)
        rotation = glm::quat();

    float angle = RadiansToDegrees(acosf(dot));

    glm::vec3 cross = (glm::cross(glm::vec3(0, 0, -1), direction));
    rotation = glm::normalize(glm::angleAxis(angle, cross));

    std::cout << glm::eulerAngles(rotation).x  << " " << glm::eulerAngles(rotation).y << " " << glm::eulerAngles(rotation).z << "\n";
}
Run Code Online (Sandbox Code Playgroud)

当我的相机在 (0.0f, 0.0f, -10.0f) 时调用 LookAt(0.0f, 0.0f, 0.0f) 时,这会输出正确的 0,0,0 旋转。但是,如果我将相机转换为 (0.0f, -0.01f, -10.0f) 或更多,我会得到大约 124,0,0 的旋转。如果我继续将 y 转换为 -0.01f,这会下降。如果我不标准化四元数,我就不会遇到这个问题。还是绕x轴旋转了124圈,但外观还是不错的。但是,如果我稍后将四元数归一化,它似乎再次旋转到大约 124。我无法归一化cross,因为这样做会引发断言。什么会导致我从我的观察函数中得到关于 x 的 124 欧拉角,我该如何解决?

Ben*_*aub 5

从 0.9.9.0 版本开始,有一个功能可以<glm/gtc/quaternion.hpp>做你想做的事情:

template<typename T, qualifier Q>
tquat<T, Q> quatLookAt(vec<3, T, Q> const& direction, vec<3, T, Q> const& up);
Run Code Online (Sandbox Code Playgroud)

它是由这个拉取请求添加的,并已于 2017 年 7 月 24 日合并到 master 中。

但是

  1. 方向必须是归一化向量!
  2. 方向不能平行向上!

因此,您可能希望围绕该函数编写一个更安全的包装器:

glm::quat safeQuatLookAt(
    glm::vec3 const& lookFrom,
    glm::vec3 const& lookTo,
    glm::vec3 const& up,
    glm::vec3 const& alternativeUp)
{
    glm::vec3  direction       = lookTo - lookFrom;
    float      directionLength = glm::length(direction);

    // Check if the direction is valid; Also deals with NaN
    if(!(directionLength > 0.0001))
        return glm::quat(1, 0, 0, 0); // Just return identity

    // Normalize direction
    direction /= directionLength;

    // Is the normal up (nearly) parallel to direction?
    if(glm::abs(glm::dot(direction, up)) > .9999f) {
        // Use alternative up
        return glm::quatLookAt(direction, alternativeUp);
    }
    else {
        return glm::quatLookAt(direction, up);
    }
}
Run Code Online (Sandbox Code Playgroud)