推导过程
设 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);
}
}