(本文未经许可禁止转载)

           

背景

OpenGL的渲染视窗默认是不提供任何透视变形的,也就是说如果我们画一块立方体上去,那么它渲染出来的楞在屏幕上是相互平行的(下图左边),没有透视关系。但我们往往想要的是下面这张图右面的效果:

透视图

思路

为了解决这个问题,我们常用的手法是:在视窗中把所有的顶点“变形”,让它们看上去有透视的感觉即可。也就是,我们所有的工作其实本质上还是在正交视图里做,只不过是把所有的几何体都变形了,让它们从特定角度上来看是存在透视关系的。

变形

用上面的图来描述的话,就是把左面灰色区域中所有的点,都通过一个3维空间Affine仿射变换矩阵(4x4)映射到上图右面的方盒里。再由OpenGL负责将仿射空间的几何体还原到欧式空间中。

形象的例子

根据学习这部分知识的时候,曾经听过了来自UC Davis Academics的一位老爷子的公开课,这课里面就有一个非常形象的例子

例子

左上角就是我们放置的立方体,而经过透视变换矩阵及还原欧式空间后,立方体就变形成老爷子指的地方了(视频连接)。

这里注意: 经过我后来的思考发现,其实这种变换存在一些“线渲染”的问题,因为我们映射的只是vertex信息,而连接点与点的折线则是在Fragment shading时期完成的。 但是仔细想想不难发现,若现实中一根直线段,它横跨我们视野的时候,根据透视规律,是弯着的。为了优化“线”的表述,不难猜测工业级CG软件需要独立构造Affine空间下的直线表述和图元。若是更加复杂的贝塞尔曲线则需要更高阶多项式的表述方式。

OpenGL实现

OpenGL Math好在不需要我们关注这个神奇的4x4的矩阵到底怎么构造的,它只要求我们提供一些参数就能构造一块透视矩阵:

参数图

glm::mat4 proj = glm::perspective(glm::radians(45.0f), (float)width/(float)height, 0.1f, 100.0f);
  • 第一个参数:FOV,一个弧度值,详情见图;
  • 第二个参数:摄像机视窗横纵比;
  • 第三个参数:近平面距离;
  • 第四个参数:远平面距离。

具体实现

在讨论具体实现前需要注意:

  • 我们在进行透视变换前,要求所有的点都已经转换成摄像机空间下的坐标了
  • 所有的矩阵都是列空间构造。
  • 矩阵不能含有输入位置的任何信息,所以我们通过把z转移到w来间接实现近大远小。

公式

M_{perspective} := \begin{pmatrix}
\frac{n}{r} & 0 & 0 & 0\\ 
0 & \frac{n}{t} & 0 &0 \\ 
0 & 0 & -\frac{f+n}{f-n} & -\frac{2fn}{f-n} \\ 
0 & 0 & -1 & 0
\end{pmatrix}

此时的参数:

变形

  • 这里,我们仅讨论视窗对称的情况,不讨论VR情形。
  • \lambda为任意不为0的实数。OpenGL为我们算好的的矩阵一般以\lambda=1为基准。

概览图

我们这章节完成了投射矩阵的学习。它在下列计算流水线的最后一步(M_{projection}:=M_{perspective}

\mathbf{v_{today}}=M_{projection}\cdot M_{camera}^{-1} \cdot M_{local} \cdot \mathbf{v_{local}}

后续工作

最后\mathbf{v_{today}}要被整体除以w坐标,得到的x,y就是屏幕空间的信息,z则被用来做深度测试

参考

OpenGL参考文章计算技术细节文章

       

(本文未经许可禁止转载)

   

发表评论