97 lines
5.7 KiB
Markdown
97 lines
5.7 KiB
Markdown
# 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.
|
|
|
|
![wektory](vectors.jpg)
|
|
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.
|
|
|
|
1. Initialize variable `glm::vec3 oldDirection` with vector (0,0,1), which is the initial direction.
|
|
2. Initialize variable `glm::quat oldRotationCamera` with identity rotation (1,0,0,0).
|
|
3. iterate from 0 to keyPoints-1 with i as index:
|
|
1. Calculate new direction: subtract starting point: `keyPoints[i]` from ending point:
|
|
`keyPoints[i+1]`.
|
|
2. Calculate new rotation, use function `glm::rotationCamera` multiply results with `oldRotationCamera` from right and normalize.
|
|
3. add this new rotation to `keyRotation` vector.
|
|
4. Overwrite `oldRotationCamera` with new rotation and `oldDritection` with new direction.
|
|
4. 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.
|
|
```C++
|
|
|
|
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. |