DirectX中投影变换D3DXMatrixPerspectiveFovLH()其实产生的变换矩阵不是将3D物体转换为2D平面画面的变换。实际上 是把3D世界的物体变换到(1,1,0) (-1,1,0) (-1,-1,0) (1,-1,0) (1,1,1) (-1,1,1) (-1,-1,1) (1,-1,1)这个小盒子中。在Jim Adams 的著作<Role playing games with DriectX 8.0>和Frank Luna 的著作中<Introduction to 3D Game Programming with DirectX9.0>都把这个变换矩阵讲成从3D到2D的变换。实际上在DirectX中3D到2D的变换是由SDK自己完成的。而我们要做的是 把3D世界变换到上面那个小盒子中。
这个变换矩阵是投影变换的预变换。
在DirectX中,把视口的高作为3D世界里面的长度:2.0f来使用的,一个视点观察世界的视野范围在Y轴上无论什么情况下都是 -1.0f到1.0f(这一点在后面来证明)。把视口的高作为y轴,顶端坐标为(x,1),底端坐标为(x,-1)。
视口:在窗体中显示3D的矩形范围,可以通过D3DDevice的SetViewPort()来设置大小,默认的大小为窗体的大小。
变换的过程就是把点的向量乘以变换矩阵,可以得到这个点在新坐标系的对应向量,在投影变换矩阵中Z轴的值落在0~1之内,也就是说3D世界里所有的点都会被投影到以上描述的小盒子中。
我们用函数D3DXMatrixPerspectiveFovLH来获得该“投影变换”矩阵。
D3DXMATRIX * D3DXMatrixPerspectiveFovLH(
D3DXMATRIX * pOut ,
FLOAT fovy ,
FLOAT Aspect ,
FLOAT zn ,
FLOAT zf
);
其中参数fovy为y轴上的视角,Aspect为高宽比,zn为近裁面,zf为远裁面。
Y轴的视角:在DirectX的帮助文档中描述fovy为filed of view in y direction。
高宽比:一般用于全屏显示,如果我们要让3D世界充满整个窗口,就必须让高宽比等于窗口的高宽比,例如让我们的窗口正常显示”标准视野”[2X2 的一个面,顶点坐标为(1,1),(-1,1),(-1,-1),(1,-1)],我们必须把窗口的高宽比设置成1:1,这样,标准视野就可以充满整个窗 口,否则 会在左右留出空白,这个空白是无法用VIEW,WORLD变换所能填充的,相当于word文档采用“文档”视图里面的两边的灰色区域,是无法编辑的,在一 般的3D程序里面是不允许出现这种情况的,但是我们的显示器的高宽比不是1:1,而且大部分的用户也不习惯用1:1的窗口来使用程序,我们需要调整这个标 准视野,来适应我们的窗口大小,Aspect就是这个作用,有一点需要说明的Aspect不等于1,不代表标准视野就不是(1,1),(-1,1), (-1,-1),(1,-1),如果Aspect = 0.5 标准视野也还是(1,1),(-1,1),(-1,-1),(1,-1),只不过x轴上的单位长度的像素值 是 y轴上单位长度的像素值的2倍。
裁面的作用: 在Z轴方向上,不显示z坐标小于zn的或者大于 zf的点。
在上图中,紫红色的线为我们要显示的面,红色的弧就是上面函数fovy 的夹角,当这个夹角为90°的时候,我们窗口的视野为标准视野, 当夹角缩小时,可视范围就变小,如图,从90°变为60°的时候,可视范围变小了,要显示的面,相对于可以范围变大了,由于我们的窗口是不变的,也就是说实际显示的像素值不变,要显示的面就相对于变大,fovy的值变小的结果就是把要显示的面变大。当fovy为0的时候呢,视野就为0,就是说什么都看不到,窗口中显示的就是底色。
当fovy变大至180°的时候呢?视野的范围为视线夹角上的垂直于Z轴的且过点(0,0,1)的连线。当视线夹角变大视野范围也变大,夹角趋近于180°的时候视野范围趋近于无穷大,要显示的面相对于视野范围就是0,也就是说缩小到最小就是0.所以我们在设定fovy的值为180°的时候也是什么都看不见,为底色。
那大于180°的情况呢?在我们的视野理论上是不存在这种情况的,当视角大于180°时,视角于Z轴垂直的连线是不会过点(0,0,1)的。但是函数放在我们面前,我们是可以输入大于180°的数值,结果会如何呢?
非常神奇的事情发生了,我们的图以原点为中心旋转了180°!这是无法用我们的视野理论来解释的,因为180°的视野范围就已经是个极限了,“超越极限”是只能在广告中出现的词汇(如果能超越,那就不叫极限了:P)。
在MSDN的文档中有这样一个数学公式:y-scale = cot(fovy/2)
这下就明白了,当我们输入大于180°的时候cot的值为负数。而x-scale = y-scale/ Aspect。.
也就是说,一个3D世界的点,在视野大于180°的作用下,它的放大/缩小倍数不变,但是需要对X轴做一次对称变换,还要对Y轴做一次对称变换。相当于对原点做一次对称变换。
拿到这个公式,我们就可以对前面的标准做一个证明:
当fovy为90° Aspect = 1的时候,就是说y-scale = cot(45°) = 1
x-scale = 1,这时y和x的缩放比都为1,没有缩放。我们显示这样一个面
{-1.0f, -1.0f, 0.0f},
{-1.0f, 1.0f, 0.0f},
{ 1.0f, 1.0f, 0.0f},
{-1.0f, -1.0f, 0.0f},
{ 1.0f, 1.0f, 0.0f},
{ 1.0f,-1.0f, 0.0f}
我们会发现无论怎么改变窗口的高度,这个画面的Y轴方向上都会充满整个视口。(如果我们把视口的高度和宽度设置成一样的,在X轴上也会充满整个视口)所以DirectX会以视口的高度的二分之一作为Y轴的单位长度。