# Quaternions 筆記
2024/11/02
## 四元數特性與定義
[來源]( https://hackmd.io/@shengwen/rotation-matrix)
- 四元數由一實數與三虛數組成 : $q=w+xi+yj+zk$
- 四元數虛數之間相互乘法不具有交換性:
- $i^2=j^2=k^2=-1$
- $ij=k,\ ji=-k$
- $jk=i,\ kj=-i$
- $ki=j,\ ik=-j$
- 四元數逆運算等於共軛值
- $q* = w -xi -yi-zk$
- $q^{-1} = q^* \space if|q|=1$
- 四元數乘法並不具備交換性,改變順序將會導致結果改變。
### 補充 酉矩陣(unitary matrix): 逆矩陣=共軛矩陣
- 對於單位矩陣(即模長為1的矩陣),其逆矩陣等於其共軛轉置矩陣。這類矩陣稱為酉矩陣(unitary matrix)
- 酉矩陣是可對角化的,即 $U=VDV$,其中V是酉矩陣、D是對角矩陣。
-
酉矩陣和正交矩陣之間有一些重要的區別和相似之處:
1. **定義**:
- [**酉矩陣**(Unitary Matrix):是一個複數矩陣,其列(或行)構成複數空間 ( $\mathbb{C}^n$ ) 的正交基。酉矩陣 ( U ) 滿足 ( $U^*U = UU^* = I$),其中 ( U^* ) 是 ( U ) 的共軛轉置矩陣。
- [**正交矩陣**(Orthogonal Matrix):是一個實數矩陣,其列(或行)構成實數空間 ( $\mathbb{R}^n$ ) 的正交基。正交矩陣 ( P ) 滿足 ( $P^T P = PP^T = I$ ),其中 ( $P^T$ ) 是 ( $P$ ) 的轉置矩陣。
2. **元素**:
- 酉矩陣的元素可以是複數。
- 正交矩陣的元素只能是實數。
3. **性質**:
- 酉矩陣的逆矩陣等於其共軛轉置矩陣,即 ( $U^{-1} = U^*$ )。
- 正交矩陣的逆矩陣等於其轉置矩陣,即 ( $P^{-1} = P^T$ )。
總結來說,酉矩陣可以看作是複數空間中的正交矩陣,而正交矩陣是實數空間中的特例。
### 使用範例


- 虛數部分 i,j,k為想要旋轉的軸
- 實數部分用來控制旋轉角度大小。
- 旋轉公式: $q\cdot p \cdot q^{-1}$,實際旋轉角度要/2,因為使用q旋轉時會造成位移,需要 $q^{-1}$抵銷位移,而它本身也會旋轉。
### 程式範例
[來源](https://www.songho.ca/opengl/gl_camera.html)
#### 四元數 = (s, vec3)
```cpp
inline void Quaternion::set(const Vector3& axis, float angle)
{
// use only half angle because of double multiplication, qpq*,
// q at the front and its conjugate at the back
Vector3 v = axis;
v.normalize(); // convert to unit vector
float sine = sinf(angle); // angle is radian
s = cosf(angle);
x = v.x * sine;
y = v.y * sine;
z = v.z * sine;
}
```
#### 給2個vector,得到旋轉的q
```cpp
// find quaternion for rotating from v1 to v2
inline Quaternion Quaternion::getQuaternion(const Vector3& v1, const Vector3& v2)
{
const float EPSILON = 0.001f;
const float HALF_PI = acos(-1) * 0.5f;
Vector3 u1 = v1; // convert to normal vector
Vector3 u2 = v2;
u1.normalize();
u2.normalize();
Vector3 v = u1.cross(u2); // compute rotation axis
float angle = acosf(u1.dot(u2)); // rotation angle
return Quaternion(v, angle * 0.5f); // half angle
}
```
#### 算共軛
```cpp
inline Quaternion& Quaternion::conjugate()
{
x = -x; y = -y; z = -z;
return *this;
}
inline Quaternion& Quaternion::invert()
{
const float EPSILON = 0.00001f;
float d = s*s + x*x + y*y + z*z;
if(d < EPSILON)
return *this; // do nothing if it is zero
Quaternion q = *this;
*this = q.conjugate() * (1.0f / d); // q* / |q||q|
return *this;
}
```
#### 換算旋轉矩陣
```cpp
inline Matrix4 Quaternion::getMatrix() const
{
// NOTE: assume the quaternion is unit length
// compute common values
float x2 = x + x;
float y2 = y + y;
float z2 = z + z;
float xx2 = x * x2;
float xy2 = x * y2;
float xz2 = x * z2;
float yy2 = y * y2;
float yz2 = y * z2;
float zz2 = z * z2;
float sx2 = s * x2;
float sy2 = s * y2;
float sz2 = s * z2;
// build 4x4 matrix (column-major) and return
return Matrix4(1 - (yy2 + zz2), xy2 + sz2, xz2 - sy2, 0, // column 0
xy2 - sz2, 1 - (xx2 + zz2), yz2 + sx2, 0, // column 1
xz2 + sy2, yz2 - sx2, 1 - (xx2 + yy2), 0, // column 2
0, 0, 0, 1);// column 3
// for non-unit quaternion
// ss+xx-yy-zz, 2xy+2sz, 2xz-2sy, 0
// 2xy-2sz, ss-xx+yy-zz, 2yz-2sx, 0
// 2xz+2sy, 2yz+2sx, ss-xx-yy+zz, 0
// 0, 0, 0, 1
}
```
#### 使用範例
```cpp
glPushMatrix();
// tramsform camera (取代lookAt function)
Matrix4 mat = quat.getMatrix(); // view matrix --> Camera的視角旋轉
mat.translate(0, 0, -cameraDistance); //---->位移camera
glMultMatrixf(mat.get());
draw(....);
```
---
### 參考
1. https://hackmd.io/@shengwen/rotation-matrix
2. https://youtu.be/zjMuIxRvygQ
3. https://eater.net/quaternions/video/intro
### 其他讀物
1. https://krasjet.github.io/quaternion/quaternion.pdf from https://github.com/Krasjet/quaternion