OpenGL投影矩阵的推导过程

OpenGL学习脚印: 投影矩阵的推导

写在前面

               本节内容翻译和整理自http://www.songho.ca songho的博客《OpenGL Projection Matrix》内容,以供自己和初学者熟悉投影矩阵推导过程。

通过本节,你可以了解到:

  •  投影矩阵计算的阶段
  •  透视投影和正交投影的矩阵推导
本节的要点就在于:   阅读时,自己拿笔推导一遍。

1.概览

计算机屏幕是2维的,OpenGL渲染的3D场景必须以2D形式的图像投影到屏幕上。GL_PROJECTION 矩阵就是用来设置投影变换的。首先,它将所有顶点从眼坐标(照相机坐标)转换到裁剪坐标系下。然后,这些裁剪坐标通过透视除法,即除以裁剪坐标中w分量,转换到归一化设备坐标系(NDC)

                                                                                                                               
                                                                                                      一个由视锥裁剪的三角形

因此,我们要记住,裁剪(视锥剔除frustum culling)NDC转换都集成到了GL_PROJECTION 矩阵。接下来的部分描述了怎么样通过left, right, bottom, top, near and far 这6个界限参数来构造投影矩阵。

注意:

   视锥剔除是在裁剪坐标系中进行的,并且恰好在透视除法之前进行。裁剪坐标xc, yc 和 zc 通过与wc比较来进行测试。 如果某个坐标值比Wc小或者比Wc大,那么这个顶点将被丢弃。然后,OpenGL会重新在裁剪进行的地方构造多边形的边缘。

补充内容:

实际上,眼坐标系下坐标在乘以投影矩阵后,裁剪测试和透视除法都是由GPU来执行的。而后面这两个过程处理的裁剪坐标系数据都是由投影矩阵变换的。

1. 裁剪测试也即视锥剔除

-Wc < Xc,Yc,Zc < Wc

2. NDC透视除法

Xn = Xc / Wc  Yn = Yc / Wc   Zn = Zc / Wc

这里需要注意的是,我们在构造16个参数的投影矩阵的同时,不仅要考虑到裁剪,还要考虑到透视除法的过程。这样,最终的NDC坐标才会满足:

-1 < Xn,Yn,Zn < 1

2.透视投影

在透视投影中,在眼坐标下截头椎体(a truncated pyramid frustum)内的3D点被映射到NDC下一个立方体中;x坐标从[l,r]映射到[-1,1],y坐标从[b,t]映射到[-1,1],z坐标从[n,f]映射到[-1,1]。

注意:

     眼坐标系使用右手坐标系,而NDC使用左手坐标系。这就是说,眼坐标系下,在原点处的照相机朝着-Z轴看去,但是在NDC中它朝着+Z轴看去。因为glFrustum() 仅接受正的nearfar距离,我们在构造GL_PROJECTION 矩阵时,需要取其相反数。眼坐标系和NDC坐标系如下图所示:



OpenGL,眼坐标下3D点被投影到近裁剪面(即投影平面)。下图展示了眼坐标系下点(xe, ye, ze) 如何投影到近裁剪面上的 (xp, yp, zp) 的。(左侧是视锥的俯视图,右侧是视锥的侧视图,拿出右手构成右手坐标系,然后比划比划就出来了)


根据三角形的相似性,由俯视图可得出:

由侧视图可以得出:

补充: xp 和yp其实是一个中间值,我们要找的是(Xc, Yc, Zc)和 (Xn, Yn, Zn)之间的关系,但是可以利用:

这一关系做过渡,后面利用xp和yp,映射到NDC中xn和yn的线性关系就利用到了 xp 和yp。这一点很重要。


注意,这里 xp 和yp 都依赖于ze,他们与 -ze成反比。换句话说,他们都被 -ze相除。这个事构造GL_PROJECTION    矩阵最初的线索。在眼坐标通过乘以 GL_PROJECTION    来转换时,裁剪坐标系仍然是一个齐次坐标系。通过对裁剪坐标进行透视除法得到最终的NDC坐标。下图解释了这个过程:


因此我们可以把裁剪坐标系下的w分量设为-ze,那么GL_PROJECTION矩阵第4行变为(0, 0, -1, 0),如下图(求出了投影矩阵第4行):


下现在我们把xp和yp,映射到NDC中xn和yn,他们之间是线性关系: [l, r] ? [-1, 1]和[b, t] ? [-1, 1].

线性关系如下图所示:

则可以推导出:

细节部分有删节,这个推导过程使用的就是简单的y=kx+b线性关系推导,同理利用[b, t] ? [-1, 1]可推得:

将上面的 xp 和yp带入求得:

注意这里Xn和Yn已经是NDC中的坐标了,通过这两个坐标可以求出GL_PROJECTION 的前两行来,书写如下(求出了投影矩阵第1,2,4行):

现在怎么求出第3行呢?

找出zn与找出xnyn不同,因为 ze总是被投影近裁剪面-n上。但是我们需要唯一的Z值进行裁剪和深度测试。另外,我们还能够unproject即反向变换(inverse transform)。因为Z值不依赖于x或者y,因此我们借用w分量来找出 zn 和 ze之间的关系。

因此我们可以这样指定第3行:

在眼坐标下We等于1,因此上式变为:

我们使用(ze, zn)的关系(-n, -1)和 (-f, 1)来求解出系数A,B;

细节部分有删节,使用消元法即可求出:

我们求出了A和B,那么ze和zn关系如下式:

最终的投影矩阵如下式:

这个公式对应的是一般的视锥,如果视锥是对称的,即r = -l ,t= -b,那么有:

z-fighting

在继续之前,我们来看看表示ze和zn关系的式3.这是一个有理函数,并且ze和zn之间不是线性关系。这意味着,在近裁剪面的精度很高,而远裁剪面则很小。如果[-n, -f]范围变得大写,就会引起深度精度问题,即z-fighting。在远裁剪面附近,ze的小变化根本不影响zn值。近裁剪面和远裁剪面之间的n和f举例,应该尽可能小,来减少深度缓存的精度问题。可参考下图来帮助理解。


3.正交投影

构造正交投影的矩阵简单很多。所有的是眼坐标下xe, ye 和ze,都被线性的映射到NDC中。我们需要做的就是讲长方体视景体缩放为规范视见体,然后移动到原点。如下图所示:

以xe和xn之间映射关系为例,[l,r]=>[-1,1],则可以推导如下:

y,z也有类似推导,这里省略,最后得出投影矩阵为:

如果视锥是对称的话,即r = -l ,t= -b的话,则可以简化为:


到这里透视投影和正交投影矩阵推导完毕。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值