推导过程
设
v
v
v是三维空间中任意向量,求
v
v
v绕
n
n
n顺时针旋转
θ
\theta
θ所得到的向量
v
′
v'
v′,其中
n
n
n是单位向量,
n
=
[
n
x
n
y
n
z
]
n=\left[ \begin{matrix} n_x\\n_y\\n_z \end{matrix} \right]
n=⎣⎡nxnynz⎦⎤,
n
x
2
+
n
y
2
+
n
z
2
=
1
n_x^2+n_y^2+n_z^2=1
nx2+ny2+nz2=1。
首先把
v
v
v分解为垂直、平行于
n
n
n的两个分量:
v
⊥
=
v
−
(
v
T
n
)
n
v_{⊥}=v-(v^Tn)n
v⊥=v−(vTn)n,
v
∣
∣
=
(
v
T
n
)
n
v_{||}=(v^Tn)n
v∣∣=(vTn)n。
不妨设
w
=
n
×
v
⊥
w=n\times v_{⊥}
w=n×v⊥,那么由叉积的定义可知,
w
w
w与
n
、
v
⊥
n、v_{⊥}
n、v⊥垂直,方向服从右手法则,由于
n
n
n是单位向量,
n
n
n和
v
⊥
v_{⊥}
v⊥也互相垂直,所以
∣
w
∣
=
∣
v
⊥
∣
|w|=|v_{⊥}|
∣w∣=∣v⊥∣。设
v
⊥
′
v_{⊥}'
v⊥′为
v
⊥
v_{⊥}
v⊥绕
n
n
n旋转
θ
\theta
θ得到的向量,那么
v
⊥
′
=
v
⊥
c
o
s
θ
+
w
s
i
n
θ
=
[
v
−
(
v
T
n
)
n
]
c
o
s
θ
+
(
n
×
v
)
s
i
n
θ
v_{⊥}'=v_{⊥}cos\theta+wsin\theta=[v-(v^Tn)n]cos\theta+(n\times v)sin\theta
v⊥′=v⊥cosθ+wsinθ=[v−(vTn)n]cosθ+(n×v)sinθ。
由于平行分量旋转后没有变化,所以:
v
′
=
v
⊥
′
+
v
∣
∣
=
[
v
−
(
v
T
n
)
n
]
c
o
s
θ
+
(
n
×
v
)
s
i
n
θ
+
(
v
T
n
)
n
v'=v_{⊥}'+v_{||}=[v-(v^Tn)n]cos\theta+(n\times v)sin\theta+(v^Tn)n
v′=v⊥′+v∣∣=[v−(vTn)n]cosθ+(n×v)sinθ+(vTn)n
令
v
=
[
1
0
0
]
v=\left[ \begin{matrix} 1\\0\\0\end{matrix} \right]
v=⎣⎡100⎦⎤,带入上式可得:
v
′
=
(
[
1
0
0
]
−
(
[
1
0
0
]
∗
[
n
x
n
y
n
z
]
)
[
n
x
n
y
n
z
]
)
c
o
s
θ
+
(
[
n
x
n
y
n
z
]
×
[
1
0
0
]
)
s
i
n
θ
+
(
[
1
0
0
]
∗
[
n
x
n
y
n
z
]
)
[
n
x
n
y
n
z
]
=
(
[
1
0
0
]
−
n
x
[
n
x
n
y
n
z
]
)
c
o
s
θ
+
[
0
n
z
−
n
y
]
s
i
n
θ
+
n
x
[
n
x
n
y
n
z
]
=
[
1
−
n
x
2
−
n
x
n
y
−
n
x
n
z
]
c
o
s
θ
+
[
0
n
z
−
n
y
]
s
i
n
θ
+
[
n
x
2
n
x
n
y
n
x
n
z
]
=
[
n
x
2
(
1
−
c
o
s
θ
)
+
c
o
s
θ
n
x
n
y
(
1
−
c
o
s
θ
)
+
n
z
s
i
n
θ
n
x
n
z
(
1
−
c
o
s
θ
)
−
n
y
s
i
n
θ
]
\begin{aligned} v' &=\left( \left[ \begin{matrix} 1\\0\\0\end{matrix} \right] - \left( \left[ \begin{matrix} 1&0&0\end{matrix} \right] * \left[ \begin{matrix} n_x\\n_y\\n_z \end{matrix} \right] \right) \left[ \begin{matrix} n_x\\n_y\\n_z \end{matrix} \right] \right)cos\theta+\left( \left[ \begin{matrix} n_x\\n_y\\n_z \end{matrix} \right] \times \left[ \begin{matrix} 1\\0\\0 \end{matrix} \right] \right)sin\theta + \left( \left[ \begin{matrix} 1&0&0\end{matrix} \right] * \left[ \begin{matrix} n_x\\n_y\\n_z \end{matrix} \right] \right)\left[ \begin{matrix} n_x\\n_y\\n_z \end{matrix} \right] \\&= \left( \left[ \begin{matrix} 1\\0\\0\end{matrix} \right] - n_x\left[ \begin{matrix} n_x\\n_y\\n_z \end{matrix} \right] \right)cos\theta + \left[ \begin{matrix} 0\\n_z\\-n_y \end{matrix} \right]sin\theta \ + \ n_x\left[ \begin{matrix} n_x\\n_y\\n_z \end{matrix} \right] \\ &= \left[ \begin{matrix} 1-n_x^2\\-n_xn_y\\-n_xn_z \end{matrix} \right]cos\theta + \left[ \begin{matrix} 0\\n_z\\-n_y \end{matrix} \right]sin\theta + \left[ \begin{matrix} n_x^2\\n_xn_y\\n_xn_z \end{matrix} \right] \\&= \left[ \begin{matrix} n_x^2(1-cos\theta)+cos\theta \\n_xn_y(1-cos\theta)+n_zsin\theta \\n_xn_z(1-cos\theta)-n_ysin\theta \end{matrix} \right] \\ \end{aligned}
v′=⎝⎛⎣⎡100⎦⎤−⎝⎛[100]∗⎣⎡nxnynz⎦⎤⎠⎞⎣⎡nxnynz⎦⎤⎠⎞cosθ+⎝⎛⎣⎡nxnynz⎦⎤×⎣⎡100⎦⎤⎠⎞sinθ+⎝⎛[100]∗⎣⎡nxnynz⎦⎤⎠⎞⎣⎡nxnynz⎦⎤=⎝⎛⎣⎡100⎦⎤−nx⎣⎡nxnynz⎦⎤⎠⎞cosθ+⎣⎡0nz−ny⎦⎤sinθ + nx⎣⎡nxnynz⎦⎤=⎣⎡1−nx2−nxny−nxnz⎦⎤cosθ+⎣⎡0nz−ny⎦⎤sinθ+⎣⎡nx2nxnynxnz⎦⎤=⎣⎡nx2(1−cosθ)+cosθnxny(1−cosθ)+nzsinθnxnz(1−cosθ)−nysinθ⎦⎤
令
v
=
[
0
1
0
]
v=\left[ \begin{matrix} 0\\1\\0\end{matrix} \right]
v=⎣⎡010⎦⎤,带入上式可得(化简过程和上面差不多,就直接省略了):
v
′
=
[
n
x
n
y
(
1
−
c
o
s
θ
)
−
n
z
s
i
n
θ
n
y
2
(
1
−
c
o
s
θ
)
+
c
o
s
θ
n
y
n
z
(
1
−
c
o
s
θ
)
+
n
x
s
i
n
θ
]
\begin{aligned} v' &=\left[ \begin{matrix} n_xn_y(1-cos\theta)-n_zsin\theta \\n_y^2(1-cos\theta)+cos\theta \\n_yn_z(1-cos\theta)+n_xsin\theta \end{matrix} \right] \\ \end{aligned}
v′=⎣⎡nxny(1−cosθ)−nzsinθny2(1−cosθ)+cosθnynz(1−cosθ)+nxsinθ⎦⎤
令
v
=
[
0
0
1
]
v=\left[ \begin{matrix} 0\\0\\1\end{matrix} \right]
v=⎣⎡001⎦⎤,带入上式可得:
v
′
=
[
n
x
n
z
(
1
−
c
o
s
θ
)
+
n
y
s
i
n
θ
n
y
n
z
(
1
−
c
o
s
θ
)
−
n
x
s
i
n
θ
n
z
2
(
1
−
c
o
s
θ
)
+
c
o
s
θ
]
\begin{aligned} v' &=\left[ \begin{matrix} n_xn_z(1-cos\theta)+n_ysin\theta \\n_yn_z(1-cos\theta)-n_xsin\theta \\n_z^2(1-cos\theta)+cos\theta \end{matrix} \right] \\ \end{aligned}
v′=⎣⎡nxnz(1−cosθ)+nysinθnynz(1−cosθ)−nxsinθnz2(1−cosθ)+cosθ⎦⎤
将上述三个结果结合起来,可以得到一个
3
∗
3
3*3
3∗3的矩阵:
M
=
[
n
x
2
(
1
−
c
o
s
θ
)
+
c
o
s
θ
n
x
n
y
(
1
−
c
o
s
θ
)
−
n
z
s
i
n
θ
n
x
n
z
(
1
−
c
o
s
θ
)
+
n
y
s
i
n
θ
n
x
n
y
(
1
−
c
o
s
θ
)
+
n
z
s
i
n
θ
n
y
2
(
1
−
c
o
s
θ
)
+
c
o
s
θ
n
y
n
z
(
1
−
c
o
s
θ
)
−
n
x
s
i
n
θ
n
x
n
z
(
1
−
c
o
s
θ
)
−
n
y
s
i
n
θ
n
y
n
z
(
1
−
c
o
s
θ
)
+
n
x
s
i
n
θ
n
z
2
(
1
−
c
o
s
θ
)
+
c
o
s
θ
]
\begin{aligned} M &=\left[ \begin{matrix} n_x^2(1-cos\theta)+cos\theta & n_xn_y(1-cos\theta)-n_zsin\theta & n_xn_z(1-cos\theta)+n_ysin\theta \\ \\n_xn_y(1-cos\theta)+n_zsin\theta &n_y^2(1-cos\theta)+cos\theta &n_yn_z(1-cos\theta)-n_xsin\theta \\ \\n_xn_z(1-cos\theta)-n_ysin\theta &n_yn_z(1-cos\theta)+n_xsin\theta &n_z^2(1-cos\theta)+cos\theta \end{matrix} \right] \\ \end{aligned}
M=⎣⎢⎢⎢⎢⎡nx2(1−cosθ)+cosθnxny(1−cosθ)+nzsinθnxnz(1−cosθ)−nysinθnxny(1−cosθ)−nzsinθny2(1−cosθ)+cosθnynz(1−cosθ)+nxsinθnxnz(1−cosθ)+nysinθnynz(1−cosθ)−nxsinθnz2(1−cosθ)+cosθ⎦⎥⎥⎥⎥⎤
演示过程
为了有较为直观的演示效果,我决定使用
u
n
i
t
y
unity
unity进行演示。思路比较简单,随便选取一个向量作为旋转轴,然后在这个向量的起始和终点位置绘制两个球体,再使用第三个球体绕着这根轴旋转。同时在
s
c
e
n
e
scene
scene场景下绘制出这三个球体之间的连线,方便我们观看旋转效果。
抛开绘制连线的部分,其实在代码中只需要修改运动球体的坐标即可。具体做法就是把球体的初始坐标或者说当前坐标作为向量,然后与这个矩阵相乘(相当于做变换)得到新的坐标,再把这个坐标当作球体的新坐标即可。
那么这里就涉及到一个问题,坐标是行向量呢还是列向量呢,如果是前者那么需要左乘矩阵,否则需要右乘,我自己测试的结果是:左乘相当于顺时针旋转,右乘相当于逆时针旋转。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class move : MonoBehaviour {
// Use this for initialization
static Vector3 col1, col2, col3;
void Start () {
float angle = Mathf.PI/12;
float c = Mathf.Cos(angle);
float s = Mathf.Sin(angle);
Vector3 A = new Vector3(1, 3, 4);
A.Normalize();
col1 = new Vector3(
c + (1 - c) * A.x * A.x,
(1 - c) * A.x * A.y + s * A.z,
(1 - c) * A.x * A.z - s * A.y
);
col2 = new Vector3(
(1 - c) * A.x * A.y - s * A.z,
c + (1 - c) * A.y * A.y,
(1 - c) * A.y * A.z + s * A.x
);
col3 = new Vector3(
(1 - c) * A.x * A.z + s * A.y,
(1 - c) * A.y * A.z - s * A.x,
c + (1 - c) * A.z * A.z
);
Debug.Log(A.ToString("f4"));
Debug.Log(col1.ToString("f4"));
Debug.Log(col2.ToString("f4"));
Debug.Log(col3.ToString("f4"));
}
// Update is called once per frame
void FixedUpdate () {
Vector3 pos = transform.position;
//左乘
//float x = pos.x * col1.x + pos.y * col1.y + pos.z * col1.z;
//float y = pos.x * col2.x + pos.y * col2.y + pos.z * col2.z;
//float z = pos.x * col3.x + pos.y * col3.y + pos.z * col3.z;
//右乘
float x = col1.x * pos.x + col2.x * pos.y + col3.x * pos.z;
float y = col1.y * pos.x + col2.y * pos.y + col3.y * pos.z;
float z = col1.z * pos.x + col2.z * pos.y + col3.z * pos.z;
transform.position = new Vector3(x, y, z);
}
public void OnDrawGizmos()
{
Vector3 start = new Vector3(0, 0, 0);
Vector3 end = new Vector3(1, 3, 4);
Gizmos.DrawLine(start, end);
Gizmos.DrawLine(transform.position, start);
Gizmos.DrawLine(transform.position, end);
}
}