坐标转换
一个向量可以被表示为\(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数学基础:图形与游戏开发》