首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >OpenGL中的三轴四元数旋转

OpenGL中的三轴四元数旋转
EN

Stack Overflow用户
提问于 2014-12-29 21:22:36
回答 2查看 1.2K关注 0票数 5

我试图创建一个OpenGL程序,在这个程序中,鸟类的模型应该沿着由塞弗特的球形螺旋描述的球体表面的一条确定的路径。然而,我已经坚持了很长一段时间正确的旋转。

作为第一步,我让这只鸟在x-z平面上沿着一条圆形的路径走:

代码语言:javascript
复制
// 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平面上实现一个圆形旋转。

当我试图累积所有不同的旋转时,问题就会发生。我试图走的路是这样的:

作为一种要求,鸟的肚子应该总是面对球的表面,而鸟应该朝前飞。

我目前的方法如下所示,它只包含三个定向四元数:

代码语言:javascript
复制
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,圆圈再次位于x-z平面上,rotationAngleXZ的值正在变化,而rotationAngleXYHALF_PI-HALF_PIrotationAngleYZ0.0fPI。我想我在这里遇到了一个Gimbal Lock,我读过很多关于它的文章,但是我仍然不知道如何在这种情况下阻止它。

代码语言:javascript
复制
// 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);
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-01-09 23:28:49

您的代码使用欧拉角(轴对齐旋转)。摇摆和跳跃是因为欧拉角是一个糟糕的参数化的空间的三维旋转。相反,这里有两种替代方法。

通过帧构造旋转矩阵

假设鸟指向x轴,在它自己的地方坐标系中向上。

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

代码语言:javascript
复制
f = v/|v|
up = p/|p|
s = cross(f, up)

现在用行f,up,s构造矩阵,具体来说:

代码语言:javascript
复制
[  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。检查此旋转是否等于上面的矩阵。

票数 3
EN

Stack Overflow用户

发布于 2014-12-30 20:37:35

我没有准备好的代码给你,但是这个想法怎么样?假设你已经有了一个关于鸟的x,y,z位置的公式,作为t的函数(塞弗特的球形螺旋)。然后:

代码语言:javascript
复制
eye    = fn(t)
center = fn(t + dt) // where will the bird be in the next time-step
up     = normalize(eye - sphereCenter)

现在,gluLookAt(眼睛,中心,向上)将提供一个矩阵,你应该能够用它来定位你的鸟。

这个引用也可能有帮助:https://gamedev.stackexchange.com/questions/41940/how-does-glulookat-work

希望这能帮上忙

-罗杰

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/27696205

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档