我正在尝试创建一个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.0f
和alpha = HALF_PI
,圆圈再次位于xz平面中,rotationAngleXZ
的值正在变化,而rotationAngleXY
是-HALF_PI
HALF_PI
{}}和rotationAngleYZ
是0.0f
或PI
。我想我在这里遇到了一个万向节锁,我读了很多关于它的文章,但是我仍然不确定在这种情况下如何防止它。
// 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);
答案 0 :(得分:3)
我没有准备好代码,但这个想法怎么样?假设你已经有了鸟的x,y,z位置的公式作为t(Seiffert的球形螺旋)的函数。然后:
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。
希望这有帮助,
- 罗杰
答案 1 :(得分:3)
您的代码使用的是欧拉角(轴对齐旋转)。摆动和跳跃是因为欧拉角是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
成为从i
到f
的轮换。 (角度为acos(dot(i,f))
,轴为cross(i,f)
)
让R1
成为从R0*j
到up
的轮换。 (使用矩阵乘法表示法,因为在这种情况下它更容易)
让R2
成为从R1*R0*k
到s
的轮换。
最终轮播应为R2*R1*R0
。检查此旋转是否等于上面的矩阵。