计算机图形学笔记(观测变换、模型变换、视图变换、投影变换、视口变换)
目录
一、简介
我们可以这样来描述观测变换(Viewing transformation)的任务:将虚拟世界中以(x,y,z)为坐标的物体(三维)变换到 以一个个像素位置(x,y) 来表示的屏幕坐标系之中(二维),这确实是一个较为复杂的过程,但是整个过程可以被细分为如下几个步骤:
1.模型变换(Model transformation)
将模型空间转换到世界空间,这一步的目的是将游戏场景中的物体调整至他们应该在的位置。
2.视图/摄像机变换(View/Camera transformation)
将世界空间转换到观察空间,在游戏中我们真正在乎的是摄像机(或者说眼睛)所看到的东西,也就是需要得到物体与摄像机的相对位置。
3.投影变换(Projection transformation)
将观察空间转换到裁剪空间,根据视图变换得到了所有可视范围内的物体对于摄像机的相对位置坐标(x,y,z)之后,便是根据是正交投影(Orthographic)还是透视投影(Perspective),裁剪空间的目标是能够方便地对渲染图元进行裁剪:完全位于这块空间内部的图元将会被保留,完全位于这块空间外部的图元将会被剔除,而与这块空间边界相交的图元就会被裁剪。
4.视口变换(Viewport transformation)
将裁剪空间转换到屏幕空间, 接下来,需要把裁剪空间投影 (真正的投影其实发生在这里) 到屏幕空间 (screen space) 中。经过这一步变换,我们会得到真正的像素位置,而不是虚拟的三维坐标。即 [ − 1 , 1 ] 2 → [ 0 , w i d t h ] ∗ [ 0 , h e i g h t ] [-1,1]^2 \rightarrow [0,width]*[0,height] [−1,1]2→[0,width]∗[0,height],其中width和height指屏幕分辨率大小。
二、模型变换
就像简介说的一样,就是利用基础的变换矩阵将世界当中的物体调整至我们想要的地方(旋转,平移,缩放)。
三、视图变换(或摄像机变换 camera transformation)
上文说,视图变换是为了得到了摄像机和物体的相对位置,那么怎么获得这个相对位置呢?
首先,我们需要先定义一个摄像机,通过下面三个信息我们可以定义一个摄像机:
摄像机的位置
e
⃗
\vec{e}
e
摄像机观测方向
g
^
\hat{g}
g^
摄像机的向上方向
t
^
\hat{t}
t^
OK,现在我们在空间中定义了一个摄像机,我们知道,只要物体和相机的相对位置不发生变化,那它们在空间中如何移动,相机观测获得的结果是相同的,所以我们不妨把相机规定到原点、向上的方向就是y轴正方向、并且向z轴负方向观测(右手系),其他的物体也随着相机移动到对应的相对位置上、我们得到的结果肯定是一样的。
所以我们需要一个视图变换矩阵
M
v
i
e
w
M_{view}
Mview这个矩阵将实现以下功能:
所以
M
v
i
e
w
=
R
v
i
e
w
T
v
i
e
w
M_{view}=R_{view}T_{view}
Mview=RviewTview
T
v
i
e
w
=
(
1
0
0
−
x
e
0
1
0
−
y
e
0
0
1
−
z
e
0
0
0
1
)
T_{view}=\left(\begin{matrix}1&0&0&-x_e\\0&1&0&-y_e\\0&0&1&-z_e\\0&0&0&1\end{matrix}\right)
Tview=⎝⎜⎜⎛100001000010−xe−ye−ze1⎠⎟⎟⎞
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}=\left(\begin{matrix}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{matrix}\right)
Rview−1=⎝⎜⎜⎛xg^×t^yg^×t^zg^×t^0xtytzt0x−gy−gz−g00001⎠⎟⎟⎞
R
v
i
e
w
=
R
v
i
e
w
−
1
T
R_{view} = R_{view}^{-1T}
Rview=Rview−1T
R
v
i
e
w
=
(
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}=\left(\begin{matrix}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{matrix}\right)
Rview=⎝⎜⎜⎛xg^×t^xtx−g0yg^×t^yty−g0zg^×t^ztz−g00001⎠⎟⎟⎞
视图变换总结:
变换摄像机到(0,0,0),上方向是y,向-z方向看,并把物体跟随相机一同变换。
四、投影变换
物体接下来要从观察空间转换到裁剪空间(clip space,也被称为齐次裁剪空间)中,这个用于转换的矩阵叫做投影矩阵(projection matrix),也被称为裁剪矩阵(clip matrix)。
裁剪空间的目标是能够方便地对渲染图元进行裁剪:完全位于这块空间内部的图元将会被保留,完全位于这块空间外部的图元将会被剔除,而与这块空间边界相交的图元就会被裁剪。那么,这块空间是如何决定的呢?答案是由视锥体(view frustum)来决定。
视锥体指的是空间中的一块区域,这块区域决定了摄像机可以看到的空间。视锥体由六个平面包围而成,这些平面也被称为裁剪平面(clip planes)。
视锥体有两种类型,这涉及两种投影类型:一种是正交投影(orthographic projection),一种是透视投影(perspective projection),如上图所示。
而用六个裁剪平面(视锥的六个面)直接判断相对复杂,所以需要投影操作将视锥变成裁剪空间,再通过齐次除法,将裁剪空间变成CVV(Canonical View Volume,规则观察体),OpenGL中CVV就是一个
[
−
1
,
1
]
3
[-1,1]^3
[−1,1]3的正方体(下面讨论这种),DirectX中的 z 分量稍微不同是 [0,1],此时的坐标就是 NDC(Normalized Device Coordinates,归一化设备坐标)
下面将分别介绍两种投影类型是如何变换得到CVV的
1.正交投影(orthographic)
如上图,正交投影的“视锥”是一个长方体,可以通过六个参数定义 right,left,top,bottom,near,far分别对应了x,y,z轴上的范围,从而构成了一个长方体。
而我们要是想令一个
[
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的标准正方体只需要进行下面步骤:
先把长方体平移到原点,再将其缩放拉伸。
所以可以得到以下变换矩阵:
2.透视投影(Perspective)
透视投影就是最类似人眼所看东西的方式,遵循近大远小,如果说正交投影都是水平线不会相交,那么透视投影则显然不是了。
透视投影的视锥如下图所示
想要将透视投影的裁剪空间变成CVV,可以先把裁剪空间压缩得像正交投影一样,再进行一次正交投影变换就可以了。所以我们需要一个矩阵将透视投影变换成正交投影即
M
p
e
r
s
p
−
>
o
r
t
h
o
M_{persp->ortho}
Mpersp−>ortho
推导:
对空间中任何一点
(
x
,
y
,
z
)
(x,y,z)
(x,y,z) 这一点的
y
y
y坐标经过变化应得到
y
′
y^{'}
y′,根据相似三角形可以得出以下结论
同理可得
x
x
x坐标经过变化应得到
x
′
x^{'}
x′
那么利用齐次坐标的性质(
(
x
,
y
,
z
,
w
)
(x,y,z,w)
(x,y,z,w)与
(
k
x
,
k
y
,
k
z
,
k
w
)
k
≠
0
(kx,ky,kz,kw)k\neq0
(kx,ky,kz,kw)k=0为同一个点),希望找到一个矩阵完成如下变换:
即:
首先,这个矩阵的前两行和最后一行是能很快确定出来的,根据最后的齐次坐标,如下:
那么如何确定第三行呢,这里就要运用透视投影的两个性质:
所有的近裁剪平面上的点(z=n)不会被压缩
所有远裁剪平面上点的z值(即f)不会发生变化
代入第一个性质则得到
我们发现n其实和x,y的值并没有关系,所以我们可以确定变换矩阵第三行的应类似于
(
0
,
0
,
A
,
B
)
(0,0,A,B)
(0,0,A,B)代入可以得出
A
n
+
B
=
n
2
An+B=n^2
An+B=n2
代入第二个性质,为了方便计算,代入far平面的中心点即x=y=0,可得到:
A
f
+
B
=
f
2
Af+B=f^2
Af+B=f2
容易解出
A
=
n
+
f
;
B
=
−
n
f
A = n + f ;B = -nf
A=n+f;B=−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} = \left(\begin{matrix}n&0&0&0\\0&n&0&0\\0&0&n+f&-nf\\0&0&1&0\end{matrix}\right)
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
五、视口变换
这一步就要获取图元在屏幕上对应的像素坐标了,就很简单了,一开始也介绍过就是两个范围空间的转换
[
−
1
,
1
]
2
→
[
0
,
w
i
d
t
h
]
∗
[
0
,
h
e
i
g
h
t
]
[-1,1]^2 \rightarrow [0,width]*[0,height]
[−1,1]2→[0,width]∗[0,height],z 坐标只用来判断深度大小,关注xy就行。
六、总结
至此历经上述4个变换,我们就已经成功的把游戏世界的任意可视物体转换到屏幕上了!
M
=
M
v
i
e
w
p
o
r
t
M
p
r
o
j
e
c
t
i
o
n
M
v
i
e
w
M
m
o
d
e
l
M = M_{viewport}M_{projection}M_{view}M_{model}
M=MviewportMprojectionMviewMmodel