Assignment1-旋转与投影
Ilovegraphics.
预备知识:
- 正交矩阵:
一个矩阵,它的转置等于它的逆,这个矩阵是正交的。
旋转矩阵就满足这个条件。一个典型的旋转矩阵如下:
R θ = [ c o s θ − s i n θ s i n θ c o s θ ] R_\theta = \begin{bmatrix} cos \theta & -sin \theta \\ sin \theta & cos \theta \end{bmatrix}Rθ=[cosθsinθ−sinθcosθ]
这个矩阵的作用是逆时针旋转θ角,因此这个矩阵的逆矩阵是顺时针旋转θ角
因此把θ换成-θ就是这个矩阵的逆,经过化简得到:
R − θ = [ c o s θ s i n θ − s i n θ c o s θ ] = R θ T R_{-\theta} = \begin{bmatrix} cos\theta & sin\theta \\ -sin\theta & cos\theta \end{bmatrix}={R_\theta}^TR−θ=[cosθ−sinθsinθcosθ]=RθT
因此旋转矩阵是一个正交阵,即矩阵的逆等于矩阵的转置(三维同理)
M.V.P. 投影
MVP投影指的是一系列使用齐次坐标及矩阵乘法完成的坐标变换,最终使得整个三维空间利用透视投影压缩到[-1,1] [-1,1] [-1,1] 这个空间中。
设想使用相机拍照的过程:
- 摆好被摄物体(相当于Model变换)
- 摆好相机 相当于View变换(此时相机和模型相对位置不动,整体移动至原点)
- Cheese!(来自可爱的闫老师) Projection投影变换
由于Model变换比较简单,因此不在这里写了,下面写一下V和P。
View变换
- 相机和被摄物体已经摆好相对位置,现在需要把相机移动到坐标原点,且看向-z的方向。先确定一下几个方向向量(如图):
摄像机的位置e,相机朝向g,相机上方向t
所以整个视图view变换分为两部分:平移和旋转
M v i e w = R v i e w ⋅ T v i e w M_{view} = R_{view} \cdot T_{view}Mview=Rview⋅Tview
平移矩阵比较简单,如下:
T v i e w = [ 1 0 0 − x 0 1 0 − y 0 0 1 − z 0 0 0 1 ] T_{view} = \begin{bmatrix} 1 & 0 & 0 & -x \\\ 0 & 1 & 0 & -y \\\ 0 & 0 & 1 & -z \\\ 0 & 0 & 0 & 1 \end{bmatrix}Tview=⎣⎢⎢⎡1 0 0 001000010−x−y−z1⎦⎥⎥⎤
下面完成旋转矩阵:
- 假设摄像机的的上方向为 t ^ \hat tt^ 朝向是 g ^ \hat gg^,二者互相垂直
因此g ^ × t ^ = e ⃗ \hat g \times \hat t = \vec eg^×t^=e - 由于从相机朝向旋转到面朝-z轴比较难求,那我们先求从面朝-z轴旋转到当前朝向,之后将这个旋转矩阵求逆(求转置)即可!
R v i e w − 1 [ x g ^ × t ^ x t x − g 0 y g ^ × t ^ y t y − g 0 z g ^ × t ^ z t z − g 0 0 0 0 1 ] {R_{view}}^{-1} \begin{bmatrix} x_{\hat g \times \hat t} & x_t & x_{-g} & 0 \\\ y_{\hat g \times \hat t} & y_t & y_{-g} & 0 \\\ z_{\hat g \times \hat t} & z_t & z_{-g} & 0 \\\ 0 & 0 & 0 & 1 \end{bmatrix}Rview−1⎣⎢⎢⎡xg^×t^ yg^×t^ zg^×t^ 0xtytzt0x−gy−gz−g00001⎦⎥⎥⎤
R v i e w = R v i e w − 1 T = [ x g ^ × t ^ y g ^ × t ^ z g ^ × t ^ 0 x t y t z t 0 x − g y − g z − g 0 0 0 0 1 ] R_{view} = {{R_{view}}^{-1}}^T = \begin{bmatrix} x_{\hat g \times \hat t} & y_{\hat g \times \hat t} & z_{\hat g \times \hat t} & 0 \\ x_t & y_t & z_t & 0 \\ x_{-g} & y_{-g} & z_{-g} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}Rview=Rview−1T=⎣⎢⎢⎡xg^×t^xtx−g0yg^×t^yty−g0zg^×t^ztz−g00001⎦⎥⎥⎤
所以最终结果是:
R v i e w = R v i e w − 1 T = [ x g ^ × t ^ y g ^ × t ^ z g ^ × t ^ − x x t y t z t − y x − g y − g z − g − z 0 0 0 1 ] R_{view} = {{R_{view}}^{-1}}^T = \begin{bmatrix} x_{\hat g \times \hat t} & y_{\hat g \times \hat t} & z_{\hat g \times \hat t} & -x \\ x_t & y_t & z_t & -y \\ x_{-g} & y_{-g} & z_{-g} & -z \\ 0 & 0 & 0 & 1 \end{bmatrix}Rview=Rview−1T=⎣⎢⎢⎡xg^×t^xtx−g0yg^×t^yty−g0zg^×t^ztz−g0−x−y−z1⎦⎥⎥⎤
Projection变换
投影有两种:正交投影、透视投影
- 正交投影不会导致近大远小,两条铁轨并不会在远处相交
- 透视投影则反之,是人眼看东西的方式
正交投影:
在图形学中,正交投影是把[ l , r ] × [ b , t ] × [ f , n ] [l, r] \times [b, t] \times [f, n][l,r]×[b,t]×[f,n]构成的空间压缩成 [ − 1 , 1 ] 3 [-1,1]^3[−1,1]3的立方体中。
其中:
l = l e f t , r = r i g h t , b = b o t t o m , t = t o p , f = f a r , n = n e a r l = left ,r = right ,b = bottom ,t = top ,f = far ,n = nearl=left,r=right,b=bottom,t=top,f=far,n=near
注意:因为摄像机是朝 -Z 方向的,所以 n > f
要确定正交投影阵M o r t h o M_{ortho}Mortho ,需要T o r t h o , S o r t h o T_{ortho} , S_{ortho}Tortho,Sortho
两个矩阵都很简单:
T o r t h o = [ 1 0 0 − l + r 2 0 1 0 − b + t 2 0 0 1 − f + n 2 0 0 0 1 ] T_{ortho} = \begin{bmatrix} 1 & 0 & 0 & -{\frac {l + r} 2} \\ 0 & 1 & 0 & -{\frac {b + t} 2} \\ 0 & 0 & 1 & -{\frac {f + n} 2} \\ 0 & 0 & 0 & 1 \end{bmatrix}Tortho=⎣⎢⎢⎡100001000010−2l+r−2b+t−2f+n1⎦⎥⎥⎤
S o r t h o = [ 2 r − l 0 0 0 0 2 t − b 0 0 0 0 2 n − f 0 0 0 0 1 ] S_{ortho} = \begin{bmatrix} \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{bmatrix}Sortho=⎣⎢⎢⎡r−l20000t−b20000n−f200001⎦⎥⎥⎤
所以:
M o r t h o = S o r t h o ⋅ T o r t h o = [ 2 r − l 0 0 − l + r 2 0 2 t − b 0 − b + t 2 0 0 2 n − f − f + n 2 0 0 0 1 ] M_{ortho} = S_{ortho} \cdot T_{ortho} = \begin{bmatrix} \frac 2 {r - l} & 0 & 0 & -{\frac {l + r} 2} \\ 0 & \frac 2 {t - b} & 0 & -{\frac {b + t} 2} \\ 0 & 0 & \frac 2 {n - f} & -{\frac {f + n} 2} \\ 0 & 0 & 0 & 1 \end{bmatrix}Mortho=Sortho⋅Tortho=⎣⎢⎢⎡r−l20000t−b20000n−f20−2l+r−2b+t−2f+n1⎦⎥⎥⎤
透视投影:
透视投影与正交投影类似,也是经过类似的步骤:
- 把相机整个空间平移到原点
- 把空间压缩成长方体(近平面不变,压缩远平面)
- 把空间压缩成[ − 1 , 1 ] 3 [-1,1]^3[−1,1]3的立方体(进行一次正交投影)
相比正交投影,透视投影多了一个步骤,就是把视锥体变换成长方体,这个变换暂且叫做M p − > o M_{p->o}Mp−>o 。 要计算这个变换,我们需要先知道这个变换过程发生了什么。
这是frustum的侧切面
假设在frustum之中的某个点( x , y , z ) (x,y,z)(x,y,z),我们要求经过压缩后y yy变化成的y ′ y^\primey′,则可以使用相似三角形求解
y ′ = n z y , x 也 同 理 : x ′ = n z x y^{\prime} = \frac n z y ,x也同理: x^{\prime} = \frac n z xy′=zny,x也同理:x′=znx
所以我们得到如下结果:
M p e r s p − > o r t h o [ x y z 1 ] = [ n x z n y z u n k n o w 1 ] ⟺ [ n x n y u n k n o w z ] M_{persp -> ortho} \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix}= \begin{bmatrix} \frac {nx} z \\ \frac {ny} z \\ unknow \\ 1 \end{bmatrix} \iff \begin{bmatrix} nx \\ ny \\ unknow \\ z \end{bmatrix}Mpersp−>ortho⎣⎢⎢⎡xyz1⎦⎥⎥⎤=⎣⎢⎢⎡znxznyunknow1⎦⎥⎥⎤⟺⎣⎢⎢⎡nxnyunknowz⎦⎥⎥⎤
稍加计算即可得到M矩阵的1、2、4行如下:
M p e r s p − > o r t h o = [ n 0 0 0 0 n 0 0 ? ? ? ? 0 0 1 0 ] M_{persp -> ortho} = \begin{bmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ ? & ? & ? & ? \\ 0 & 0 & 1 & 0 \end{bmatrix}Mpersp−>ortho=⎣⎢⎢⎡n0?00n?000?100?0⎦⎥⎥⎤
!但是需要注意的是目前我们的计算是基于使用齐次坐标的点最后一维我们化成了z,所以后面的求解也要化成z再带入。
矩阵的第三行是求解z zz变化为z ′ z^\primez′的变换。
下面我们知道有两个特殊点:n面上所有点的z不会变,f面的中心点z不会变
所以就用这两个特殊点来求解M矩阵的第三行 :
1、由n面上一点得:
[ n 0 0 0 0 n 0 0 ? ? ? ? 0 0 1 0 ] [ x y n 1 ] = [ x y n 1 ] = = [ n x n y n 2 n ] \begin{bmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ ? & ? & ? & ? \\ 0 & 0 & 1 & 0 \end{bmatrix} \begin{bmatrix} x \\ y \\ n \\ 1 \end{bmatrix} = \begin{bmatrix} x \\ y \\ n \\ 1 \end{bmatrix} == \begin{bmatrix} nx \\ ny \\ n^2 \\ n \end{bmatrix}⎣⎢⎢⎡n0?00n?000?100?0⎦⎥⎥⎤⎣⎢⎢⎡xyn1⎦⎥⎥⎤=⎣⎢⎢⎡xyn1⎦⎥⎥⎤==⎣⎢⎢⎡nxnyn2n⎦⎥⎥⎤
所以通过第三行可得:
a x + b y + c n + d = n 2 ax + by + cn + d = n^2ax+by+cn+d=n2
因为不用x,y则无法在a,b凑出0,所以a=0,b=0.
则第三行可知为:[ 0 0 A B ] \begin{bmatrix}0&0&A&B\end{bmatrix}[00AB]
所以得:A n + B = n 2 An + B = n^2An+B=n2 ①
2、由f面的中心点得:
[ 0 0 A B ] [ 0 0 f 1 ] = [ 0 0 f 1 ] = = [ 0 0 f 2 f ] \begin{bmatrix} 0 & 0 & A & B \end{bmatrix} \begin{bmatrix} 0 \\ 0 \\ f \\ 1 \end{bmatrix} = \begin{bmatrix} 0 \\ 0 \\ f \\ 1 \end{bmatrix} == \begin{bmatrix} 0 \\ 0 \\ f^2 \\ f \end{bmatrix}[00AB]⎣⎢⎢⎡00f1⎦⎥⎥⎤=⎣⎢⎢⎡00f1⎦⎥⎥⎤==⎣⎢⎢⎡00f2f⎦⎥⎥⎤
即:A f + B = f 2 Af + B = f^2Af+B=f2 ②
联立等式 ① ② 解得:
{ A = n + f B = − n f \begin{cases} A = n+f\\ B = -nf \end{cases}{A=n+fB=−nf
至此整个矩阵填写完毕:
M p e r s p − > o r t h o = [ n 0 0 0 0 n 0 0 0 0 n + f − n f 0 0 1 0 ] M_{persp -> ortho}= \begin{bmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ 0 & 0 & n + f & -nf \\ 0 & 0 & 1 & 0 \end{bmatrix}Mpersp−>ortho=⎣⎢⎢⎡n0000n0000n+f100−nf0⎦⎥⎥⎤
而投影矩阵为:M p e r s p = M o r t h o M p e r s p − > o r t h o M_{persp} = M_{ortho} M_{persp -> ortho}Mpersp=MorthoMpersp−>ortho,还需要求M o r t h o M_{ortho}Mortho
前文中此矩阵表达为:
M o r t h o = [ 2 r − l 0 0 − l + r 2 0 2 t − b 0 − b + t 2 0 0 2 n − f − f + n 2 0 0 0 1 ] M_{ortho} = \begin{bmatrix} \frac 2 {r - l} & 0 & 0 & -{\frac {l + r} 2} \\ 0 & \frac 2 {t - b} & 0 & -{\frac {b + t} 2} \\ 0 & 0 & \frac 2 {n - f} & -{\frac {f + n} 2} \\ 0 & 0 & 0 & 1 \end{bmatrix}Mortho=⎣⎢⎢⎡r−l20000t−b20000n−f20−2l+r−2b+t−2f+n1⎦⎥⎥⎤
n nn和f ff都是已知量(哪些东西要拍下来)而t b l r t\;b\;l\;rtblr都是未知的。
因此在图形学中,对于透视投影还有两个重要概念:f i e l d o f v i e w ( f o v ) 、 a s p e c t r a t i o \color {red}{field\;of\;view(fov)}、aspect \;ratiofieldofview(fov)、aspectratio
fov
是指视野范围,分为 fovY
和 fovX
;aspect ratio
是指近平面n nn的宽高比。fovX、fovY、aspect ratio
知二推三。
由图易知:t a n f o v Y 2 = t ∣ n ∣ tan {\frac {fovY} 2} = \frac t {|n|}tan2fovY=∣n∣t
由定义易知:a s p e c t = w i d t h h e i g h t = 2 r 2 t = r t aspect = \frac {width} {height} = \frac {2r} {2t} = \frac r taspect=heightwidth=2t2r=tr
所以t b l r t\;b\;l\;rtblr可联立方程求得:
{ t = ∣ n ∣ ⋅ t a n f o v Y 2 r = a s p e c t ⋅ t b = − t l = − r \begin{cases} t = |n| \cdot tan {\frac {fovY} 2}\\ r = aspect \cdot t\\ b = -t\\ l = -r \end{cases}⎩⎪⎪⎪⎨⎪⎪⎪⎧t=∣n∣⋅tan2fovYr=aspect⋅tb=−tl=−r
所以所有要求的量都有了,代入M o r t h o M_{ortho}Mortho即可
至此,Projection变换求毕。
T E X { \it {T_EX}}TEX 真好看!真难敲!
作业代码:
// main.cpp
#include <cmath>
Eigen::Matrix4f get_model_matrix(float rotation_angle)
{
Eigen::Matrix4f model = Eigen::Matrix4f::Identity();
float rad = rotation_angle * TO_RAD;
float sin_theta = sin(rad);
float cos_theta = cos(rad);
model(0, 0) = cos_theta;
model(0, 1) = -sin_theta;
model(1, 0) = sin_theta;
model(1, 1) = cos_theta;
return model;
}
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio,
float zNear, float zFar)
{
Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();
Eigen::Matrix4f ortho = Eigen::Matrix4f::Identity();
Eigen::Matrix4f proj_to_ortho = Eigen::Matrix4f::Identity();
float n, f, l, r, t, b;
n = zNear;
f = zFar;
t = n * tan((eye_fov / 2) * TO_RAD);
b = -t;
r = t * aspect_ratio;
l = -r;
ortho(0, 0) = 2 / (r - l);
ortho(1, 1) = 2 / (t - b);
ortho(2, 2) = 2 / (n - f);
ortho(0, 3) = -(r + l) / 2;
ortho(1, 3) = -(t + b) / 2;
ortho(2, 3) = -(n + f) / 2;
proj_to_ortho(0, 0) = n;
proj_to_ortho(1, 1) = n;
proj_to_ortho(2, 2) = n + f;
proj_to_ortho(2, 3) = -n * f;
proj_to_ortho(3, 2) = 1;
proj_to_ortho(3, 3) = 0;
projection = ortho * proj_to_ortho;
return projection;
}
芜湖,小三角转起来了!