3-axis quaternion rotation in OpenGL

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:

enter image description hereenter image description here

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); 
+5
source share
2 answers

Your code uses Euler angles (along the rotation axis). Waves and jumps are associated with the fact that Euler angles are a poor parameterization of the space of three-dimensional rotations. Instead, there are two alternative approaches.

Building a rotation matrix using a frame

Assuming that the bird points down on the X axis and up in it its own local coordinate system.

Let p = [xyz] be the position of the bird. Let v be its velocity vector. Let be

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

Now build a matrix with the rows f, up, s. In particular:

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

Then create a quaternion through the GLM quat_cast function.

Avoid gluLookAt because it uses an obsolete fixed function matrix stack.

Creation through rotations (quaternions)

Let R0 be the rotation from i to f . (The angle is acos(dot(i,f)) and the axis is cross(i,f) )

Let R1 be the rotation from R0*j to up . (Using the notation of matrix multiplication, since in this context it is easier)

Let R2 be the rotation from R1*R0*k to s .

Final rotation should be R2*R1*R0 . Make sure this rotation is equal to the matrix above.

+3
source

I do not have ready code for you, but what about this idea? Assuming you already have a formula for the location of the bird x, y, z versus t (Seiffert Spherical Spiral). Then:

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

Now gluLookAt (eye, center, up) will provide the matrix, and you can use it to orient your bird.

This link may also help: https://gamedev.stackexchange.com/questions/41940/how-does-glulookat-work .

Hope this helps,

- Roger

+3
source

Source: https://habr.com/ru/post/1210072/


All Articles