三维旋转矩阵推导及演示

推导过程

  设 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_{⊥} nv垂直,方向服从右手法则,由于 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=vcosθ+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]nxnynznxnynzcosθ+nxnynz×100sinθ+[100]nxnynznxnynz=100nxnxnynzcosθ+0nznysinθ + nxnxnynz=1nx2nxnynxnzcosθ+0nznysinθ+nx2nxnynxnz=nx2(1cosθ)+cosθnxny(1cosθ)+nzsinθnxnz(1cosθ)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(1cosθ)nzsinθny2(1cosθ)+cosθnynz(1cosθ)+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(1cosθ)+nysinθnynz(1cosθ)nxsinθnz2(1cosθ)+cosθ
  将上述三个结果结合起来,可以得到一个 3 ∗ 3 3*3 33的矩阵:
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(1cosθ)+cosθnxny(1cosθ)+nzsinθnxnz(1cosθ)nysinθnxny(1cosθ)nzsinθny2(1cosθ)+cosθnynz(1cosθ)+nxsinθnxnz(1cosθ)+nysinθnynz(1cosθ)nxsinθnz2(1cosθ)+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);
	}
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/xiji333/article/details/112745224