5.7 KiB
Quaternion interpolation
Goal of this exercise is to practice implement the interpolation of rotations using quaternions.
Description of main project file
After attaching main10_2
to the project it renders a futuristic city with flying cars. The flying cars start from at a skyscraper and fly around in the city. If drawing is too demanding for your computer, then in the function initModels
replace city model with a less complex one, lowering multisampling values should also help (ine glutSetOption(GLUT_MULTISAMPLE, 4);
). The Assimp library with simple material implementation is used for fbx object loading.
Cars move on a curve created by keyPoints
interpolation with the Catmull-Rom spline. They don't have changes of orientations defined, your task is to compute their rotations in time.
For easier debugging following features were implemented for camera:
- quaternion rotation
- buttons q and e move camera to next/previous control point
- button 0 moves to the first control point
- button 1 pins camera to the car.
Most work will be done in the function animationMatrix
. It contains a variable speed
, that can be useful for debugging.
Direction computation
First we need to calculate quaternions that are responsible for the ship rotation between consecutive control points. We want the ship to be rotated in the direction of the next control point. For this we need to acquire the normalized direction vector for every two sequential control points as in the picture bellow.
Next you have to compute the quaternion that describes the starting rotation (it is (0,0,1)) and defined by a direction vector. To calculate the rotation between one vector and another one, calculate the axis of rotation, using the cross product, and angle of rotation, using the dot product. It might be tempting to calculate rotations between starting direction and all the direction vectors, but this can lead to undesired behavior like spinning. Thus it is better to calculate rotation between consecutive vectors and acumulate those rotations.
Task
Fill in the missing code in function initKeyRoation
so that it will provide std::vector<glm::quat> keyRotation
with the correct quaternions.
- Initialize variable
glm::vec3 oldDirection
with vector (0,0,1), which is the initial direction. - Initialize variable
glm::quat oldRotationCamera
with identity rotation (1,0,0,0). - iterate from 0 to keyPoints-1 with i as index:
- Calculate new direction: subtract starting point:
keyPoints[i]
from ending point:keyPoints[i+1]
. - Calculate new rotation, use function
glm::rotationCamera
multiply results witholdRotationCamera
from right and normalize. - add this new rotation to
keyRotation
vector. - Overwrite
oldRotationCamera
with new rotation andoldDritection
with new direction.
- Calculate new direction: subtract starting point:
- After the loop there is one less quaternion than control points add one more with values (1,0,0,0).
Interpolation
For interpolation we will use the function slerp
described by equation
slerp(\hat{q_i},\hat{q}_{i+1},t) = \frac{\sin(\phi(1-t))}{\sin(\phi)}\hat{q_i}+\frac{\sin(\phi t)}{\sin(\phi)}\hat{q}_{i+1}
where \hat{q_i},\hat{q}_{i+1}
are interpolated quaternions, t
is parameter from 0 to 1 and \phi
can be acquired from equation:
cos\phi = q_xr_x+q_yr_y+q_zr_z+q_wr_w
. dla t\in\left[0,1\right]
slerp
calculates closest path from quaternions p and q in space of unitary quaternions.
You don't have to calculate it manually, instead use function glm::slerp
.
Task
In function animationMatrix
add quaternion interpolation.
glm::mat4 animationMatrix(float time) {
...
//index of first keyPoint
int index = 0;
while (distances[index] <= time) {
time = time - distances[index];
index += 1;
}
//t coefitient between 0 and 1 for interpolation
float t = time / distances[index];
...
//implement corect animation
auto animationRotation = glm::quat(1,0,0,0);
glm::mat4 result = glm::translate(pos) * glm::mat4_cast(animationRotation);
return result;
}
Acquire from keyRotation
quaternions of index equal to index
and index+1
call glm::slerp
with them as arguments. Third parameter is supposed to be value t
.
While acquiring values from
keyRotation
remember not to get outside of its scope. One idea is to clamp with 0 and size of vector.
The resulting animation would be continuous but not smooth at control points. It happens because the function is not smooth with t
equal to 0 and 1. To mitigate this effect we will use function glm::squat
.
Squat function similarly like Catmull-Rom takes 4 values but instead taking four consecutive vectors, it takes two consecutive quaternions and two intermediate quaternions. It is described by the equation:
squad(\hat{q}_{i},\hat{q}_{i+1},\hat{a}_{i},\hat{a}_{i+1},t)=slerp(slerp(\hat{q}_{i},\hat{q}_{i+1},t),slerp(\hat{a}_{i},\hat{a}_{i+1},t),2t(1-t))
where \hat{q}_{i},\hat{q}_{i+1}
are interpolated quaternions, t
is parameter from 0 to 1 and \hat{a}_{i},\hat{a}_{i+1}
are described by equation:
\hat{a}_i = \hat{q}_i\exp\left[-\frac{\log(\hat{q}_i^{-1}\hat{q}_{i-1})+\log(\hat{q}_i^{-1}\hat{q}_{i+1})}{4}\right].
Task
Change slerp
interpolation to squat
interpolation. First acquire \hat{q}_{i-1}
, \hat{q}_{i}
, \hat{q}_{i+2}
, \hat{q}_{i+2}
from keyRotation
(i
refers to index
as before), calculate \hat{a}_i
, \hat{a}_{i+1}
, functions glm::inverse
, glm::exp
, glm::log
will be useful. finally replace slerp
to squat
.
Now transitions between rotations should be smooth. One remaining problem is to fix landing of the flying cars. Set manually the last few quaternions in such a way that the ship appears to land horizontally.