OpenGL ES —— Perspective Projection的推导

来源:互联网 发布:两个皇冠的淘宝店 编辑:程序博客网 时间:2024/05/21 12:03

引言

透视投影(Perspective Projection)是3D固定流水线的重要组成部分,是将相机空间中的点从视锥体(frustum)变换到规则观察体(Canonical View Volume)中,待裁剪完毕后进行透视除法的行为。

这里写图片描述 
简单的来讲就是把图中的cube投影到屏幕上(二维图形)的过程,我们丢弃了Z坐标,然后将它投影到屏幕上。(显然这种简单的投影会是的画面不是很真实,因为在现实世界里,越远的物体会变得越小,想象你站在铁路上,如果视线足够远的话,你脚底下的铁轨是会在远处相交的)

理解Perspective Projection还是很困难的,还好OpenGL为我们做好了这些底层工作,我们可以在不理解Perspective Projection的情况下也能够控制程序按照我们的意愿运行。不过我觉得程序员不应该只满足于调用API,而应该去尝试了解How does it work。

本文就是从数学角度一步步推导OpenGL里Perspective Projection的原理。不过我们首先要学习齐次坐标的相关知识,因为2D世界中通过它才能表示3D世界中的点。

let’s examine things at a visual level

在欧几里得空间(Euclidean space)里,两个平行的直线是永远不会相交的,但这在projective space里并不总是对的,考虑一下的例子: 
这里写图片描述 
这是一条铁轨,其中他的两条轨道是平行的,然而随着距离的拉长,我们可以看到最极远处,他们已经相交于一个点。显然在Euclidean Space里,2d/3d的几何图形被很好的描述,但是却不能够准确描述射影空间(projective space)里的图形:比如极远的点。两条平行线相交于无穷远处的一点,我们不妨设它为(∞,∞),然而这个点却在Euclidean Space无意义。

解决方案:齐次坐标(Homogeneous Coordinates)

Homogeneous Coordinates是使用N+1个坐标来表示N维坐标的一种表示方式,考虑2D点的例子,我们会使用一个额外的变量来表示一个点,比如(x, y)在Homogeneous Coordinates中就是(x, y, w),其中w就是那个额外的变量,之后这三个变量可以表示一个笛卡尔坐标(Cartesian coordinates)中新的点(X, Y): 
X = x / w; 
Y = y / w;

所以,对于刚刚上文提到的无穷远处的点(∞,∞)即可用(x, y, 0)表示。

那么为什么称它是Homogeneous的呢? 
我们知道当Homogeneous Coordinates坐标转为Cartesian coordinates只是简单的让x, y除以w:

这里写图片描述

那么考虑下面的例子:

这里写图片描述

我们发现(1, 2, 3), (2 , 4, 6)都指向了同一个点,因此他们是Homogeneous的(都指向了Cartesian space中的同一个点)

证明两条平行线相交

在Cartesian space中考虑如下的式子:

这里写图片描述

当 C 等于 D的时候无解,当C 不等于 D的时候这两条线是重合的,那么下面让我们在projective space重写这个式子

这里写图片描述

现在我们有一个解(x, y, 0),因此这两平行线条线将会在(x, y, 0)处相交,这也是上文我们所说的无穷远处的一点。

透视投影变换

在OpenGL中,我们不会明确指出齐次坐标中w的值,而是通过OpenGL提供的接口生成投影矩阵,之后我们的坐标(2D, 3D坐标)便会通过这个矩阵,变换到一个新的空间体中

我们先看下OpenGL提供给我们的接口 
这里写图片描述

  • fovy 是视线在y方向的角度
  • aspect 是宽高比
  • zNear 是近平面z坐标
  • zFar 是远平面z坐标

什么是近平面,远平面z坐标呢?

原来在透视投影中,视域体是一个金字塔 
这里写图片描述
小端的面称为近平面,大端成为远平面,相同的物体,如果离近平面越远,那么它就会被压缩的更厉害(因为这个形状变换到规范视域体盒子,更大的体积应该缩放一下放入规范视域体),因此便会有一种3D的感觉,毕竟越远的物体越小嘛。

我们看下刚刚接口生成的矩阵:

这里写图片描述

  • a是y方向角度一般的cot值
  • aspect 宽高比
  • f 远平面z坐标
  • n 近平面z坐标

证明

这里写图片描述

我们可以看到在View Frustum中有一点B,我们所要做的工作就是求出它在近平面中对应的点(A点),并且把A点的x, y 坐标最后规格化到[-1 , 1], z规格化到[0, 1]

根据三角形相似,我们可以得出

OD / OC = L2 / L
  • 1

其中OD = n, OC = z

所以

L2 / L = n / z
  • 1

对于L 它的长度是 
这里写图片描述

所以L2的长度是 
这里写图片描述

所以A的坐标就是

( xn / z, yn / z, n)
  • 1

下面就是把A的 x, y 坐标 映射到[-1 , 1]的范围中

我们假设在近平面中 x 的范围是[l, r], y的范围是[t, b]所以l <= x <= r (1)0 <= x - l <= r - l (2)0 <= (x - l) / (r - l) <= 1 (3)不等式两边乘以20 <=  2 * (x - l) / (r - l )<= 2 (4) 0 - 1 <= (2 * (x - l) / (r - l)) - 1 <= 2 - 1 (5)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

(5)式经过化简可得: 
这里写图片描述

同理y坐标: 
这里写图片描述

代入得: 
这里写图片描述

两边同乘以z得 
这里写图片描述

显然这里的已经不能化成: 
这里写图片描述

除非我们能够得到z’z = …的形式,这样只要对等式除以z就可以得到(x’, y’, z’)的形式

我们定义:

z'z = pz + q 
  • 1

因为z坐标比较特殊,他的范围是[0, 1],所以当z = n时z’ = 0, z = f时 z’ = 1 
所以得到等式

0 = np + q (6)f = p + q (7)
  • 1
  • 2
  • 3

解得

p = f / (f - n) (8)q = - fn / (f - n) (9)
  • 1
  • 2
  • 3

所以

z'z = f / (f - n) * p - fn / (f - n) (10)
  • 1

还剩下其次坐标系中的w值,不过这里不用担心,OpenGL默认会设置它为1,所以

w'z = z (11)
  • 1

现在我们得到的等式:

这里写图片描述

我们写成矩阵的形式 
这里写图片描述

现在有点像一开始的矩阵了,不过我们还可以再化简一下

r - l = w (12)t - b = h (13)
  • 1
  • 2
  • 3

这里写图片描述

我们看到,之前的api,还提供一个Y方向的角度: 
这里写图片描述

可得 
这里写图片描述

我们定义aspect为 w / h,但是为了方便计算,我们定义它为r 
所以 
这里写图片描述

所以现在得到的矩阵为: 
这里写图片描述

cot (α / 2) = 1 / tan ( α  / 2) = aaspect = r
  • 1
  • 2
  • 3

所以得到的公式是: 
这里写图片描述

0 0
原创粉丝点击