[翻译转载]OpenGL投影矩阵(Projection Matrix)构造方法

OpenGL Projection Matrix

原文地址:原文

Overview

电脑显示器是一个二维平面, 而OpenGL渲染出来的场景却是三维的 , 所以必须要投影到二维的电脑屏幕上. 可以使用 GL_PROJCETION matrix 来进行投影转换. 首先,它把顶点数据从 eye coordinates(视点坐标) 转换到 clip coordinates(裁剪坐标). 在将这些坐标除以(w) 坐标分量来转换到NDC(标准化设备坐标)上

因此,我们应该清楚 裁剪(可视平截头体的裁剪) 和 NDC 的转换都包含在了 GL_PROJECTION 矩阵中了. 下一段将会说明如何通过l,r,b,t,n,f(左,右,下,上,近和远)这六个边界值来构建透视矩阵.

注意 可视平截头体的裁剪(裁剪) 是在 clip coordinates(裁剪坐标) 中, 在除以 (w_c) 前执行的.
clip coordinates 的 (x_c,y_c,z_c) 会通过 (w_c) 来测试, 如果任何一个大于(w_c) 或 小于(-w_c), 那么这个顶点就会被丢弃.

[-w_c<x_c,y_c,z_c<w_c ]

当有丢弃时,OpenGL 将会重新构建多边形的边.

一个被平截头体剪裁的三角形
一个被平截头体剪裁的三角形

## Perspective Projection(透视投影)

在透视投影中, truncated pyramid frustum (eye coordinates) (截锥体平截头体(视点坐标)) 中 三维的点呗映射到一个立方体(NDC); 三个坐标分量分别映射到 ([-1,1]. x: [l,r] => [-1,1], y:[b,t] => [-1,1], z:[n,f] => [-1,1])

注意 eye coordinates 是定义在右手坐标中的, 但 NDC 是在左手坐标中的. 因此 源点摄像机在 eye space (视觉空间) 中看向 (z轴) 负向 而在 NDC中 看向(z轴) 正向. 由于 glFrustum() 对由进到远只接受正值, 我们需要在构造 GL_PROJECTION 矩阵时 对他们取反.

截锥体平截头体和标准化设备坐标
截锥体平截头体和标准化设备坐标

在OpenGL在, 在 eye space 中的三维点 会被投影到 near plane(projection plane) (近平面(投影平面)) 上, 下面这张图显示了如何将 (p(x_e,y_e,z_e)) 投影到 (p(x_p,y_p,z_p)) 上.

视锥体的顶视图

视锥体的顶视图

视锥体的侧视图

视锥体的侧视图

 

在顶视图中,(x_e),eye space 中的的x坐标, 通过相似三角形的比值的映射到 (x_p);

(frac{x_p}{x_e} = frac{-n}{z_e})
(x_p = frac{-n*x_e}{z_e} = frac{n*x_e}{-z_e})

在侧视图中,(y_p)也是通过相似的方法计算出来的;

(frac{y_p}{y_e} = frac{-n}{z_e})
(y_p = frac{-n*y_e}{z_e} = frac{n*y_e}{-z_e})

注意(x_p和y_p)都与(z_e)有关; 与(-z_e)成反比即除以(-z_e). 这是构造 GL_PROJECTION 矩阵的第一条线索. 在 eye coordinates 与 GL_PROJECTION 矩阵相乘完成变换后, clip coordinates 依然是 homogeneous coordinates(齐次坐标). 最终将其除以他的w分量来得到NDC.(更多细节OpenGL Transformation)

[egin{pmatrix}x_{clip}\y_{clip}\ z_{clip}\w_{clip}end{pmatrix} = M_{projection} imes egin{pmatrix}x_{eye}\y_{eye}\ z_{eye}\w_{eye}end{pmatrix}, egin{pmatrix}x_{ndc}\y_{ndc}\ z_{ndc}end{pmatrix} = egin{pmatrix}x_{clip}/w_{clip}\y_{ndc}/w_{clip}\ z_{ndc}/w_{clip}end{pmatrix} ]

因此,我们可以用 (-z_e) 作为 clip coordinates 的 w分量. 于是 GL_PROJECTION 矩阵的第四行变成了 ((0,0,-1,0).)

[egin{pmatrix}x_{c}\y_{c}\ z_{c}\w_{c}end{pmatrix} = egin{pmatrix} .& . & . & .\ .& . & . &. \ .& . & .&. \ 0& 0 & -1 & 0 end{pmatrix} imes egin{pmatrix}x_{e}\y_{e}\ z_{e}\w_{e}end{pmatrix}, herefore w_c = -z_e ]

接下来,我们通过线性关系把 (x_p,y_p) 映射到 NDC中的(x_n,y_n)上; ([l,r] => [-1,1] 和 [b,t] => [-1,1])

把$x_p$映射到$x_n$

(x_p)映射到(x_n)

[x_n = frac{1-(-1)}{r-l}*x_p + eta ]

[1 = frac{2r}{r-l}+ eta (用(r,1)代换(x_p,x_n)) ]

[eta = 1-frac{2r}{r-l} = frac{r-l}{r-l}-frac{2r}{r-l} = frac{r-l-2r}{r-l} = frac{-r-l}{r-l} = -frac{r+l}{r-l} ]

[ herefore x_n = frac{2x_p}{r-l} - frac{r+l}{r-l} ]

 

把$y_p$映射到$y_n$

(y_p)映射到(y_n)

[y_n = frac{1-(-1)}{t-b}*y_p + eta ]

[1 = frac{2t}{t-b}+ eta (用(t,1)代换(y_p,y_n)) ]

[eta = 1-frac{2t}{t-b} = frac{t-b}{t-b}-frac{2t}{t-b} = frac{t-b-2t}{t-b} = frac{-t-b}{t-b} = -frac{t+b}{t-b} ]

[ herefore y_n = frac{2y_p}{t-b} - frac{t+b}{t-b} ]

 

然后把(x_p和y_p变量代换到上述式子中)

(x_n = frac{2x_p}{r-l}-frac{r+l}{r-l} (x_p = frac{nxe}{-z_e}))

(x_n = frac{2*frac{nx_e}{-z_e}}{r-l} - frac{r+l}{r-l})

(x_n = frac{2n*x_e}{(r-l)(-z_e)} - frac{r+l}{r-l})

(x_n = frac{frac{2n}{r-l}*x_e}{-z_e} - frac{r+l}{r-l})

(x_n = frac{frac{2n}{r-l}*x_e}{-z_e} + frac{frac{r+l}{r-l}*z_e} {-z_e})

(x_n = (frac{2n}{r-l}*x_e+frac{r+l}{r-l}*z_e)/-z_e = x_c / -z_e)

同理可得

(y_n = (frac{2n}{t-b}*y_e+frac{t+b}{t-b}*z_e)/-z_e = y_c/-z_e)

注意我们使每个方程的两个项都除以(-z_e) 来表示perspective division(透视除法)((x_c/w_c,y_c/w_c)). 而且我们之前已经把(w_c设成了-z_e)了, 所以括号内的项已经是 clip coordinates 的 (x_c和y_c)了.

于是得到了GL_PROJECTION 矩阵的前两行

[egin{pmatrix}x_{c}\y_{c}\ z_{c}\w_{c}end{pmatrix} = egin{pmatrix} frac{2n}{r-l}& 0 & frac{r+l}{r-l} & 0\ 0& frac{2n}{t-b} & frac{t+b}{t-b} &0 \ .& . & .&. \ 0& 0 & -1 & 0 end{pmatrix} imes egin{pmatrix}x_{e}\y_{e}\ z_{e}\w_{e}end{pmatrix}]

现在我们只剩GL_PROJECTION 矩阵的第三行需要解出了. 但解(z_n)并不像其他坐标那样简单,因为 eye space 的(z_e)总是被投影到近平面的-n上. 但我们为了 clipping(裁剪)和 depth test(深度测试)需要保证z坐标的唯一性.,而且还要能够反投影(还原变换). 因为z并不依赖于x和y坐标,我们借用w分量 来找到 (z_n和z_e)之间的关系. 因此我们可以像下面这样来指定 GL_PROJECTION 矩阵的第三行.

[egin{pmatrix}x_{c}\y_{c}\ z_{c}\w_{c}end{pmatrix} = egin{pmatrix} frac{2n}{r-l}& 0 & frac{r+l}{r-l} & 0\ 0& frac{2n}{t-b} & frac{t+b}{t-b} &0 \ .& . & A&B \ 0& 0 & -1 & 0 end{pmatrix} imes egin{pmatrix}x_{e}\y_{e}\ z_{e}\w_{e}end{pmatrix}, z_n=z_c/w_c=frac{Az_e+Bw_e}{-z_e}]

在 eye space中, (w_e) 等于1. 因此等式变成
(z_n = frac{Az_e+B}{-z_e})

我们用((z_e,z_n))的关系来找到系数A和B;将((-n,-1)和(-f,1))回代到上式中
(left{egin{matrix} frac{-An+B}{n} = -1\ frac{-Af+B}{f} = 1 end{matrix} ight. ightarrow left{egin{matrix} -An+B = -n ;(1)\ -Af+B = f ;(2) end{matrix} ight.)

重写等式(1);

(B=An-n ; (1'))

将B带入(2);

(-Af +(An-n) = f ; (2))
$-(f-n)A = f + n ( )A = -frac{f+n}{f-n}$

把A回代到(1)中;

((frac{f+n}{f-n})n + B = -n ; (1))
(B = -n - (frac{f+n}{f-n})n = -(1+frac{f+n}{f-n})n = -(frac{f-n+f+n}{f-n})n = -frac{2fn}{f-n})

解出A和B后就可以得到(Z_e和Z_n)的关系;

(Z_n = frac{-frac{f+n}{f-n}z_e - frac{2fn}{f-n}}{-z_e} ; (3))

最终解出了整个 GL_PROJECTION 矩阵

[egin{pmatrix} frac{2n}{r-l}& 0 & frac{r+l}{r-l} & 0\ 0& frac{2n}{t-b} & frac{t+b}{t-b} &0 \ 0& 0 & frac{-{f+n}}{f-n}& frac{-2fn}{f-n} \ 0& 0 & -1 & 0 end{pmatrix} ]

这个投影矩阵是一种通用形式,如果可视平截头体是对称的,即(r=-l 和 t=-b),可以对其化简

[left{egin{matrix} r+l=0\ r-l=2r ; (宽) end{matrix} ight. , left{egin{matrix} t+b=0 \ t-b=2t ;(高) end{matrix} ight.]

[egin{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}]

在继续之前,请观察一下等式(3)中(z_e和z_n)的关系,你会发现他们并不是线性关系而是分式关系,这意味着在近平面会有非常高的精度而远平面的精度很低.如果([-n,-f])的范围很大,就会导致深度精度问题(z-fighting(深度冲突)); 在远平面(z_e)值小改动不会影响到(z_n)的值. 所以(n和f)的距离应该越小越好从而减少深度缓冲的精度问题.

深度缓冲精度的比较
深度缓冲精度的比较

Orthographic Projection(正射投影)

为正射投影构造 GL_PROJECTION 矩阵 比透视模式下的要简单的多

正射视锥和标准化设备坐标(NDC)
正射视锥和标准化设备坐标(NDC)

eye space 中所有的(x_e,y_e和z_e)分量都线性映射到 NDC. 我们只需要把长方体视锥缩放正方体,然后把它移动到原点. 让我们来通过线性关系来解出 GL_PROJECTION 里的元素吧.

把$x_e映射到x_n$

(x_e映射到x_n)

[x_n = frac{1-(-1)}{r-l}*x_e + eta ]

[1 = frac{2r}{r-l} + eta ; (用(r,1)代换(x_e,x_n)) ]

[eta = 1 - frac{2r}{r-l} = - frac{r+l}{r-l} ]

[ herefore x_n = frac{2}{r-l}*x_e - frac{r+l}{r-l} ]

 

把$y_e映射到y_n$

(y_e映射到y_n)

[y_n = frac{1-(-1)}{t-b}*y_e + eta ]

[1 = frac{2t}{t-b} + eta ; (用(t,1)代换(y_e,y_n)) ]

[eta = 1 - frac{2t}{t-b} = - frac{t+b}{t-b} ]

[ herefore y_n = frac{2}{t-b}*y_e - frac{t+b}{t-b} ]

 

把$z_e映射到z_n$

(z_e映射到z_n)

[z_n = frac{1-(-1)}{-f-(-n)}*z_e + eta ]

[1 = frac{2f}{f-n} + eta ; (用(-f,1)代换(z_e,z_n)) ]

[eta = 1 - frac{2f}{f-n} = - frac{f+n}{f-n} ]

[ herefore z_n = frac{-2}{f-n}*z_e - frac{f+n}{f-n} ]

 

由于在正射投影中不再需要w分量, GL_PROJECTION 矩阵的第四行 保留成(0,0,0,1), 于是可以得到 正射投影的 GL_PROJECTION 矩阵

[egin{pmatrix} frac{2}{r-l}& 0 & 0 & -frac{r+l}{r-l}\ 0& frac{2}{t-b} & 0 &-frac{t+b}{t-b} \ 0& 0 & frac{-2}{f-n}& -frac{f+n}{f-n} \ 0& 0 & 0 & 1 end{pmatrix} ]

同样的,如果视锥是对称的,即(r=-l 和 t=-b),可以对其化简

[left{egin{matrix} r+l=0\ r-l=2r ; (宽) end{matrix} ight. , left{egin{matrix} t+b=0 \ t-b=2t ;(高) end{matrix} ight.]

[egin{pmatrix} frac{1}{r}& 0 & 0 & 0\ 0& frac{1}{t} & 0 &0 \ 0& 0 & frac{-{2}}{f-n}& -frac{f+n}{f-n} \ 0& 0 & 0 & 1 end{pmatrix}]

原文地址:https://www.cnblogs.com/xxrlz/p/12506060.html