【问题标题】:3-axes quaternion rotation in OpenGLOpenGL中的三轴四元数旋转
【发布时间】:2014-12-29 21:22:36
【问题描述】:

我正在尝试创建一个 OpenGL 程序,其中鸟的模型应该沿着由 Seiffert 的球形螺旋描述的球体表面沿着定义的路径移动。 然而,我一直坚持让旋转正确有一段时间了。

作为第一步,我让小鸟在 x-z 平面上沿着圆形路径前进:

// 1. Circle in x-z plane
float phi =  TWO_PI * t; // t = [0..1]

float x = boundingSphereRadius * cos(phi);
float y = 0.0f;
float z = boundingSphereRadius * sin(phi);

float rotationAngle = glm::orientedAngle(glm::vec3(0.0f, 0.0f, 1.0f),    
                                         glm::normalize(glm::vec3(x, 0, z)),
                                         glm::vec3(0.0f, 1.0f, 0.0f)) - HALF_PI;
glm::fquat rotation = glm::angleAxis(rotationAngle, glm::vec3(0.0f, 1.0f, 0.0f));

固定的-HALF_PI 是必要的,这样鸟儿才能正确对齐。这工作得很好,同样我可以在 x-y 和 y-z 平面上实现圆形旋转。

当我尝试累积所有不同的旋转时,就会出现问题。我试图遵循的路径如下所示:

作为一项要求,鸟的腹部应该始终面向球体表面,并且鸟应该向前飞行。

我目前的方法是这样的,它只包含三个方向四元数的组合:

glm::fquat rotationX  = glm::angleAxis(glm::orientedAngle(glm::normalize(glm::vec3(0.0f, 0.0f, 1.0f)), glm::normalize(glm::vec3(x, 0, z)), glm::vec3(0.0f, 1.0f, 0.0f)) - HALF_PI, glm::vec3(0.0f, 1.0f, 0.0f));
glm::fquat rotationY1 = glm::angleAxis(-HALF_PI, glm::vec3(0.0f, 1.0f, 0.0f));
glm::fquat rotationY2 = glm::angleAxis(glm::orientedAngle(glm::vec3(0.0f, 1.0f, 0.0f), glm::normalize(glm::vec3(x, y, 0)), glm::vec3(0.0f, 0.0f, 1.0f)), glm::vec3(0.0f, 0.0f, 1.0f));
glm::fquat rotationY  = rotationY2 * rotationY1;
glm::fquat rotationZ  = glm::angleAxis(glm::orientedAngle(glm::vec3(0.0f, 0.0f, 1.0f), glm::normalize(glm::vec3(0, y, z)), glm::vec3(1.0f, 0.0f, 0.0f)) + HALF_PI, glm::vec3(1.0f, 0.0f, 0.0f));
glm::fquat rotation   = rotationZ * rotationY * rotationX;

但是,方向变化是完全错误的,并且在某些角度会发生跳跃。

编辑:

我现在正在球体上尝试不同的圆圈,其中需要多次旋转。对于beta = gamma = 0.0falpha = HALF_PI,圆再次位于xz 平面中,rotationAngleXZ 的值正在发生变化,而rotationAngleXY-HALF_PIHALF_PIrotationAngleYZ0.0fPI。我想我在这里遇到了 Gimbal Lock 并且我已经阅读了大量关于它的文章,但是我仍然不确定在这种情况下如何防止它。

// 10. `Arbitrary` circles on sphere surface
// http://math.stackexchange.com/questions/643130/circle-on-sphere
//
// Parameters:
//      alpha = 0...HALF_PI - For alpha = 0, the circle is just a point - For alpha = HALF_PI, the circle is a Great Circle
//      (beta, gamma) = center of circle in spherical coordinates
float phi =  TWO_PI * t;

float x = boundingSphereRadius * ( (sin(alpha) * cos(beta) * cos(gamma)) * cos(phi) + (sin(alpha) * sin(gamma)) * sin(phi) - (cos(alpha) * sin(beta) * cos(gamma)));
float y = boundingSphereRadius * ( (sin(alpha) * sin(beta)) * cos(phi) + cos(alpha) * cos(beta));
float z = boundingSphereRadius * (-(sin(alpha) * cos(beta) * sin(gamma)) * cos(phi) + (sin(alpha) * cos(gamma)) * sin(phi) + (cos(alpha) * sin(beta) * sin(gamma)));

float rotationAngleXZ = glm::orientedAngle(glm::normalize(glm::vec3(0.0f, 0.0f, 1.0f)), glm::normalize(glm::vec3(x, 0, z)), glm::vec3(0.0f, 1.0f, 0.0f));
std::cout << "Rotation Angle XZ = " << rotationAngleXZ << std::endl;
glm::fquat rotationXZ = glm::angleAxis(rotationAngleXZ - HALF_PI, glm::vec3(0.0f, 1.0f, 0.0f));

float rotationAngleXY = glm::orientedAngle(glm::vec3(0.0f, 1.0f, 0.0f), glm::normalize(glm::vec3(x, y, 0)), glm::vec3(0.0f, 0.0f, 1.0f));
std::cout << "Rotation Angle XY = " << rotationAngleXY << std::endl;
glm::fquat rotationXY_Y = glm::angleAxis(-HALF_PI, glm::vec3(0.0f, 1.0f, 0.0f));
glm::fquat rotationXY_Z = glm::angleAxis(rotationAngleXY, glm::vec3(0.0f, 0.0f, 1.0f));
glm::fquat rotationXY = rotationXY_Z * rotationXY_Y;

float rotationAngleYZ = glm::orientedAngle(glm::vec3(0.0f, 0.0f, 1.0f), glm::normalize(glm::vec3(0, y, z)), glm::vec3(1.0f, 0.0f, 0.0f));
std::cout << "Rotation Angle YZ = " << rotationAngleYZ << std::endl;
glm::fquat rotationYZ = glm::angleAxis(rotationAngleYZ + HALF_PI, glm::vec3(1.0f, 0.0f, 0.0f));

glm::fquat rotation = glm::normalize(rotationXZ) * glm::normalize(rotationXY) * glm::normalize(rotationYZ);

【问题讨论】:

    标签: c++ opengl rotation geometry quaternions


    【解决方案1】:

    您的代码使用欧拉角(轴对齐旋转)。摆动和跳跃是因为欧拉角是 3D 旋转空间的不良参数化。相反,这里有两种替代方法。

    通过框架构造旋转矩阵

    假设这只鸟在它自己的局部坐标系中指向 x 轴下方和上方。

    p = [x y z] 成为小鸟的位置。令 v 为其速度向量。让

    f = v/|v|
    up = p/|p|
    s = cross(f, up)
    

    现在构造具有 f、up、s 行的矩阵。具体来说:

    [  f[0]   f[1]  f[2] ]
    [ up[0]  up[1] up[2] ]
    [  s[0]   s[1]  s[2] ]
    

    然后通过 GLM 的quat_cast 函数生成一个四元数。

    避免使用 gluLookAt,因为它使用已弃用的固定函数矩阵堆栈。

    通过旋转(四元数)构建

    R0 是从if 的轮换。 (角度为acos(dot(i,f)),轴为cross(i,f)

    R1 是从R0*jup 的轮换。 (使用矩阵乘法符号,因为在这种情况下更容易)

    R2 是从R1*R0*ks 的轮换。

    最后的轮换应该是R2*R1*R0。检查这个旋转是否等于上面的矩阵。

    【讨论】:

    • 感谢您的回答泰勒!我想我正在寻找一种不同的解决方案,我可以在其中组合三个轴的各个四元数 - 我有一个用于 x、y 和 z 轴的方向四元数,如我发布的代码中所述.据我了解,可以将三个四元数相乘以获得组合方向,这是我的鸟在每一帧中的最终方向。
    • 我已经尝试了很多不同的方法来组合四元数但没有成功,我想给可以解释如何实现这一点的人(或者为什么它不可能在我正在尝试这样做的方式。)
    • 没问题!我可以理解为什么你最终想要一个四元数来表示方向,但是为什么要通过单个四元数来建立方向呢?
    【解决方案2】:

    我没有现成的代码给你,但是这个想法怎么样?假设您已经有了作为 t 函数的鸟的 x、y、z 位置的公式(Seiffert 的球形螺旋)。那么:

    eye    = fn(t)
    center = fn(t + dt) // where will the bird be in the next time-step
    up     = normalize(eye - sphereCenter)
    

    现在,gluLookAt(eye, center, up) 将提供一个矩阵,您应该可以使用它来定位您的鸟。

    此参考可能也有帮助:https://gamedev.stackexchange.com/questions/41940/how-does-glulookat-work

    希望这会有所帮助,

    --罗杰

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-17
      • 2011-05-25
      • 2011-04-10
      • 1970-01-01
      相关资源
      最近更新 更多