I am trying to create an OpenGL program where it is assumed that the bird model must follow a certain path along the surface of the sphere described by the Seifert spherical spiral. However, I have long been stuck in the corners.
As a first step, I make the bird simply follow a circular path in the xz plane:
// 1. Circle in xz 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));
Fixed -HALF_PI required so that the bird is properly aligned. This works fine, and in a similar way, I could achieve circular rotation in the xy- and yz planes.
The problem occurs when I try to copy all the different rotations. The path I'm trying to execute is as follows:


As a requirement, the birdโs belly should always collide with the surface of the sphere, and the bird should fly in the forward direction.
My current approach is that it consists only of combining three orientation quaternions:
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;
However, the orientation changes are completely wrong and jumps occur at certain angles.
EDIT:
I try different circles on a sphere where more than one turn is required. For beta = gamma = 0.0f and alpha = HALF_PI circle is again in the xz plane, and the rotationAngleXZ value changes, and rotationAngleXY is either -HALF_PI HALF_PI , and rotationAngleYZ is either 0.0f or PI . I guess I came across Gimbal Lock here and I read a lot of articles about it, however I'm still not sure how I can prevent this from happening.
// 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);