您的位置:首页 > 汽车 > 新车 > app制作平台下载_湖南长沙房价_廊坊关键词排名首页_网站优化推广价格

app制作平台下载_湖南长沙房价_廊坊关键词排名首页_网站优化推广价格

2024/12/24 3:46:22 来源:https://blog.csdn.net/weixin_45776473/article/details/144487678  浏览:    关键词:app制作平台下载_湖南长沙房价_廊坊关键词排名首页_网站优化推广价格
app制作平台下载_湖南长沙房价_廊坊关键词排名首页_网站优化推广价格

UE5的TRS矩阵

  • matrix
  • translate
  • rotate
  • scale
  • TRS顺序
  • 绕任意轴旋转
  • Reference

我们知道,常用的transform主要有三种,分别是translate,rotate,scale。接下来我们逐一看下在UE5中,如何分别使用TRS构造相应的矩阵,以及如何从矩阵中提取TRS。

matrix

UE中的矩阵是行主序,x,y,z基向量会以行向量的形式存储在矩阵中。矩阵的数据结构定义很简单,就是一个4x4的template二维数组:

/*** 4x4 matrix of floating point values.* Matrix-matrix multiplication happens with a pre-multiple of the transpose --* in other words, Res = Mat1.operator*(Mat2) means Res = Mat2^FArg * Mat1, as* opposed to Res = Mat1 * Mat2.* Matrix elements are accessed with M[RowIndex][ColumnIndex].*/template<typename T>
struct alignas(16) TMatrix
{static_assert(std::is_floating_point_v<T>, "T must be floating point");public:using FReal = T;alignas(16) T M[4][4];
};

translate

UE提供了TTranslationMatrixstruct来辅助构造平移矩阵。

template<typename T>
FORCEINLINE TTranslationMatrix<T>::TTranslationMatrix(const TVector<T>& Delta): TMatrix<T>(TPlane<T>(1.0f,		0.0f,	0.0f,	0.0f),TPlane<T>(0.0f,		1.0f,	0.0f,	0.0f),TPlane<T>(0.0f,		0.0f,	1.0f,	0.0f),TPlane<T>(Delta.X,	Delta.Y,Delta.Z,1.0f))
{ }

这里,TPlane可以理解为一个简单的4维向量,可以看到平移向量Delta位于第4行,那么需要行向量右乘该矩阵,才能达到平移的结果。
( p x , p y , p z , 1 ) ( 1 0 0 0 0 1 0 0 0 0 1 0 t x t y t z 1 ) = ( p x + t x , p y + t y , p z + t z , 1 ) (p_x, p_y, p_z, 1) \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ t_x & t_y & t_z & 1 \end{pmatrix} = (p_x + t_x, p_y + t_y, p_z + t_z, 1) (px,py,pz,1) 100tx010ty001tz0001 =(px+tx,py+ty,pz+tz,1)
那么相应地,从矩阵中提取平移向量也很简单,返回矩阵的最后一行即可。

rotate

除四元数外,UE中还有一个用来专门表示旋转的数据结构,TRotator。它和Unity的欧拉角概念类似,有3个分量,pitch,yaw,roll,分别表示绕这3个轴各自的旋转角度。UE在代码中详细解释了TRotator的定义:

/*** Implements a container for rotation information.** All rotation values are stored in degrees.** The angles are interpreted as intrinsic rotations applied in the order Yaw, then Pitch, then Roll. I.e., an object would be rotated* first by the specified yaw around its up axis (with positive angles interpreted as clockwise when viewed from above, along -Z), * then pitched around its (new) right axis (with positive angles interpreted as 'nose up', i.e. clockwise when viewed along +Y), * and then finally rolled around its (final) forward axis (with positive angles interpreted as clockwise rotations when viewed along +X).** Note that these conventions differ from quaternion axis/angle. UE Quat always considers a positive angle to be a left-handed rotation, * whereas Rotator treats yaw as left-handed but pitch and roll as right-handed.* */
template<typename T>
struct TRotator
{public:using FReal = T;/** Rotation around the right axis (around Y axis), Looking up and down (0=Straight Ahead, +Up, -Down) */T Pitch;/** Rotation around the up axis (around Z axis), Turning around (0=Forward, +Right, -Left)*/T Yaw;/** Rotation around the forward axis (around X axis), Tilting your head, (0=Straight, +Clockwise, -CCW) */T Roll;
};

首先是pitch,yaw,roll这三个值的含义以及对应旋转的方向。由注释可知,pitch对应绕UE的y轴旋转,yaw对应绕UE的z轴旋转,roll对应绕UE的x轴旋转。

接下来看方向,由注释可知,pitch的正方向表示向上看:

在这里插入图片描述

yaw的正方向表示向右转:

在这里插入图片描述

roll的正方向为顺时针,在UE中x轴通常被认为是forward向量。

在这里插入图片描述

再看旋转顺序,由注释可知,UE定义旋转顺序为先yaw,再pitch,最后roll的内旋旋转顺序。所谓内旋,就是指坐标轴会随着一起旋转,相对的就是外旋了,即绕固定轴旋转。由于内旋可以看作是坐标系一起旋转了,那么被旋转的向量,其实可以认为,在旋转前的坐标系下,和旋转后的坐标系下,坐标其实是相同的。

UE5的TRS矩阵4

内旋与外旋的对比图如下:

在这里插入图片描述

在这里插入图片描述

所以内旋本质上可以拆解为两步,第一步是把初始坐标系变换到经过yaw,pitch,roll之后的坐标系,此时被旋转的向量也跟着变换了;第二步再计算新坐标系下的被旋转的向量,在初始坐标系下的坐标。我们先看一下第一步中的Yaw矩阵,容易知道yaw变换后的坐标系,基向量的坐标分别为:
X = ( c o s θ , s i n θ , 0 ) X = (cos \theta, sin \theta, 0) X=(cosθ,sinθ,0)

Y = ( − s i n θ , c o s θ , 0 ) Y = (-sin\theta, cos\theta, 0) Y=(sinθ,cosθ,0)

Z = ( 0 , 0 , 1 ) Z = (0, 0, 1) Z=(0,0,1)

那么yaw变换其实就是把三个基向量变换为:
X ′ = ( 1 , 0 , 0 ) X' = (1, 0, 0) X=(1,0,0)

Y ′ = ( 0 , 1 , 0 ) Y' = (0, 1, 0) Y=(0,1,0)

Z ′ = ( 0 , 0 , 1 ) Z' = (0, 0, 1) Z=(0,0,1)

注意UE是行向量右乘矩阵,所以Yaw矩阵为
Y a w = ( c o s α − s i n α 0 0 s i n α c o s α 0 0 0 0 1 0 0 0 0 1 ) Yaw = \begin{pmatrix} cos\alpha & -sin\alpha & 0 & 0 \\ sin\alpha & cos\alpha & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} Yaw= cosαsinα00sinαcosα0000100001

类似地,可以得到Pitch和Roll矩阵为
P i t c h = ( c o s β 0 − s i n β 0 0 1 0 0 s i n β 0 c o s β 0 0 0 0 1 ) Pitch = \begin{pmatrix} cos\beta & 0 & -sin\beta & 0 \\ 0 & 1 & 0 & 0 \\ sin\beta & 0 & cos\beta & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} Pitch= cosβ0sinβ00100sinβ0cosβ00001

R o l l = ( 1 0 0 0 0 c o s γ s i n γ 0 0 − s i n γ c o s γ 0 0 0 0 1 ) Roll = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & cos\gamma & sin\gamma & 0 \\ 0 & -sin\gamma & cos\gamma & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} Roll= 10000cosγsinγ00sinγcosγ00001

那么第一步最后得到的坐标系变换矩阵为
M = Y a w ⋅ P i t c h ⋅ R o l l M = Yaw \cdot Pitch \cdot Roll M=YawPitchRoll
第二步,需要计算新坐标系下的向量,在初始坐标系下的坐标。依然从基向量出发,新坐标系下的的基向量,显然为
X = ( 1 , 0 , 0 ) X = (1, 0, 0) X=(1,0,0)

Y = ( 0 , 1 , 0 ) Y = (0, 1, 0) Y=(0,1,0)

Z = ( 0 , 0 , 1 ) Z = (0, 0, 1) Z=(0,0,1)

初始坐标系的基向量,在新坐标系下的坐标,其实就是M矩阵的三个行向量:
X ’ = M [ 0 ] X’ = M[0] X=M[0]

Y ′ = M [ 1 ] Y' = M[1] Y=M[1]

Z ′ = M [ 2 ] Z' = M[2] Z=M[2]

那么向量V,用新坐标系的基向量可表示为
V = ( v x , v y , v z , 0 ) ( X 0 Y 0 Z 0 0 1 ) = ( v x , v y , v z , 0 ) I V = (v_x,v_y,v_z, 0) \begin{pmatrix} X & 0\\ Y & 0\\ Z & 0 \\ \textbf{0} & 1 \end{pmatrix} = (v_x,v_y,v_z, 0)I V=(vx,vy,vz,0) XYZ00001 =(vx,vy,vz,0)I
而用初始坐标系的基向量可表示为
V = ( v x ′ , v y ′ , v z ′ , 0 ) ( X ′ 0 Y ′ 0 Z ′ 0 0 1 ) = ( v x ′ , v y ′ , v z ′ , 0 ) M V = (v'_x,v'_y,v'_z, 0) \begin{pmatrix} X' & 0\\ Y' & 0\\ Z' & 0 \\ \textbf{0} & 1 \end{pmatrix} = (v'_x,v'_y,v'_z, 0) M V=(vx,vy,vz,0) XYZ00001 =(vx,vy,vz,0)M

那么,向量V在初始坐标系下的坐标
( v x ′ , v y ′ , v z ′ , 0 ) = ( v x , v y , v z , 0 ) M − 1 = ( v x , v y , v z , 0 ) M T (v'_x,v'_y,v'_z, 0) = (v_x,v_y,v_z, 0)M^{-1} = (v_x,v_y,v_z, 0)M^T (vx,vy,vz,0)=(vx,vy,vz,0)M1=(vx,vy,vz,0)MT

所以最终的变换矩阵其实就是M矩阵的转置,根据矩阵的性质,进一步可以得到
M T = ( Y a w ⋅ P i t c h ⋅ R o l l ) T = R o l l T ⋅ P i t c h T ⋅ Y a w T M^T = (Yaw \cdot Pitch \cdot Roll)^T = Roll^T \cdot Pitch^T \cdot Yaw^T MT=(YawPitchRoll)T=RollTPitchTYawT
Roll,Pitch,Yaw的转置矩阵就是对应外旋下的旋转矩阵。这也说明了,内旋和外旋影响三个轴的旋转矩阵相乘的顺序,但不影响最终的旋转矩阵结果,先yaw,再pitch,最后roll的内旋等价于先roll,再pitch,最后yaw的外旋。最终的变换矩阵如下:
( c o s α c o s β s i n α c o s β s i n β 0 c o s α s i n β s i n γ − s i n α c o s γ s i n α s i n β s i n γ + c o s α c o s γ − c o s β s i n γ 0 − ( c o s α s i n β c o s γ + s i n α s i n γ ) c o s α s i n γ − s i n α s i n β c o s γ c o s β c o s γ 0 0 0 0 1 ) \begin{pmatrix} cos\alpha cos\beta & sin\alpha cos\beta & sin\beta & 0 \\ cos\alpha sin\beta sin\gamma - sin\alpha cos\gamma & sin\alpha sin\beta sin\gamma + cos\alpha cos\gamma & -cos\beta sin\gamma & 0 \\ -(cos\alpha sin\beta cos\gamma + sin\alpha sin\gamma) & cos\alpha sin\gamma - sin\alpha sin\beta cos\gamma & cos\beta cos\gamma & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} cosαcosβcosαsinβsinγsinαcosγ(cosαsinβcosγ+sinαsinγ)0sinαcosβsinαsinβsinγ+cosαcosγcosαsinγsinαsinβcosγ0sinβcosβsinγcosβcosγ00001

与UE代码的旋转部分完全一致:

template<typename T>
FORCEINLINE TRotationTranslationMatrix<T>::TRotationTranslationMatrix(const TRotator<T>& Rot, const TVector<T>& Origin)
{M[0][0]	= CP * CY;M[0][1]	= CP * SY;M[0][2]	= SP;M[0][3]	= 0.f;M[1][0]	= SR * SP * CY - CR * SY;M[1][1]	= SR * SP * SY + CR * CY;M[1][2]	= - SR * CP;M[1][3]	= 0.f;M[2][0]	= -( CR * SP * CY + SR * SY );M[2][1]	= CY * SR - CR * SP * SY;M[2][2]	= CR * CP;M[2][3]	= 0.f;M[3][0]	= Origin.X;M[3][1]	= Origin.Y;M[3][2]	= Origin.Z;M[3][3]	= 1.f;
}

有了这个矩阵,我们还可以从数学的角度分析一下万向锁。我们假设
β = 9 0 ∘ \beta = 90^\circ β=90
也就是绕y轴旋转90度。那么
s i n β = 1 sin\beta = 1 sinβ=1

c o s β = 0 cos\beta = 0 cosβ=0

此时变换矩阵简化为
( 0 0 1 0 c o s α s i n γ − s i n α c o s γ s i n α s i n γ + c o s α c o s γ 0 0 − ( c o s α c o s γ + s i n α s i n γ ) c o s α s i n γ − s i n α c o s γ 0 0 0 0 0 1 ) \begin{pmatrix} 0 & 0 & 1 & 0 \\ cos\alpha sin\gamma - sin\alpha cos\gamma & sin\alpha sin\gamma + cos\alpha cos\gamma & 0 & 0 \\ -(cos\alpha cos\gamma + sin\alpha sin\gamma) & cos\alpha sin\gamma - sin\alpha cos\gamma & 0 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} 0cosαsinγsinαcosγ(cosαcosγ+sinαsinγ)00sinαsinγ+cosαcosγcosαsinγsinαcosγ010000001
根据三角函数公式,有
( 0 0 1 0 s i n ( γ − α ) c o s ( γ − α ) 0 0 − c o s ( γ − α ) s i n ( γ − α ) 0 0 0 0 0 1 ) \begin{pmatrix} 0 & 0 & 1 & 0 \\ sin(\gamma - \alpha) & cos(\gamma - \alpha) & 0 & 0 \\ -cos(\gamma - \alpha) & sin(\gamma - \alpha) & 0 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} 0sin(γα)cos(γα)00cos(γα)sin(γα)010000001
可以看到,这里的yaw和roll其实已经可以被一个变量所取代,也就是失去了一个自由的轴,这就是触发万向锁了。

反过来,从矩阵提取yaw,pitch,roll也很简单,就是些纯三角函数的计算了:

template<typename T>
UE::Math::TRotator<T> UE::Math::TMatrix<T>::Rotator() const
{using TRotator = UE::Math::TRotator<T>;using TVector = UE::Math::TVector<T>;const TVector		XAxis	= GetScaledAxis( EAxis::X );const TVector		YAxis	= GetScaledAxis( EAxis::Y );const TVector		ZAxis	= GetScaledAxis( EAxis::Z );const T RadToDeg = T(180.0 / UE_DOUBLE_PI);TRotator Rotator	= TRotator(FMath::Atan2( XAxis.Z, FMath::Sqrt(FMath::Square(XAxis.X)+FMath::Square(XAxis.Y)) ) * RadToDeg,FMath::Atan2( XAxis.Y, XAxis.X ) * RadToDeg,0 );const TVector	SYAxis	= (TVector)UE::Math::TRotationMatrix<T>( Rotator ).GetScaledAxis( EAxis::Y );Rotator.Roll		= FMath::Atan2( ZAxis | SYAxis, YAxis | SYAxis ) * RadToDeg;Rotator.DiagnosticCheckNaN();return Rotator;
}

scale

UE提供了辅助数据结构TScaleMatrix来构造缩放矩阵:

template<typename T>
FORCEINLINE TScaleMatrix<T>::TScaleMatrix( const TVector<T>& Scale ): TMatrix<T>(TPlane<T>(Scale.X,	0.0f,		0.0f,		0.0f),TPlane<T>(0.0f,		Scale.Y,	0.0f,		0.0f),TPlane<T>(0.0f,		0.0f,		Scale.Z,	0.0f),TPlane<T>(0.0f,		0.0f,		0.0f,		1.0f))
{ }

从矩阵中提取scale也比较简单:

template<typename T>
inline TVector<T> TMatrix<T>::GetScaleVector(T Tolerance/*=UE_SMALL_NUMBER*/) const
{TVector<T> Scale3D(1, 1, 1);// For each row, find magnitude, and if its non-zero re-scale so its unit length.for (int32 i = 0; i < 3; i++){const T SquareSum = (M[i][0] * M[i][0]) + (M[i][1] * M[i][1]) + (M[i][2] * M[i][2]);if (SquareSum > Tolerance){Scale3D[i] = FMath::Sqrt(SquareSum);}else{Scale3D[i] = 0.f;}}return Scale3D;
}

TRS顺序

这一点UE和通用的规则保持一致,先缩放,再旋转,最后才是平移。UE提供TScaleRotationTranslationMatrix这个数据结构来构造TRS矩阵。

template<typename T>
FORCEINLINE TScaleRotationTranslationMatrix<T>::TScaleRotationTranslationMatrix(const TVector<T>& Scale, const TRotator<T>& Rot, const TVector<T>& Origin)
{T SP, SY, SR;T CP, CY, CR;GetSinCos(SP, CP, (T)Rot.Pitch);GetSinCos(SY, CY, (T)Rot.Yaw);GetSinCos(SR, CR, (T)Rot.Roll);M[0][0]	= (CP * CY) * Scale.X;M[0][1]	= (CP * SY) * Scale.X;M[0][2]	= (SP) * Scale.X;M[0][3]	= 0.f;M[1][0]	= (SR * SP * CY - CR * SY) * Scale.Y;M[1][1]	= (SR * SP * SY + CR * CY) * Scale.Y;M[1][2]	= (- SR * CP) * Scale.Y;M[1][3]	= 0.f;M[2][0]	= ( -( CR * SP * CY + SR * SY ) ) * Scale.Z;M[2][1]	= (CY * SR - CR * SP * SY) * Scale.Z;M[2][2]	= (CR * CP) * Scale.Z;M[2][3]	= 0.f;M[3][0]	= Origin.X;M[3][1]	= Origin.Y;M[3][2]	= Origin.Z;M[3][3]	= 1.f;
}

绕任意轴旋转

UE也提供了绕任意轴旋转任意角度的变换矩阵:

template<typename T>
inline TVector<T> TVector<T>::RotateAngleAxisRad(const T AngleRad, const TVector<T>& Axis) const
{T S, C;FMath::SinCos(&S, &C, AngleRad);const T XX	= Axis.X * Axis.X;const T YY	= Axis.Y * Axis.Y;const T ZZ	= Axis.Z * Axis.Z;const T XY	= Axis.X * Axis.Y;const T YZ	= Axis.Y * Axis.Z;const T ZX	= Axis.Z * Axis.X;const T XS	= Axis.X * S;const T YS	= Axis.Y * S;const T ZS	= Axis.Z * S;const T OMC	= 1.f - C;return TVector<T>((OMC * XX + C) * X + (OMC * XY - ZS) * Y + (OMC * ZX + YS) * Z,(OMC * XY + ZS) * X + (OMC * YY + C) * Y + (OMC * YZ - XS) * Z,(OMC * ZX - YS) * X + (OMC * YZ + XS) * Y + (OMC * ZZ + C) * Z);
}

这个矩阵又是怎么来的呢?它有两种主流的推导方式。第一种,我们可以先把任意的旋转轴变换到某个基向量上(例如x轴),然后问题就转换成了绕x轴旋转任意角度,旋转之后再把旋转轴变换到原来的位置。

UE5的TRS矩阵7

假设旋转轴为r,我们先基于r建立一个新的坐标系,r,s,t为三个基向量。那么矩阵M为
M = ( r x s x t x 0 r y s y t y 0 r z s z t z 0 0 0 0 1 ) M = \begin{pmatrix} r_x & s_x & t_x & 0 \\ r_y & s_y & t_y & 0 \\ r_z & s_z & t_z & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} M= rxryrz0sxsysz0txtytz00001
左手系下绕x轴旋转的矩阵为
R = ( 1 0 0 0 0 c o s α s i n α 0 0 − s i n α c o s α 0 0 0 0 1 ) R = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & cos\alpha & sin\alpha & 0 \\ 0 & -sin\alpha & cos\alpha & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} R= 10000cosαsinα00sinαcosα00001
三个矩阵相乘可以得到
M R M T = ( r x 2 + ( s x 2 + t x 2 ) c o s α r x r y + ( s x s y + t x t y ) c o s α + ( s x t y − s y t x ) s i n α r x r z + ( s x s z + t x t z ) c o s α + ( s x t z − s z t x ) s i n α 0 r x r y + ( s x s y + t x t y ) c o s α + ( s y t x − s x t y ) s i n α r y 2 + ( s y 2 + t y 2 ) c o s α r y r z + ( s y s z + t y t z ) c o s α + ( s y t z − s z t y ) s i n α 0 r x r z + ( s x s z + t x t z ) c o s α + ( s z t x − s x t z ) s i n α r y r z + ( s y s z + t y t z ) c o s α + ( s z t y − s y t z ) s i n α r z 2 + ( s z 2 + t z 2 ) c o s α 0 0 0 0 1 ) MRM^T = \begin{pmatrix} r_x^2 + (s_x^2 + t_x^2)cos\alpha & r_xr_y + (s_xs_y + t_xt_y)cos\alpha + (s_xt_y - s_yt_x)sin\alpha & r_xr_z + (s_xs_z + t_xt_z)cos\alpha + (s_xt_z - s_zt_x)sin\alpha & 0 \\ r_xr_y + (s_xs_y + t_xt_y)cos\alpha + (s_yt_x - s_xt_y)sin\alpha & r_y^2 + (s_y^2 + t_y^2)cos\alpha & r_yr_z + (s_ys_z + t_yt_z)cos\alpha + (s_yt_z - s_zt_y)sin\alpha & 0 \\ r_xr_z + (s_xs_z + t_xt_z)cos\alpha + (s_zt_x - s_xt_z)sin\alpha & r_yr_z + (s_ys_z + t_yt_z)cos\alpha + (s_zt_y - s_yt_z)sin\alpha & r_z^2 + (s_z^2 + t_z^2)cos\alpha & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} MRMT= rx2+(sx2+tx2)cosαrxry+(sxsy+txty)cosα+(sytxsxty)sinαrxrz+(sxsz+txtz)cosα+(sztxsxtz)sinα0rxry+(sxsy+txty)cosα+(sxtysytx)sinαry2+(sy2+ty2)cosαryrz+(sysz+tytz)cosα+(sztysytz)sinα0rxrz+(sxsz+txtz)cosα+(sxtzsztx)sinαryrz+(sysz+tytz)cosα+(sytzszty)sinαrz2+(sz2+tz2)cosα00001
接下来想办法要把多余的s和t变量给消除掉。首先我们知道基向量的模为1,那么有
r x 2 + s x 2 + t x 2 = 1 r_x^2 + s_x^2 + t_x^2 = 1 rx2+sx2+tx2=1

r y 2 + s y 2 + t y 2 = 1 r_y^2 + s_y^2 + t_y^2 = 1 ry2+sy2+ty2=1

r z 2 + s z 2 + t z 2 = 1 r_z^2 + s_z^2 + t_z^2 = 1 rz2+sz2+tz2=1

那么上述矩阵可简化为
( c o s α + r x 2 ( 1 − c o s α ) r x r y + ( s x s y + t x t y ) c o s α + ( s x t y − s y t x ) s i n α r x r z + ( s x s z + t x t z ) c o s α + ( s x t z − s z t x ) s i n α 0 r x r y + ( s x s y + t x t y ) c o s α + ( s y t x − s x t y ) s i n α c o s α + r y 2 ( 1 − c o s α ) r y r z + ( s y s z + t y t z ) c o s α + ( s y t z − s z t y ) s i n α 0 r x r z + ( s x s z + t x t z ) c o s α + ( s z t x − s x t z ) s i n α r y r z + ( s y s z + t y t z ) c o s α + ( s z t y − s y t z ) s i n α c o s α + r z 2 ( 1 − c o s α ) 0 0 0 0 1 ) \begin{pmatrix} cos\alpha + r_x^2(1 - cos\alpha) & r_xr_y + (s_xs_y + t_xt_y)cos\alpha + (s_xt_y - s_yt_x)sin\alpha & r_xr_z + (s_xs_z + t_xt_z)cos\alpha + (s_xt_z - s_zt_x)sin\alpha & 0 \\ r_xr_y + (s_xs_y + t_xt_y)cos\alpha + (s_yt_x - s_xt_y)sin\alpha & cos\alpha + r_y^2(1 - cos\alpha) & r_yr_z + (s_ys_z + t_yt_z)cos\alpha + (s_yt_z - s_zt_y)sin\alpha & 0 \\ r_xr_z + (s_xs_z + t_xt_z)cos\alpha + (s_zt_x - s_xt_z)sin\alpha & r_yr_z + (s_ys_z + t_yt_z)cos\alpha + (s_zt_y - s_yt_z)sin\alpha & cos\alpha + r_z^2(1 - cos\alpha) & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} cosα+rx2(1cosα)rxry+(sxsy+txty)cosα+(sytxsxty)sinαrxrz+(sxsz+txtz)cosα+(sztxsxtz)sinα0rxry+(sxsy+txty)cosα+(sxtysytx)sinαcosα+ry2(1cosα)ryrz+(sysz+tytz)cosα+(sztysytz)sinα0rxrz+(sxsz+txtz)cosα+(sxtzsztx)sinαryrz+(sysz+tytz)cosα+(sytzszty)sinαcosα+rz2(1cosα)00001
其次,基向量是相互正交的,也就是点积为0,那么有
r x r y + s x s y + t x t y = 0 r_xr_y + s_xs_y + t_xt_y = 0 rxry+sxsy+txty=0

r x r z + s x s z + t x t z = 0 r_xr_z + s_xs_z + t_xt_z = 0 rxrz+sxsz+txtz=0

r y r z + s y s z + t y t z = 0 r_yr_z + s_ys_z + t_yt_z = 0 ryrz+sysz+tytz=0

矩阵进一步简化为
( c o s α + r x 2 ( 1 − c o s α ) r x r y ( 1 − c o s α ) + ( s x t y − s y t x ) s i n α r x r z ( 1 − c o s α ) + ( s x t z − s z t x ) s i n α 0 r x r y ( 1 − c o s α ) + ( s y t x − s x t y ) s i n α c o s α + r y 2 ( 1 − c o s α ) r y r z ( 1 − c o s α ) + ( s y t z − s z t y ) s i n α 0 r x r z ( 1 − c o s α ) + ( s z t x − s x t z ) s i n α r y r z ( 1 − c o s α ) + ( s z t y − s y t z ) s i n α c o s α + r z 2 ( 1 − c o s α ) 0 0 0 0 1 ) \begin{pmatrix} cos\alpha + r_x^2(1 - cos\alpha) & r_xr_y(1 - cos\alpha) + (s_xt_y - s_yt_x)sin\alpha & r_xr_z(1 - cos\alpha) + (s_xt_z - s_zt_x)sin\alpha & 0 \\ r_xr_y(1 - cos\alpha) + (s_yt_x - s_xt_y)sin\alpha & cos\alpha + r_y^2(1 - cos\alpha) & r_yr_z(1 - cos\alpha) + (s_yt_z - s_zt_y)sin\alpha & 0 \\ r_xr_z(1 - cos\alpha) + (s_zt_x - s_xt_z)sin\alpha & r_yr_z(1 - cos\alpha) + (s_zt_y - s_yt_z)sin\alpha & cos\alpha + r_z^2(1 - cos\alpha) & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} cosα+rx2(1cosα)rxry(1cosα)+(sytxsxty)sinαrxrz(1cosα)+(sztxsxtz)sinα0rxry(1cosα)+(sxtysytx)sinαcosα+ry2(1cosα)ryrz(1cosα)+(sztysytz)sinα0rxrz(1cosα)+(sxtzsztx)sinαryrz(1cosα)+(sytzszty)sinαcosα+rz2(1cosα)00001
最后,基向量r可由s叉乘t得到,那么
r x = s y t z − s z t y r_x = s_yt_z - s_zt_y rx=sytzszty

r y = s z t x − s x t z r_y = s_zt_x - s_xt_z ry=sztxsxtz

r z = s x t y − s y t x r_z = s_xt_y - s_yt_x rz=sxtysytx

最终得到的矩阵
( c o s α + r x 2 ( 1 − c o s α ) r x r y ( 1 − c o s α ) + r z s i n α r x r z ( 1 − c o s α ) − r y s i n α 0 r x r y ( 1 − c o s α ) − r z s i n α c o s α + r y 2 ( 1 − c o s α ) r y r z ( 1 − c o s α ) + r x s i n α 0 r x r z ( 1 − c o s α ) + r y s i n α r y r z ( 1 − c o s α ) − r x s i n α c o s α + r z 2 ( 1 − c o s α ) 0 0 0 0 1 ) \begin{pmatrix} cos\alpha + r_x^2(1 - cos\alpha) & r_xr_y(1 - cos\alpha) + r_zsin\alpha & r_xr_z(1 - cos\alpha) - r_ysin\alpha & 0 \\ r_xr_y(1 - cos\alpha) - r_zsin\alpha & cos\alpha + r_y^2(1 - cos\alpha) & r_yr_z(1 - cos\alpha) + r_xsin\alpha & 0 \\ r_xr_z(1 - cos\alpha) + r_ysin\alpha & r_yr_z(1 - cos\alpha) - r_xsin\alpha & cos\alpha + r_z^2(1 - cos\alpha) & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} cosα+rx2(1cosα)rxry(1cosα)rzsinαrxrz(1cosα)+rysinα0rxry(1cosα)+rzsinαcosα+ry2(1cosα)ryrz(1cosα)rxsinα0rxrz(1cosα)rysinαryrz(1cosα)+rxsinαcosα+rz2(1cosα)00001

再看第二种推导方式。本质上,绕旋转轴旋转的向量,可分解为垂直于旋转轴的向量与平行于旋转轴的向量,平行于旋转轴的向量不参与旋转,只需要将垂直旋转轴的向量旋转相应的角度,就可以得到最终旋转后的向量。

UE5的TRS矩阵8

假设旋转轴为单位向量N,旋转向量为V,那么v可以分解为
V / / = ( V ⋅ N ) N V_{//} = (V \cdot N)N V//=(VN)N

V ⊥ = V − V / / V_{\perp} = V - V_{//} V=VV//

然后,对垂直于旋转轴的向量进行旋转,首先需要一组正交基:
X = V ⊥ ∣ ∣ V ⊥ ∣ ∣ X = \dfrac{V_{\perp}}{||V_{\perp}||} X=∣∣V∣∣V

Y = N × V ∣ ∣ N × V ∣ ∣ Y = \dfrac{N \times V}{||N \times V||} Y=∣∣N×V∣∣N×V

可得到旋转后的垂直向量
V ⊥ ′ = ∣ ∣ V ⊥ ∣ ∣ c o s α X + ∣ ∣ V ⊥ ∣ ∣ s i n α Y V'_{\perp} = ||V_{\perp}||cos\alpha X + ||V_{\perp}||sin\alpha Y V=∣∣V∣∣cosαX+∣∣V∣∣sinαY
最终向量即为
V ′ = V / / + V ⊥ ′ V' = V_{//} + V'_{\perp} V=V//+V
接下来还是一样,利用数学的魔法,把这些中间临时变量都消除掉。首先把上述几个式子代入,得到

V ′ = ( 1 − c o s α ) ( V ⋅ N ) N + c o s α V + s i n α ( N × V ) V' = (1 - cos\alpha)(V \cdot N)N + cos\alpha V + sin\alpha (N \times V) V=(1cosα)(VN)N+cosαV+sinα(N×V)
我们知道,叉乘是可以转换为矩阵形式的,即
N × V = V N × = V ( 0 n z − n y − n z 0 n x n y − n x 0 ) N \times V = V N_{\times} = V \begin{pmatrix} 0 & n_z & -n_y \\ -n_z & 0 & n_x \\ n_y & -n_x & 0 \end{pmatrix} N×V=VN×=V 0nznynz0nxnynx0
然后,根据向量三重积公式
N × ( N × V ) = N ⋅ ( N ⋅ V ) − V ⋅ ( N ⋅ N ) N \times (N \times V) = N \cdot (N \cdot V) - V \cdot (N \cdot N) N×(N×V)=N(NV)V(NN)

( V ⋅ N ) ⋅ N = V ⋅ ( I + N × 2 ) (V \cdot N) \cdot N = V \cdot (I + N_{\times}^2) (VN)N=V(I+N×2)
所以最终V’
V ′ = V ( ( 1 − c o s α ) ( I + N × 2 ) + c o s α + s i n α N × ) V' = V((1 - cos\alpha)(I + N_{\times}^2) + cos\alpha + sin\alpha N_{\times}) V=V((1cosα)(I+N×2)+cosα+sinαN×)
后面那一坨就是最终的矩阵了,计算之后,可以发现和第一种推导方式得出的结论一致。

Reference

[1] 内旋外旋与矩阵左乘右乘

[2] Unreal Engine C++: FMatrix doc sheet

[3] How to Rotate in Unity (complete beginner’s guide)

[4] Extrinsic & intrinsic rotation: Do I multiply from right or left?

[5] 【线性代数的几何意义】行列式的几何意义

[6] 绕任意轴旋转的两种方法简单推导

[7] 叉乘速查手册

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com