OpenGL中的3轴四元数旋转

Sch*_*ges 5 c++ opengl geometry rotation quaternions

我正在尝试创建一个OpenGL程序,其中鸟的模型应该遵循Seiffert的球形螺旋所描述的球体表面的定义路径.但是,我一直坚持让旋转正确一段时间.

作为第一步,我让鸟只跟随xz平面中的圆形路径:

// 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));
Run Code Online (Sandbox Code Playgroud)

固定-HALF_PI是必要的,以便鸟正确对齐.这非常好,同样我可以在xy和yz平面上实现圆形旋转.

当我尝试累积所有不同的旋转时会出现问题.我想要遵循的路径如下所示:

在此输入图像描述 在此输入图像描述

作为要求,鸟的腹部应该始终面向球体的表面,鸟应该向前飞行.

我目前的方法看起来像这样,它只包含三个方向四元数:

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;
Run Code Online (Sandbox Code Playgroud)

然而,方向变化是完全错误的,并且在某些角度出现跳跃.

编辑:

我现在在球体上尝试不同的圆圈,需要多次旋转.对于beta = gamma = 0.0falpha = HALF_PI圆再次是在xz平面和的值rotationAngleXZ是变化的,而rotationAngleXY是任一-HALF_PIHALF_PIrotationAngleYZ是任一0.0fPI.我想我在这里遇到了一个万向锁,我已经阅读了很多关于它的文章,但是我仍然不确定在这种情况下如何防止它.

// 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);
Run Code Online (Sandbox Code Playgroud)

Tay*_*lor 3

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

通过框架构建旋转矩阵

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

p = [x y z]为鸟的位置。设 v 为其速度矢量。让

f = v/|v|
up = p/|p|
s = cross(f, up)
Run Code Online (Sandbox Code Playgroud)

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

[  f[0]   f[1]  f[2] ]
[ up[0]  up[1] up[2] ]
[  s[0]   s[1]  s[2] ]
Run Code Online (Sandbox Code Playgroud)

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

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

通过旋转(四元数)构建

让为 从到 的R0旋转。(角度为,轴为)ifacos(dot(i,f))cross(i,f)

让为 从到 的R1旋转。(使用矩阵乘法表示法,因为在这种情况下更容易)R0*jup

让为 从到 的R2旋转。R1*R0*ks

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