【问题标题】:Glm Quaternion lookat functionglm四元数lookat函数
【发布时间】:2013-08-11 13:25:40
【问题描述】:

我正在尝试编写一个使用 glm::quat 表示旋转的查看函数,基于此answer。但是,我在获得正确的角度时遇到了麻烦。这是我的查看功能:

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,因为这样做会引发断言。什么会导致我从我的查看函数中得到关于 x 的 124 欧拉角,我该如何解决?

【问题讨论】:

  • 正如GLM documentation 所说,glm::angleAxis 的轴必须标准化,但您的情况并非如此。当您尝试规范化cross 时会抛出什么样的断言?只要direction 不平行于(0, 0, -1) 应该没有问题。
  • 调试代码,我刚刚意识到 cross 是(0.0f, 0.0f, 0.0f),它解释了从 inversesrt 抛出的断言。
  • 啊.. 在这种情况下 dot == -1rotation 已经设置好了。但是,您尝试再次计算它,这是没有意义的。所以只需添加一个else 块。
  • 现在轮换大约是(180, 0, 0)。这个还是不看(0,0,0)
  • 这就是你在第一种情况下定义的。您可能想再次使用 angleAxis() 而不是纯构造函数。

标签: c++ opengl quaternions glm-math


【解决方案1】:

从 0.9.9.0 版开始,&lt;glm/gtc/quaternion.hpp&gt; 中有一个函数主要执行您想要的操作:

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日合并到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);
    }
}

【讨论】:

  • 如果方向和上向量是平行的,那么选择的上应该是什么?
  • @TomH 从字面上看,任何与向上不平行的向量。如果我认为“向上”和“方向”很少会平行,我通常只使用沿 X 或 Z 轴的向量。对于游戏角色来说,指向角色背部的向量是一个不错的选择,因为“alternativeUp”基本上是他们直视上方时头顶所指向的位置。
【解决方案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 的否定性的必要性。它解决了我的最后一个问题,并且解释了为什么会有帮助。

【讨论】:

  • 那是因为你的轴指向错误的方向。你也可以cross = glm::normalize(glm::cross(direction, glm::vec3(0, 0, 1)))。顺便说一句,glm::angleAxis() 可能已经返回了一个标准化的四元数。
猜你喜欢
  • 2012-09-08
  • 1970-01-01
  • 1970-01-01
  • 2013-03-13
  • 2014-02-04
  • 2014-03-16
  • 2016-04-21
  • 2016-08-11
  • 1970-01-01
相关资源
最近更新 更多