代码改变世界

第四章 3d数学基础

2023-06-22 23:19  安川  阅读(92)  评论(0编辑  收藏  举报

第四章 3d数学基础

正交矩阵

定理:满足条件 \(M^TM = I\) 的矩阵是正交矩阵。


定理可能有些抽象,我们稍微具体一下:
假设有一个长度为n的正交向量组,满足\(\pmb{v_i}·\pmb{v_j}=δ_{ij}\) ,其中\(δ_(ij)\)在ij相等时为1,ij不相等时结果为0。假设这个正交向量组就是矩阵\(M\),那么\(M^TM\)的每一项就是\(v_i·v_j\), \(M^TM = I\)


正交矩阵具有很重要的性质,我们在计算机图形学中应用的大多数哦3x3矩阵都是正交矩阵:
①经过正交矩阵变换后,向量长度保持不变,即\(||M\pmb{v}||=||\pmb{v}||\).
②经过正交矩阵变换后,向量的相对位置保持不变,即\(M\pmb{v_1}·M\pmb{v_2}=\pmb{v_1}·\pmb{v_2}\)

齐次坐标

对于旋转、比例的变换,我们仅需要一个3x3的矩阵就可以了,如果同样用3x3的矩阵进行平移的变换呢?
我们举个例子,比如f(x)=x+(1,2,3),它就不是一个线性变换,因为它不满足标量乘法和矢量加法,也就不能通过一个3x3的矩阵来表示。
为了在线性变换中引入平移,我们使用齐次坐标来表示所有的线性变换,它是一个4x4的矩阵,具体格式如下:

\[F = \begin{bmatrix} M_{3x3}&\pmb{t_{3x1}}\\ \pmb{0_{1x3}}& 1\\ \end{bmatrix}\]

其中\(M_{3x3}\)矩阵表示旋转和缩放,\(\pmb{t_{3x1}}\)表示平移,\(\pmb{0_{1x3}}\)表示零矩阵,1是标量1 。
矩阵的逆为

\[F^{-1} = \begin{bmatrix} M^{-1}&-M^{-1}\pmb{t}\\ \pmb{0}& 1\\ \end{bmatrix}\]

平移矩阵

对一个点来说

\[\begin{bmatrix} 1&0&0&t_x\\ 0&1&0&t_y\\ 0&0&1&t_z\\ 0&0&0&1\\ \end{bmatrix} \begin{bmatrix} x\\y\\z\\1 \end{bmatrix} = \begin{bmatrix} x + t_x\\y + t_y\\z + t_z\\1 \end{bmatrix} \]

对于一个向量来说

\[\begin{bmatrix} 1&0&0&t_x\\ 0&1&0&t_y\\ 0&0&1&t_z\\ 0&0&0&1\\ \end{bmatrix} \begin{bmatrix} x\\y\\z\\0 \end{bmatrix} = \begin{bmatrix} x\\y\\z\\0 \end{bmatrix} \]

我们注意到对向量的平移没有发生任何变化,这是因为矢量没有位置属性。

缩放矩阵

对一个点来说

\[\begin{bmatrix} k_x&0&0&0\\ 0&k_y&0&0\\ 0&0&k_z&0\\ 0&0&0&1\\ \end{bmatrix} \begin{bmatrix} x\\y\\z\\1 \end{bmatrix} = \begin{bmatrix} k_xx\\k_yy\\k_zz\\1 \end{bmatrix} \]

对一个向量来说

\[\begin{bmatrix} k_x&0&0&0\\ 0&k_y&0&0\\ 0&0&k_z&0\\ 0&0&0&1\\ \end{bmatrix} \begin{bmatrix} x\\y\\z\\0 \end{bmatrix} = \begin{bmatrix} k_xx\\k_yy\\k_zz\\0 \end{bmatrix} \]

旋转矩阵

如果我们需要把点绕着x轴旋转\(\theta\)度,旋转矩阵为

\[R_x({\theta})= \begin{bmatrix} 1&0&0&0\\ 0&cos\theta&-sin\theta&0\\ 0&sin\theta&cos\theta&0\\ 0&0&0&1 \end{bmatrix} \]

绕y轴

\[R_y({\theta})= \begin{bmatrix} cos\theta&0&sin\theta&0\\ 0&1&0&0\\ -sin\theta&0&cos\theta&0\\ 0&0&0&1 \end{bmatrix} \]

绕z轴

\[R_z({\theta})= \begin{bmatrix} cos\theta&-sin\theta&0&0\\ sin\theta&cos\theta&0&0\\ 0&0&1&0\\ 0&0&0&1 \end{bmatrix} \]

对于绕任意轴\(\pmb{a}\)的旋转,我们有:

\[R_a({\theta})= \begin{bmatrix} cos\theta+(1-cos\theta)a_x^2& (1-cos\theta)a_xa_y-sin\theta a_z& (1-cos\theta)a_xa_z+sin\theta a_y&0\\ (1-cos\theta)a_xa_y+sin\theta a_z& cos\theta+(1-cos\theta)a_y^2& (1-cos\theta)a_ya_z-sin\theta a_x&0\\ (1-cos\theta)a_xa_z-sin\theta a_y& (1-cos\theta)a_ya_z+sin\theta a_x& cos\theta+(1-cos\theta)a_z^2&0\\ 0&0&0&1 \end{bmatrix} \]

复合变换

对于一个结合了平移、旋转、缩放的变换,我们可以将其写为:
\(\pmb{p'}=TRS\pmb{p}\)
其中S为缩放矩阵,R为旋转矩阵,T为平移矩阵。
计算顺序从右到左,即先缩放,再旋转,最后平移。
注意:这个过程不能前后替换。如果把平移和旋转替换了会怎么样呢?假设我们原先的步骤是先左转45°再向前走100米,替换后变成了先向前走100米再左转45°,两个步骤得到的位置显然是不一样的。

坐标空间的变换

坐标空间的转换一般分为四个步骤,局部空间到世界空间的转换,世界空间到观察空间的转换,观察空间到齐次裁剪空间空间的转换,其次裁剪空间到屏幕空间的转换。

局部空间到世界空间的转换

将点\(a_L=(a,b,c)\)从局部坐标系到世界坐标系的转换中,我们将其分为四个步骤。
①从原点\(O_L\)出发
②向局部坐标系的x轴移动a个单位
③向局部坐标系的y轴移动b个单位
④向局部坐标系的z轴移动c个单位
则我们得到的式子为:

\[a_W=O_L+a\pmb{x_L}+b\pmb{y_L}+c\pmb{z_L} \]

我们将这个式子转化为矩阵形式得:

\[M_{L→W}= \begin{bmatrix} |&|&|&|\\ \pmb{x_L}&\pmb{y_L}&\pmb{z_L}&\pmb{O_L}\\ |&|&|&|\\ 0&0&0&1 \end{bmatrix} \]

其中“|”符号表示向量按列展开。
但是对于一个矢量而言,或许我们不需要考虑原点的坐标变化,则我们可以用3x3的矩阵来表示矢量的坐标变换。

\[M_{L→W}= \begin{bmatrix} |&|&|\\ \pmb{x_L}&\pmb{y_L}&\pmb{z_L}\\ |&|&|\\ \end{bmatrix} \]

如果\(M_{L→W}\)是正交矩阵的话(局部坐标系的坐标轴互相垂直),那么就有

\[M_{W→L}=M_{L→W}^{-1}=M_{L→W}^T= \begin{bmatrix} -&\pmb{x_L}&-\\ -&\pmb{y_L}&-\\ -&\pmb{z_L}&-\\ \end{bmatrix} \]

世界空间到观察空间的转换

为了得到顶点在观察空间中的位置,我们可以计算观察空间的三个坐标轴在世界空间下的表示,构建出从观察空间变换到世界空间的变换矩阵,再对该矩阵求逆来得到从世界空间变换到观察空间的变换矩阵。

\[M_{W→V}=M_{V→W}^{-1}=(TR)^{-1}=R^{-1}T^{-1}=R^TT^{-1}=\\ \begin{bmatrix} -&\pmb{x_V}&-&0\\ -&\pmb{y_V}&-&0\\ -&\pmb{z_V}&-&0\\ 0&0&0&1 \end{bmatrix} \begin{bmatrix} 1&0&0&|\\ 0&1&0&-\pmb{O_V}\\ 0&0&1&|\\ 0&0&0&1 \end{bmatrix}= \begin{bmatrix} -&\pmb{x_V}&-&-O_V·\pmb{x_V}\\ -&\pmb{y_V}&-&-O_V·\pmb{y_V}\\ -&\pmb{z_V}&-&-O_V·\pmb{z_V}\\ 0&0&0&1 \end{bmatrix} \]

但是,由于Unity的观察空间使用的是右手坐标系,我们要对z进行取反,最后得到的矩阵为:

\[\begin{bmatrix} -&\pmb{x_V}&-&-O_V·\pmb{x_V}\\ -&\pmb{y_V}&-&-O_V·\pmb{y_V}\\ -&-\pmb{z_V}&-&O_V·\pmb{z_V}\\ 0&0&0&1 \end{bmatrix} \]

观察空间到齐次裁剪空间的转换

裁剪空间的目标是能够方便地对渲染图元进行裁剪:完全位于这块空间内部的图元将会被保留,完全位于这块空间外部的图元将会被剔除,而与这块空间边界相交的图元就会被裁剪。这块空间是由视锥体决定的

1. 透视投影

透视投影的视锥体如上图所示,其中我们可以通过Camera组件的Field of View(简称FOV)属性来改变视锥体竖直方向的张开角度,而Clipping Planes中的Near和Far参数可以控制视锥体的近裁剪平面和远裁剪平面距离摄像机的远近。

我们令近裁剪平面的高度为\(h_n\)、距离为\(d_n\),远裁剪平面的高度和距离为\(h_f\)\(d_f\),FOV为\(\alpha\),则有:

\[h_n=2·d_n·tan\frac{\alpha}{2}\\ h_f=2·d_f·tan\frac{\alpha}{2} \]

假设摄像机的纵横比为a,近裁剪平面的宽度为\(w_n\),远裁剪平面的高度为\(w_f\),则有:

\[a=w_n/h_n\\ a=w_f/h_f \]

透视投影的矩阵如下,这里不做推导:

\[M_{frustum}= \begin{bmatrix} \frac{cot\frac{\alpha}{2}}{a}&0&0&0\\ 0&cot\frac{\alpha}{2}&0&0\\ 0&0&-\frac{d_f+d_n}{d_f-d_n}&-\frac{2·d_f·d_n}{d_f-d_n}\\ 0&0&-1&0 \end{bmatrix} \]

我们注意到,透视投影改变了旋向性。变成了左手坐标系。

2. 正交投影

正交投影的视锥体是一个长方体,因此计算上相比透视投影来说更加简单。由图可以看出,我们可以通过Camera组件的Size属性来改变视锥体竖直方向上高度的一半,而Clipping Planes中的Near和Far参数可以控制视锥体的近裁剪平面和远裁剪平面距离摄像机的远近。
我们设Size为s,则有正交投影矩阵:

\[M_{frustum}= \begin{bmatrix} \frac{1}{as}&0&0&0\\ 0&\frac{1}{s}&0&0\\ 0&0&-\frac{2}{d_f-d_n}&-\frac{d_f+d_n}{d_f-d_n}\\ 0&0&0&1 \end{bmatrix} \]

同样正交投影矩阵也改变了旋向性


对于得到的(x,y,z,w)我们需要判断其是否在视锥体内,判断方式如下:

\[-w≤x≤w\\ -w≤y≤w\\ -w≤z≤w\]

如果上述条件都满足,则在视锥体内。

屏幕空间

经过投影矩阵的变换后,我们可以进行裁剪操作。当完成了所有的裁剪工作后,就需要进行真正的投影了,也就是说,我们需要把视锥体投影到屏幕空间 (screen space) 中。经过这一步变换,我们会得到真正的像素位置,而不是虚拟的三维坐标。
我们将其分为两个步骤:
①齐次除法,得到归一化的设备坐标(NDC),其实就是用w分量除x,y,z分量。

②转换为屏幕坐标,Unity的左下角是(0,0),右上角是(pixelwidth,pixelHeight),转换的过程就是缩放的过程。

总结


法线变换

法线 (normal) ,也被称为法矢量 (normal vector) 。在上面我们已经看到如何使用变换矩阵来变换一个顶点或一个方向矢量,但法线是需要我们特殊处理的一种方向矢量。在经过变换后,法线要求和切线依然保持垂直。
设切线为T,法线为N,则:

\[N'·T'=(GN)·(MT) =(GN)^TMT=N^TG^TMT\\ \]

其中G是法线变换的矩阵,M是切线变换的矩阵。
由于\(N^TT=0\),如果\(G^TM=I\),则上式等于0。
所以:

\[G=(M^{-1})^T \]

如果M是正交矩阵,则:

\[G=(M^{-1})^T=M \]

Unity Shader的内置变量

变换矩阵

变量名 描述
UNITY_MATRIX_MVP 当前的模型观察投影矩阵,用于将顶点/方向矢量从模型空间变换到裁剪空间
UNITY_MATRIX_MV 当前的模型观察矩阵,用于将顶点/方向矢量从模型空间变换到观察空间
UNITY_MATRIX_V 当前的观察矩阵,用于将顶点/方向矢量从世界空间变换到观察空间
UNITY_MATRIX_P 当前的投影矩阵,用于将顶点/方向矢量从观察空间变换到裁剪空间
UNITY_MATRIX_VP 当前的观察投影矩阵,用于将顶点/方向矢量从世界空间变换到裁剪空间
UNITY_MATRIX_T_MV UNITY_MATRIX_MV的转置矩阵
UNITY_MATRIX_IT_MV UNITY_MATRIX_MV的逆转置矩阵,用于将法线从模型空间变换到观察空间,也可用于得到UNITY_MATRIX_MV的逆矩阵
_Object2World 当前的模型矩阵,用于将顶点/方向矢量从模型空间变换到世界空间
_World2Object _Object2World的逆矩阵,用于将顶点/方向矢量从世界空间变换到模型空间

摄像机和屏幕参数

变量名 类型 描述
_WorldSpaceCameraPos float3 该摄像机在世界空间中的位置
_ProjectionParams float4 x = 1.0(或−1.0,如果正在使用一个翻转的投影矩阵进行渲染),y = Near,z = Far,w = 1.0 + 1.0/Far,其中Near和Far分别是近裁剪平面和远裁剪平面和摄像机的距离
_ScreenParams float4 x = width,y = height,z = 1.0 + 1.0/width,w = 1.0 + 1.0/height,其中width和height分别是该摄像机的渲染目标(render target)的像素宽度和高度
_ZBufferParams float4 x = 1− Far/Near,y = Far/Near,z = x/Far,w = y/Far,该变量用于线性化Z缓存中的深度值(可参考13.1节)
unity_OrthoParams float4 x = width,y = heigth,z 没有定义,w = 1.0(该摄像机是正交摄像机)或w = 0.0(该摄像机是透视摄像机),其中width和height是正交投影摄像机的宽度和高度
unity_CameraProjection float4x4 该摄像机的投影矩阵
unity_CameraInvProjection float4x4 该摄像机的投影矩阵的逆矩阵
unity_CameraWorldClipPlanes[6] float4该摄像机的6个裁剪平面在世界空间下的等式,按如下顺序:左、右、下、上、近、远裁剪平面