回顾Games101图形学(一)几何变换中一些公式的推导
回顾Games101 chatper1 – 6
前言
本文只写回顾后重新加深认识的知识
透视除法的意义
经过MVP矩阵之后,将模型空间下某点的坐标,转换成了裁剪空间下的坐标,此时因为裁剪空间的范围是x∈[-W/2,W/2]和y∈[-H/2,H/2],所以经过以下两个变换,其中除以pz就是透视除法
一:
\\
-1≤2·\frac{\left( \frac{p_y}{p_z}·near \right)}{h}≤1
\]
二:
x& y& z& w\\
\end{matrix} \right] \left[ \begin{matrix}
1& 0& 0& 0\\
0& 1& 0& 0\\
0& 0& 1& 0\\
\varDelta x& \varDelta y& \varDelta z& 1\\
\end{matrix} \right] =\left[ \begin{matrix}
x+\varDelta x*w& y+\varDelta y*w& z+\varDelta z*w& w\\
\end{matrix} \right]
\]
只有当W=1,这个三维坐标转换是等价的,才能保证位移的量是正确的,W=0时,则没有位移
只有当W=1时,三维坐标点转换成四维齐次坐标点才是等价的
坐标系变换和矩阵推导
坐标系变换理解不直观,倾向于101中闫老师所说的理解坐标系的转换通过矩阵进行的线性变换,将A坐标系下的点P,乘上矩阵得出B坐标系下的点P’,以下是抛开常见的变换(如透视投影变换、正交投影变换等)如何得出变换矩阵M,通过矩阵变换(下文着重说明)
已知坐标系A和坐标系B
\]
\]
则将坐标系B中一点P从坐标系B变换到坐标系A的变换矩阵为:(注意此处的例子是将源坐标系A变换到目标坐标系B下)
u_{\mathrm{x}}& u_{\mathrm{y}}& u_{\mathrm{z}}& 0\\
v_{\mathrm{x}}& v_{\mathrm{y}}& v_{\mathrm{z}}& 0\\
w_{\mathrm{x}}& w_{\mathrm{y}}& w_{\mathrm{z}}& 0\\
Q_{\mathrm{x}}& Q_{\mathrm{y}}& Q_{\mathrm{z}}& 1\\
\end{matrix} \right]
\]
如之前所说,变换过程中点p在空间中的绝对位置没有发生改变,只是参考坐标系发生了改变,从B坐标系变到A坐标系。(缩放,旋转,平移变换只有在同一坐标系下才有意义)
矩阵变换是基于基向量组的结果
- 矩阵变换之于同一个坐标系,可以理解为坐标系不变,点的位置改变
- 矩阵变换之于不同坐标系,可以理解为点的绝对位置不变,坐标系改变
x^{‘}\\
y^{‘}\\
\end{array} \right] =B\left[ \begin{array}{c}
x\\
y\\
\end{array} \right] \Rightarrow \left[ \begin{array}{c}
x\\
y\\
\end{array} \right] =B^{-1}\left[ \begin{array}{c}
x^{‘}\\
y^{‘}\\
\end{array} \right] \text{,}B=\left[ \begin{matrix}
\overrightarrow{b_1}& \overrightarrow{b_2}\\
\end{matrix} \right] \text{,且}\overrightarrow{b_1}\text{,}\overrightarrow{b_2}\text{是坐标系}B\text{的基向量}
\]
其中,矩阵B的各个列向量分别对应B坐标系的各个基向量,\(
\left[ \begin{array}{c}
x\\
y\\
\end{array} \right]
\)是向量\(
\overrightarrow{OP}
\)或者说点P在B坐标系的表示,\(
\left[ \begin{array}{c}
x^{‘}\\
y^{‘}\\
\end{array} \right]
\)则是向量\(
\overrightarrow{OP}
\)或者点P在A坐标系中的表示
以图中的两个向量\(
\overrightarrow{b_1}
\),\(
\overrightarrow{b_2}
\)为基确定一个坐标系B,显然在B坐标系中\(
\overrightarrow{b_{1B}}=\left[ \begin{array}{c}
1\\
0\\
\end{array} \right]
\),\(
\overrightarrow{b_{2B}}=\left[ \begin{array}{c}
0\\
1\\
\end{array} \right]
\),接下来,将\(
\overrightarrow{b_1}
\),\(
\overrightarrow{b_2}
\)定位到A坐标系中,得到\(
\overrightarrow{b_{1A}}=\left[ \begin{array}{c}
2\\
1\\
\end{array} \right]
\),\(
\overrightarrow{b_{2A}}=\left[ \begin{array}{c}
-1\\
1\\
\end{array} \right]
\)
\(
\because \overrightarrow{OP}=2\overrightarrow{b_1}+2\overrightarrow{b_2}
\)
\(
\therefore \overrightarrow{OP}\)在B坐标系中的表示为\(
\left[ \begin{array}{c}
2\\
2\\
\end{array} \right]
\),现在,将\(
\overrightarrow{OP}
\)用A坐标系描叙:
\(
\overrightarrow{OP}=2\overrightarrow{b_1}+2\overrightarrow{b_2}=2\overrightarrow{b_{1A}}+2\overrightarrow{b_{2A}}=\left[ \begin{matrix}
\overrightarrow{b_{1A}}& \overrightarrow{b_{2A}}\\
\end{matrix} \right] \left[ \begin{array}{c}
2\\
2\\
\end{array} \right] =\left[ \begin{array}{c}
2\\
4\\
\end{array} \right]
\\
\)
现在,令矩阵B=\(
\left[ \begin{matrix}
\overrightarrow{b_{1A}}& \overrightarrow{b_{2A}}\\
\end{matrix} \right]
\),P点是用B坐标系表示的任意一点\((x,y)\)。
于是\(
\overrightarrow{OP}
\)在A坐标系中的表示\(
\left[ \begin{array}{c}
^{x^{‘}}\\
y^{‘}\\
\end{array} \right] =B\left[ \begin{array}{c}
x\\
y\\
\end{array} \right]
\),显然,B是可逆的,于是就有了之前的结论
那么在这个例子当中,当我们需要知道某点在转换坐标系后的新坐标时,通过该例子也可以加深印象,比如在B坐标系下有点\(Q(3,4)\),即\(\overrightarrow{OQ}=(3,4)\),跟据刚才的例子可以看出它转换在A坐标系下的点
\overrightarrow{b_{1A}}& \overrightarrow{b_{2A}}\\
\end{matrix} \right] \left[ \begin{array}{c}
3\\
4\\
\end{array} \right] =\left[ \begin{matrix}
2& -1\\
1& 1\\
\end{matrix} \right] \left[ \begin{array}{c}
5\\
4\\
\end{array} \right] =\left[ \begin{array}{c}
6\\
9\\
\end{array} \right]
\]
即转换到A坐标系下的点\(Q^{‘}\)的坐标为\(Q^{‘}(6,9)\)
虽然这里的讨论是基于二维的,但是,结论可以扩展到任意维度
阐述结论:
将B坐标系的基向量定位到A坐标系,然后将定位之后的基向量作为矩阵B的列向量,用矩阵B对B坐标系中的点P的坐标进行矩阵变换,将得到点P在A坐标系中的坐标。这个过程,就是从坐标系B到坐标系A的一个追溯过程
View/Camera Transformation
先将相机移到原点,然后进行分别对坐标轴进行旋转,用矩阵表示则是\(M_{view}=R_{view}T_{view}\)
- 将相机移回原点
1& 0& 0& -x_e\\
0& 1& 0& -y_e\\
0& 0& 1& -z_e\\
0& 0& 0& 1\\
\end{matrix} \right]
\]
-
\(Rotate\,\,g\,\,to\,\,-Z, t\,\,to\,\,Y, \left( g×t \right) \,\,To\,\,X\)
g是相机看的方向(lookAt),t是相机向上的方向(Up),也就是相机的-Z轴和Y轴,两个向量叉积就是另一个坐标轴
x_{\widehat{g}×\widehat{t}}& x_t& x_{-g}& 0\\
y_{\widehat{g}×\widehat{t}}& y_t& y_{-g}& 0\\
z_{\widehat{g}×\widehat{t}}& z_t& z_{-g}& 0\\
0& 0& 0& 1\\
\end{matrix} \right]
\]
旋转矩阵是正交矩阵,所以旋转矩阵的逆就是旋转矩阵的转置
x_{\widehat{g}×\widehat{t}}& y_{\widehat{g}×\widehat{t}}& z_{\widehat{g}×\widehat{t}}& 0\\
x_t& y_t& y_t& 0\\
x_{-g}& y_{-g}& z_{-g}& 0\\
0& 0& 0& 1\\
\end{matrix} \right]
\]
正交投影矩阵
无论是正交投影还是透视投影,都是要将x、y、z移到-1到1的范围内,先将中心点移到原点,然后缩放
\frac{2}{r-l}& 0& 0& 0\\
0& \frac{2}{t-b}& 0& 0\\
0& 0& \frac{2}{n-f}& 0\\
0& 0& 0& 1\\
\end{matrix} \right) \left( \begin{matrix}
1& 0& 0& -\frac{r+l}{2}\\
0& 1& 0& -\frac{t+b}{2}\\
0& 0& 1& -\frac{n+f}{2}\\
0& 0& 0& 1\\
\end{matrix} \right)
\]
透视投影矩阵推导
首先先将frustum 转变为cuboid(n -> n,f -> f)(\(
M_{persp->ortho}
\)
)
然后再做正交投影
整个投影变换包括两部分
- v = P(矩阵)*p
- \(v=\frac{v}{v_w}=\frac{v}{pz}\)透视除法
以上大概推出等式这一步,接下来用公式展示更为直观
m00& m01& m02& m03\\
m10& m11& m12& m13\\
m20& m21& m22& m23\\
m30& m31& m32& m33\\
\end{matrix} \right) \left( \begin{array}{c}
x\\
y\\
z\\
1\\
\end{array} \right) =\left( \begin{array}{c}
\frac{x}{z*aspect*\tan \left( \frac{fov}{2} \right)}\\
\frac{y}{z*tan\left( \frac{fov}{2} \right)}\\
z^{‘’}\\
1\\
\end{array} \right)
\]
\]
将右边的四维列向量表示的坐标每一项乘以z,所以有
m00& m01& m02& m03\\
m10& m11& m12& m13\\
m20& m21& m22& m23\\
m30& m31& m32& m33\\
\end{matrix} \right) *\left( \begin{array}{c}
x\\
y\\
z\\
1\\
\end{array} \right) =\left( \begin{array}{c}
\frac{x}{aspect*\tan \left( \frac{fov}{2} \right)}\\
\frac{y}{\tan \left( \frac{fov}{2} \right)}\\
z*z^{{‘}{‘}}\\
z\\
\end{array} \right)
\]
所以求得矩阵为
\frac{1}{aspect*\tan \left( \frac{fov}{2} \right)}& 0& 0& 0\\
0& \frac{1}{\tan \left( \frac{fov}{2} \right)}& 0& 0\\
0& 0& m22& m23\\
0& 0& 1& 0\\
\end{matrix} \right)
\]
\\
\Rightarrow m22+\frac{m23}{z}=z^{{‘}{‘}}
\]
因为z=zNear时,z”=-1;z=zFar时,z”=1所以有以下等式
\\
m22+\frac{m23}{zFar}=1
\]
联立求得:
\\
m23=\frac{2*zFar*zNear}{zNear-zFar}
\]
最后求得投影矩阵为
\frac{1}{aspect*\tan \left( \frac{fov}{2} \right)}& 0& 0& 0\\
0& \frac{1}{\tan \left( \frac{fov}{2} \right)}& 0& 0\\
0& 0& \frac{-zFar-zNear}{zNear-zFar}& \frac{2*zNear*zFar}{zNear-zFar}\\
0& 0& 1& 0\\
\end{matrix} \right)
\]
将这样得矩阵乘以视锥体内的一个顶点坐标,得到一个新的向量,再将这个向量的每个分量除以第四个分量(此步骤也被称为透视除法)(w),这样就可以得到顶点映射到规则立方观察体后的新的坐标
注意:z坐标的映射方式的获得,最后我们是为了方便矩阵乘法的操作方向求得了z坐标与cvv中的z坐标的映射方式:
\]
此时的映射并不是线性的,当z越大时,z的变化对z”的扰动越小
Canonical Cube to Screen
- Irrelevant to z
- Transform in xy plane : [-1, 1] to [0, width] × [0, height]
- Viewport transform matrix:
视口矩阵
\frac{width}{2}& 0& 0& \frac{width}{2}\\
0& \frac{height}{2}& 0& \frac{height}{2}\\
0& 0& 1& 0\\
0& 0& 0& 1\\
\end{matrix} \right]
\]
深度z的计算
前言
3D光栅化发生在图元被变换到Screen space之后,因为这里的Screen space与2D的Screen Space完全一致,所以2D的光栅化算法在这里依然适用。
然而由于图元经过了投影变换,且投影变换为非线性变换,所以不能用简单的线性插值获取fragment的属性
p0\left( p0_x,p0_y,p0_z,1 \right)
$,$
p1\left( p1_x,p1_y,p1_z,1 \right)
$在near plane上的投影为点$
s0\left( s0_x,s0_y \right)
$,$
s1\left( s1_x,s1_y \right)
$。$
p0
$,$p1$中间一点$v(v_x,v_y,v_z,1)$在near plane上的投影为点$q(q_x,q_y)$。从图中可以看出点v到p0,p1的距离比值与点q到s0,s1的距离比值完全不同,投影变换不保持距离不变。
为了执行z-buffer算法,需要通过点q获取到v的深度值(z)
点\(v\)的深度值可以通过如下方法插值得到:
\]
以下是推导的过程:
手写版:
文字版:
由于点\(q\)为点\(v\)在near plane上的投影,因此点\(q\)与点\(v\)的关系为:
-
\(q_x=\frac{v_x·near}{v_z}\)
且\(v\)位于\(p0p1\)之间,则 -
\(v_z=p0_{z}+t·(p1_z-p0_z)=\frac{v_x·near}{q_x}\)
由点\(v\)在\(p0\),\(p1\)之间,点\(q\)在\(s0\),\(s1\)之间则有 - \(v_x=p0_{x}·(1-t)+p1_{x}·t=p0_{x}+t·(p1_{x}-p0_{x})\)
-
\(q_x=s0_{x}·(1-c)+s1_{x}·c=s0_{x}+c·(s1_{x}-s0_{x})\)
代入式(1)可得
\(v_z=\frac{v_x·near}{q_x}=\frac{(p0_x+t·(p1_x-p0_x))·near}{s0_x+c·(s1_x-s0_x)}\)式(2)
又s0和s1分别为p0和p1在near plane上的投影,则: - \(s0_x=\frac{p0_x·near}{p1_z}\)
-
\(s1_x=\frac{p1_x·near}{p1_z}\)
代入式(2)可得:
\]
\\
v_z=\frac{\left( p0_x·s0_x+t·\left( p1_x·s1_x-p0_x·s0_x \right) \right)}{s0_x+c·\left( s1_x-s0_x \right)}
\\
p0_z+t·\left( p1_z-p0_z \right) =\frac{\left( p0_x·s0_x+t·\left( p1_x·s1_x-p0_x·s0_x \right) \right)}{s0_x+c·\left( s1_x-s0_x \right)}
\\
\left( p0_z+t·\left( p1_z-p0_z \right) \right) ·\left( s0_x+c·\left( s1_x-s0_x \right) \right) =p0_x·s0_x+t·\left( p1_x·s1_x-p0_x·s0_x \right)
\\
p0_z·s0_x+p0_z·c·\left( s1_x-s0_x \right) +t·\left( p1_z-p0_z \right) ·s0_x+t·c·\left( p1_z-p0_z \right) ·\left( s1_x-s0_x \right) =p0_x·s0_x+t·\left( p1_x·s1_x-p0_x·s0_x \right)
\]
化简得:
\]
则:
\]
代入式(1)可得
\]
\]
\]
若View Space中三角形\(v0v1v2\),变换到Screen Space后为三角形\(s0s1s2\),\(v0v1v2\)内一点v在Screen Space的投影点\(s0s1s2\)内的点\(q\),对三角形\(s0s1s2\)内的点(fragment)\(q\),可以通过如下方法取得fragment\(q\)在View Space中对应的深度值:
\]
\(\lambda0,\lambda1,\lambda2\)为点p在三角形\(s0s1s2\)内的重心坐标
引入结论:
对Screen Space三角形\(s0,s1,s2\)内一点p的任意属性插值的公式为:
\]
\(\lambda0,\lambda1,\lambda2\)为点\(p\)的重心坐标,\(z0,z1,z2,z\)分别为\(s0,s1,s2,p\)在view space中对应点的深度值,可以用这个方法插值得到\(p\)在NDC Space内对应点的深度值
罗德里格斯旋转公式
字写得不好,在爬了…
手写版:
文字版:
首先先将\(\overrightarrow{k}\)处理成单位向量,这点很重要,关乎着下一步等式是否成立,有些博文写这里不需要处理单位向量,这是错的
\]
可得
\\
\overrightarrow{v}=\overrightarrow{v_{\bot}}+\overrightarrow{v_{||}}
\\
\overrightarrow{v_{\bot}}=\overrightarrow{v}-\overrightarrow{v_{||}}=\overrightarrow{v}-\left( \overrightarrow{v}·\overrightarrow{k} \right) \overrightarrow{k}
\]
绕\(\overrightarrow{k}\)做旋转时,向下做垂线,可看作底部经过了类似半圆的旋转
要求得\(\overrightarrow{v_{rot}}=\overrightarrow{v_{||}}+\overrightarrow{v_{rot\bot}}\),将\(\overrightarrow{v_{rot\bot}}\)作正交分解有\(
\overrightarrow{v_{rot\bot}}=\overrightarrow{a}+\overrightarrow{b}
\),易得\(
|\overrightarrow{w}|=|\overrightarrow{v_{\bot}}|
\),则有\(
\overrightarrow{w}=\overrightarrow{k}×\overrightarrow{v_{\bot}}=\overrightarrow{k}×\left[ \overrightarrow{v}-\overrightarrow{v_{||}} \right] =\overrightarrow{k}×\overrightarrow{v}-\overrightarrow{k}×\overrightarrow{v_{||}}=\overrightarrow{k}×\overrightarrow{v}-0=\overrightarrow{k}×\overrightarrow{v}
\)
接下来求\(
\overrightarrow{a}
\)和\(
\overrightarrow{b}
\)
\\
\overrightarrow{a}=\frac{\overrightarrow{w}}{|\overrightarrow{w}|}·|\overrightarrow{a}|=\frac{\overrightarrow{w}}{|\overrightarrow{v_{rot\bot}}|}·|\overrightarrow{v_{rot\bot}}|·\sin \left( \theta \right) =\overrightarrow{w}·\sin \left( \theta \right)
\]
\\
\overrightarrow{b}=\frac{\overrightarrow{v_{\bot}}}{|\overrightarrow{v_{\bot}}|}·|\overrightarrow{b}|=\frac{\overrightarrow{v_{\bot}}}{|\overrightarrow{v_{\bot}}|}·|\overrightarrow{v_{rot\bot}}|·\cos \left( \theta \right) =\overrightarrow{v_{\bot}}·\cos \left( \theta \right) \,\, \text{注意}|\overrightarrow{v_{\bot}}|=|\overrightarrow{v_{rot\bot}}|
\]
\\
\overrightarrow{v_{rot}}=\overrightarrow{v_{||}}+\overrightarrow{v_{rot\bot}}=\left( \overrightarrow{v}·\overrightarrow{k} \right) \overrightarrow{k}+\sin \left( \theta \right) ·\left( \overrightarrow{k}×\overrightarrow{v} \right) +\cos \left( \theta \right) \left( \overrightarrow{v}-\left( \overrightarrow{v}·\overrightarrow{k} \right) \overrightarrow{k} \right)
\\
=\cos \left( \theta \right) \overrightarrow{v}+\left( 1-\cos \left( \theta \right) \left( \overrightarrow{v}·\overrightarrow{k} \right) \overrightarrow{k} \right) +\sin \left( \theta \right) ·\left( \overrightarrow{k}×\overrightarrow{v} \right)
\]
把\(
\overrightarrow{k}
\)和\(
\overrightarrow{v}
\)分别写为列向量
k_x\\
k_y\\
k_z\\
\end{array} \right)
\]
v_x\\
v_y\\
v_z\\
\end{array} \right)
\]
令\(
\overrightarrow{v_{rot}}=R·\overrightarrow{v}
\)
两个式子
\]
k_yv_z-k_zv_y\\
k_zv_x-k_xv_z\\
k_xv_y-k_yv_x\\
\end{array} \right] =\left[ \begin{matrix}
0& -k_z& k_y\\
k_z& 0& -k_x\\
-k_y& k_x& 0\\
\end{matrix} \right] \left[ \begin{array}{c}
v_x\\
v_y\\
v_z\\
\end{array} \right]
\]
结合以上两个式子可得,其中\(I\)为3×3的单位矩阵
k_x\\
k_y\\
k_z\\
\end{array} \right) \left( \begin{matrix}
k_x& k_y& k_z\\
\end{matrix} \right) +\sin \left( \theta \right) \left( \begin{matrix}
0& -k_z& k_y\\
k_z& 0& -k_x\\
-k_y& k_x& 0\\
\end{matrix} \right) \,\,
\]
以下是比较通用的表示方式
0& -n_z& n_y\\
n_z& 0& -n_x\\
-n_y& n_x& 0\\
\end{matrix} \right)
\]
部分引用的博文
https://blog.csdn.net/unclerunning/article/details/70948696#齐次坐标系与平移