是坐标转换,也是旋转——矩阵变换
是坐标转换,也是旋转——矩阵变换
矩阵变换是3D图形学和游戏开发中的核心概念之一,它不仅能够实现坐标系之间的转换,还能够完成向量的旋转。本文将从基向量的表示开始,逐步解释矩阵变换的基本原理,并通过具体的代码示例展示其在实际应用中的实现方式。
坐标转换
一个向量可以被表示为(v=xi+yj+zk),其中(i),(j),(k)为坐标系的基向量:
(i= \begin{bmatrix} 1\ 0\ 0 \end{bmatrix}),(j= \begin{bmatrix} 0\ 1\ 0 \end{bmatrix}),(k= \begin{bmatrix} 0\ 0\ 1 \end{bmatrix})
基向量可写成矩阵的方式:
(\begin{bmatrix} i_x&i_y&i_z\ j_x&j_y&j_z\ k_x&k_y&k_z \end{bmatrix})
一个坐标系可以使用任意线性无关的基向量构成,如(l),(m),(n)
使用由(l),(m),(n)构成的矩阵乘以一个任意向量(\begin{bmatrix}a&b&c\end{bmatrix}):
(\begin{bmatrix} a&b&c \end{bmatrix} \begin{bmatrix} l_x&l_y&l_z\ m_x&m_y&m_z\ n_x&n_y&n_z \end{bmatrix}= \begin{bmatrix} al_x+bm_x+cn_x&al_y+bm_y+cn_y&al_z+bm_z+cn_z \end{bmatrix})
即
(\begin{bmatrix} al_x+bm_x+cn_x\ al_y+bm_y+cn_y\ al_z+bm_z+cn_z \end{bmatrix}=a \begin{bmatrix} l_x\ l_y\ l_z \end{bmatrix}+b \begin{bmatrix} m_x\ m_y\ m_z \end{bmatrix}+c \begin{bmatrix} n_x\ n_y\ n_z \end{bmatrix}= al+bm+cn)
最终得到了由基向量表示的结果,由此可见
如果把矩阵的行解释为坐标系的基向量,那么乘以该矩阵就相当于执行了一次坐标转换,若有(aM=b),我们就可以说,(M)将(a)转换到(b)。
从另一个角度看,x,y,z分量分别代表了向量在每个基向量方向上分别“走了多少个单位”,变换的只是基向量的方向,而在每个方向上走多远却没有改变。
旋转
使用单位且正交的基向量(x'),(y'),(z')构成新的坐标系,使用基向量构成的矩阵乘以向量(v)
(\begin{bmatrix} v_x&v_y&v_z \end{bmatrix} \begin{bmatrix} x_x&x_y&x_z\ y_x&y_y&y_z\ z_x&z_y&z_z \end{bmatrix}= \begin{bmatrix} v'_x&v'_y&v'_z \end{bmatrix})
可以发现,在矩阵变换向量的过程中,也完成了旋转。
应用
首先确定一个(forward)正方向,利用世界空间的(up)方向((0,1,0))通过叉积运算得到与(forward)正交的(right)方向,然后再利用计算得到的(right)方向通过叉积运算得到正确的(up)方向。
float3 forward = direction;
half isParallel = step(0.999, forward.y);
float3 up = isParallel * float3(0, 0, 1) + (1 - isParallel) * float3(0, 1, 0);
float3 right = normalize(cross(up, forward));
up = normalize(cross(forward, right));
在(up)的计算中,为了防止(forward)方向与世界空间的(up)方向平行得到错误的计算,加入判断选择需要使用的向量。
例如可以使用计算得到的三个基向量变换模型空间的顶点位置,以此达到旋转模型的目的:
float3 newPos = positionOS.x * right + positionOS.y * up + positionOS.z * forward;
或者构成矩阵用以旋转向量:
float3x3 rotationMatrix = float3x3(right, up, forward);
在实际使用过程中注意左乘和右乘的区别,即行向量与列向量的使用区别:
向量以行表示,则矩阵以行构建,向量以列表示,则矩阵以列构建。
(\begin{bmatrix} a&b&c \end{bmatrix} \begin{bmatrix} l_x&l_y&l_z\ m_x&m_y&m_z\ n_x&n_y&n_z \end{bmatrix}= \begin{bmatrix} al_x+bm_x+cn_x&al_y+bm_y+cn_y&al_z+bm_z+cn_z \end{bmatrix})
(\begin{bmatrix} l_x&m_x&n_x\ l_y&m_y&n_y\ l_z&m_z&n_z \end{bmatrix} \begin{bmatrix} a\ b\ c \end{bmatrix}= \begin{bmatrix} al_x+bm_x+cn_x\ al_y+bm_y+cn_y\ al_z+bm_z+cn_z \end{bmatrix})
在Unity Shader中, float3x3(right, up, forward) 表示将向量以行向量的方式组成矩阵,故变换向量时使用 mul(vector, rotationMatrix) 方式计算,若 rotationMatrix 以列向量方式存储,故变换向量时使用 mul(rotationMatrix, vector) 方式计算。
参考
《3D数学基础:图形与游戏开发》
本文原文来自cnblogs