Glm Quaternion lookat功能

时间:2013-08-11 13:25:41

标签: c++ opengl quaternions glm-math

我正在尝试编写一个使用glm :: quat表示旋转的lookat函数,基于此answer。然而,遇到正确的角度我遇到了麻烦。这是我的lookat函数:

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";
}

当我的相机处于(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,因为这样做会抛出一个断言。什么会导致我从我的lookat函数得到大约124的euler角度,我该如何解决?

2 个答案:

答案 0 :(得分:4)

从版本0.9.9.0开始,<glm/gtx/quaternion.hpp>中的一个函数主要执行您想要的操作:

template<typename T, qualifier Q>
tquat<T, Q> quatLookAt(vec<3, T, Q> const& direction, vec<3, T, Q> const& up);

this pull request添加,并于2017年7月24日合并为主人。

<强>但是

  1. 方向必须是规范化的向量!
  2. 方向不能平行!
  3. 所以你可能想在函数周围编写一个更安全的包装器:

    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);
        }
    }
    

答案 1 :(得分:2)

我已使用以下代码解决了问题:

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::angleAxis(RadiansToDegrees(M_PI), glm::vec3(0, 1, 0));
        return;
    }
    else if (fabs(dot - (1.0f)) < 0.000001f) {
        rotation = glm::quat();
        return;
    }

    float angle = -RadiansToDegrees(acosf(dot));

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

但我不理解angle上否定的必要性。它解决了我的最后一个问题,并解释了为什么会有用的数学。