构造投影矩阵/裁剪矩阵

来源:互联网 发布:java判断上午下午 编辑:程序博客网 时间:2024/04/28 01:20
 矩阵变化一般包含三部分,即wvp。此篇文章致力于讲解P矩阵即投影裁剪矩阵构造过程中的一些原理。

在讲解开始前,先了解一点。投影矩阵的作用是讲视锥体里的物体保存下来,最终与视口相对应。过程大致为三步骤。第一,将视锥体进行投影处理,使之成为所谓的平行管道观察体,可以理解为将视锥体转换为一个长方体;第二,将得到的视锥体变为一个立方体,这个立方体三条边最大不超过正负1。第三部就是将这个立方体中的内容与视口对应。我们所最终得到的矩阵其实仅仅能完成前两步,而且必须是齐次化之后的值。

下面是摄像机的大致示意图,可参考这幅图进行抽象推理。

有了前面的大致流程之后,我们可以一步步得到我们想要的矩阵。首先构造一个投影矩阵。先来看下大致的投影原理:

此图展示了大致的投影原理。途中z=d处为投影平面。p为实际点(x,y,z,1),p‘为投影点。不难得出p’的坐标为(dx/z,dy/z,d)。为了能够用4*4矩阵进行转换,我们可以按照矩阵乘法原理以及齐次原理推导出投影在某一投影平面上的投影矩阵,为下面矩阵

(1 0 0 0

    0 1 0 0

    0 0 1 1/d

    0 0 0 0)

当点乘以此矩阵后,得到一个思维齐次向量,除以第四维后即是三维空间的向量。

由此我们可以得到一个真正的其次投影矩阵。

例如:p‘乘以此矩阵后,就得到(x,y,z,z/d),然后全部除以z/d就得到了投影点的坐标。

现在我们的目的是为了得到平行管道观察体,不难发现,通过投影后,我们可以将x,y坐标进行矩形化,但是,z投影后变为了统一坐标,即最终投影结果为一平面,非是一个我们希望得到的长方体。这里我们必须改变z轴的变化,不能让它在站换后在一个平面上,这样做将抛弃z轴的信息,这个信息在后续过程中有重要作用。故而我们应想办法在x,y保持不变的情况下,改变z的变化。我们知道,我们最终的目的是得到一个立方体,而且我们知道立方体的边长皆为2,最大最小值分别为1,-1。我们不妨在此时就将z值到达我们想要的值。我们知道单靠缩放z的值不可能达到这个目标,故而需要平移+缩放。不妨假设矩阵为以下格式:

(1 0 0 0

    0 1 0 0

    0 0 a 1

    0 0 b 0)

如此一来,可以看到az+b为乘法的值,这个值要满足以下式子才能到达要求,即-1=<(az+b)/z<=1(opengl规定的,若是dx则是0~1),z的最大值为f,最小值为n,通过极值,最终求出a,b的值。

a=(f+n)/(f-n),b=(2nf)/(n-f)。

至此我们完成了到长方体的转换。

补充:可能会感到奇怪,为什么没有设定投影平面,而是直接是一。这里面有两个原因能解释。第一,投影平面在哪里并不重要,因为那些物体最终呈现在投影平面上是由视锥体决定的,而设置为1则计算起来很方便。第二,有资料上说,DX的投影平面就是为1。

   

现在能进行第二个步骤,即获得一个立方体。在DX里仍旧是长方体。

由于z的值在投影矩阵里已做过处理,这里不在考虑,主要考虑X,y的缩放比例。我们知道,XY的缩放比例必须与视口比例一致,不然会出现拉伸的情况。所以这里只需要知道视角或者缩放值就可以解决问题。一般情况下由用户提供视角大小。下面就来看如何通过视角得到缩放比例。


由图我们知道,目的是将x转换到1,通过三角函数,x×toom=x×(1/tan(角度/2))=x×z/x=z,我们知道,z的值为投影平面的值,所以z=1,所以toom为缩放值。toom=1/tan(角度/2)。按照比例可以通过x或者y的缩放值求得另外一个缩放值。

最后,我们降投影矩阵与缩放矩阵相乘就得到我们想要的矩阵来。

(1 0 0 0                                     (zoomx 0 0 0

    0 1 0 0                                     0 zoomy 0 0

    0 0 (f+n)/(f-n) 1           *            0 0 1 0

    0 0 2nf/(n-f) 0)                          0 0 0 1)

=(zooomx 0 0 0

    0 zoomy 0 0

    0 0 (f+n)/(f-n) 1

    0 0 2nf/(n-f) 0)   

这个是opengl里的矩阵形式。dx仅仅是z轴的变化上的区别。前面提到过。

得出这个矩阵之后,然后除以第四维值,就得到了立方体。

原创粉丝点击