参考:html
http://www.songho.ca/opengl/gl_projectionmatrix.html数组
3D空间中的对象,最终显示在屏幕上,须要进行一系列的矩阵变化,将其从世界空间,转化到屏幕上。缓存
坐标的具体转化过程是:数据结构
世界坐标world---->视坐标eye-----》不一样的投影方法(平行投影,透视投影)投影面上坐标--->正则坐标(将可视体转化成2*2*2的正方体)---->屏幕坐标(像素点)htm
其中modelView矩阵 将世界坐标转化到eye坐标, 而projection矩阵将视坐标转化到正则坐标, 以后的像素生成不禁咱们控制。对象
Opengl使用的矩阵和通常数学中使用的矩阵不一样,二者互为转置(transpose), 这有点相似于2维数组存储时行有先仍是列优先的问题, 普通是行优先, 而OPENGL采用的是列优先的存储方式。(详细见数据结构---数组---多维数组的存储方式)get
有了以上讨论,要构造一个投影矩阵就须要知道:数学
采用的投影方法, 如何转化到正则坐标io
照相机的坐标系:照相机在原点, 面朝Z负方向, 向上是y轴。model
可视体:平行投影中是一个和照相机坐标轴平行的立方体, 由left right bottom top near 和 far 6个值来定义, 其中near表示 近平面距离照相机的位置, far表示远平面距离照相机的距离
下图是一个平行投影的可视体。
1:平行投影
对于任意在这个可视体中的点,咱们须要将其转化到一个正则可视体的坐标系中2*2*2
即将[left, right]->[-1, 1] [-near, -far]-->[-1, 1] [bottom, top]->[-1, 1]
假设点的eye坐标: xe ye ze we
则正则坐标 xn yn zn wn
其中wn 应该是1 表示没有透视效果
xn = (1-(-1))/(R-L)*(xe-L)+-1
yn = (1-(-1))/(T-B)*(ye-B) + -1
zn = (1-(-1))/(-F-(-n))*(ze-(-n)) + -1
wn = 1
xn = 2/(R-L)*xe- (R+L)/(R-L)
yn = 2/(T-B)*ye-(T+B)/(T-B)
zn = 2/(N-F)*ze - (-F+-N)/(N-F)
wn = 1
一般视坐标中的we = 1
因此数学上的投影矩阵形式是:
2/(R-L) 0 0 -(R+L)/(R-L)
0 2/(T-B) 0 -(T+B)/(T-B)
0 0 2/(N-F) (F+N)/(N-F)
0 0 0 1
2: 透视投影
透视投影须要首先将视坐标转化到可视体的近面上, 接着再进行正则化。
所以有3钟坐标:
xe ye ze we 视坐标
xp yp zp wp 近平面投影坐标
xc yc zc wc 切割空间坐标
xn yn zn wn 正则坐标
由于透视投影有近大远小的特色,须要引入切割空间坐标 用于转化到 正则坐标 xn = xc/wc yn = yc/wc zn = zc/wc
其中wc 和 ze 相关: wc = -ze
而投影矩阵 * 视坐标的结果是 切割空间坐标 即: projectionMatrix * (xe, ye, ze, we) = (xc, yc, zc, wc)
第一步:
缩放x y 方向
xp = -n/ze * xe
yp = -n/ze * ye
第二步 正则化 可视体:
设近平面的 范围是 Left Right Bottom Top
两个平面的z位置是 [-Near, -Far]
因此 [L, R] ->[-1, 1] [B, T]--->[-1, 1] [-N, -F]--->[-1, 1]
相似于平行投影咱们获得 xn xp yn yp zn ze 之间的关系, 须要注意zn 是和 ze 之间存在关系, 由于ze范围才是在[-N, -F]
xn = 2/(R-L)*xp- (R+L)/(R-L)
yn = 2/(T-B)*yp-(T+B)/(T-B)
zn = zc/wc
zc 和 ze 之间的关系不明确, 可是必须是线性关系 即 zc = A*ze + B*xe + C*ye + D*we
计算xn yn 和 xe ye 的关系: 将1/-ze 提出来, 做为wc 坐标缩放因子
xn = (2/(R-L)*n*xe+(R+L)/(R-L)*ze ) / -ze
yn = (2/(T-B)*n*ye+(T+B)/(T-B)*ze)/ -ze
因此
xc = 2/(R-L)*n*xe+(R+L)/(R-L)*ze
yc = 2/(T-B)*n*ye+(T+B)/(T-B)*ze
wc = -ze
初步的投影矩阵 xe ye we xc yc wc 的关系是:
2/(R-L)*n 0 (R+L)/(R-L) 0
0 2/(T-B)*n (T+B)/(T-B) 0
? ? A B
0 0 -1 0
而ze 和 zc之间存在关系 zc 和 xe, ye 之间是独立的, 因此上面的投影矩阵的第3行前两列为0, 后两列 系数是 A B
zn = (Aze + Bwe)/wc = (Aze+B)/ -ze 视坐标的we = 1
根据[-N, -F] ---> [-1, 1] 的正则关系
-1 = (A*-N+B)/ N
1 = (A*-F+B) / F
A*-N + B = -N
A*-F + B = F
determ = F-N
A = -(N+F)/(F-N)
B = -2NF/(F-N)
zn = Aze+B/-ze = -A + B/-ze = (N+F)/(F-N) - B/ze zn的值最后将存储在深度缓存中, 取值范围[-1, 1]
设 F-N = diff zn = 2N/diff + 1 + 2NF/(diff*ze)
其中 ze [-N, -F], ze 越靠近 -F 则zn 变化率越小 d(zn)/d(ze) = -2NF/(diff*ze*ze)
因此获得了整个透视投影矩阵:
求解矩阵采用待定系数法, 已知 坐标轴之间的独立关系, 以及变换后 取值范围的关系, 求解系数。