1 视锥
视锥把空间限制在一个6面的突面体,在这个空间的物体会被渲染,下图是一个截断的金字塔。
视图窗口大小,视角,视图距离。知道任意2个计算第三个变量。
视图窗口大小固定,调整视角(fov),视角越大,视图距离(focus length)越小。
视角固定,调整视图距离,视图距离越大,视图窗口越大。
视锥通常有一个近平面,近平面距离一般不为0,因为之后做除法会有问题,在投影矩阵中讲。
远平面是可选择的,一般为了提高效率会设置一个圆平面,近平面和远平面的距离决定了深度(z)的精度。
2 齐次坐标
三维空间
R3
中的点(x,y,z)可以表示齐次坐标
RP3
的点(x,y,z,1)
在n维空间中的点在n+1维空间中可以通过变换n维空间点的尺度,并把这个尺度放到多出来的维度上。
(x,y,z)→(x′,y′,z′,w)
即从三维空间到齐次坐标乘以一个尺度w,一般情况下w=1
(xw,yw,zw,w)
从齐次坐标到三维坐标
(x′,y′,z′,w)→(x′/w,y′/w,z′/w)
三维平面的点在齐次坐标系中有n个点,即不同的w,如果当w=0情况下,
(x′/w,y′/w,z′/w)
都无穷大,表示的是一个无穷远的点。通常在代码中,我们可以设置
(x,y,z,0)
表示一个向量,
(x,y,z,1)
表示一个顶点。
3 透视投影
假设视角(fov)为θ,我们采用和opengl一致的右手坐标系,看向-z轴,投影在yz平面上。焦距d计算如下:
tanθ2=1d→d=cotθ2
在规格化设备坐标(normalized device coordinates,ndc)中,即
x∈[−1,1],y∈[−1,1]
,我们投影在ndc坐标系中,投影yz平面如上,
yv,zv
表示在View空间的点,
(yndc,−d)
表示
yv,zv
投影在
z=−d
平面上的点,
yndcyv=d−zv→yndc=d−zvyv
假设平面宽高比为
a=wvhv
,
wv,hv
分别代表视窗的长和宽。同样要使得
yv
归一化到
[−1,1]
同理可以得到:
xndcxva=d−zv→xndc=d−azvxv
即:
⎧⎩⎨⎪⎪⎪⎪⎪⎪xndc=d−azvxvyndc=d−zvyv
总的公式如下:
⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪xndc=d−azvxvyndc=d−zvyvzndc=−d
所有的z坐标都变成了-d。不能表示成一个
线性或者是仿射变换。
齐次坐标中,
RP3
到
R3
变换通过一个w量。
把w设为-zv,就可以处理这个非线性的变换。
⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪x齐次=daxv(d/a,0,0,0)y齐次=dyv(0,d,0,0)z齐次=dzv(0,0,d,0)w=−zv(0,0,−1,0)
w未使用
写成矩阵形式
⎡⎣⎢⎢⎢⎢x齐次y齐次z齐次w⎤⎦⎥⎥⎥⎥=⎡⎣⎢⎢⎢⎢⎢da0000d0000d−10000⎤⎦⎥⎥⎥⎥⎥⎡⎣⎢⎢⎢xvyvzv1⎤⎦⎥⎥⎥
我们引入了一个w来表示这个变换,对于第三行我们知道把齐次坐标转换到三维坐标经过除法w。保留
z
的值用于深度测试。假设缩放到[-1,1](direx3D缩放到[0,1])。
假设近平面n和远平面f。那么z的坐标值分别为-n,-f。 [-n,-f]映射到[-1,1]
同样我们采用一个尺度A和平移B变换来表示
⎡⎣⎢⎢⎢⎢x齐次y齐次z齐次w⎤⎦⎥⎥⎥⎥=⎡⎣⎢⎢⎢⎢⎢da0000d0000A−100B0⎤⎦⎥⎥⎥⎥⎥⎡⎣⎢⎢⎢xvyvzv1⎤⎦⎥⎥⎥
即
zndc=Azv+Bw=−A−Bzv
把(-n,-1),(-f,1)带入得到
A=n+fn−fB=2nfn−f
把AB带入公式得到:
Mperspecitve=⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢da0000d0000n+fn−f−1002nfn−f0⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥
z映射到[0,1]
Mperspecitve=⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢da0000d0000fn−f−100nfn−f0⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥