[翻译转载]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)

\[\begin{pmatrix}x_{clip}\\y_{clip}\\ z_{clip}\\w_{clip}\end{pmatrix} = M_{projection} \times \begin{pmatrix}x_{eye}\\y_{eye}\\ z_{eye}\\w_{eye}\end{pmatrix}, \begin{pmatrix}x_{ndc}\\y_{ndc}\\ z_{ndc}\end{pmatrix} = \begin{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).\)

\[\begin{pmatrix}x_{c}\\y_{c}\\ z_{c}\\w_{c}\end{pmatrix} = \begin{pmatrix} .& . & . & .\\ .& . & . &. \\ .& . & .&. \\ 0& 0 & -1 & 0 \end{pmatrix} \times \begin{pmatrix}x_{e}\\y_{e}\\ z_{e}\\w_{e}\end{pmatrix}, \therefore 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 + \beta \]

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

\[\beta = 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} \]

\[\therefore 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 + \beta \]

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

\[\beta = 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} \]

\[\therefore 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 矩阵的前两行

\[\begin{pmatrix}x_{c}\\y_{c}\\ z_{c}\\w_{c}\end{pmatrix} = \begin{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} \times \begin{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 矩阵的第三行.

\[\begin{pmatrix}x_{c}\\y_{c}\\ z_{c}\\w_{c}\end{pmatrix} = \begin{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} \times \begin{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\{\begin{matrix} \frac{-An+B}{n} = -1\\ \frac{-Af+B}{f} = 1 \end{matrix}\right. \rightarrow \left\{\begin{matrix} -An+B = -n \;(1)\\ -Af+B = f \;(2) \end{matrix}\right.\)

重写等式(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 矩阵

\[\begin{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\{\begin{matrix} r+l=0\\ r-l=2r \; (宽) \end{matrix}\right. , \left\{\begin{matrix} t+b=0 \\ t-b=2t \;(高) \end{matrix}\right.\]

\[\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}\]

在继续之前,请观察一下等式(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 + \beta \]

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

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

\[\therefore 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 + \beta \]

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

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

\[\therefore 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 + \beta \]

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

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

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

 

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

\[\begin{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\{\begin{matrix} r+l=0\\ r-l=2r \; (宽) \end{matrix}\right. , \left\{\begin{matrix} t+b=0 \\ t-b=2t \;(高) \end{matrix}\right.\]

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

posted @ 2020-03-16 19:43  新新人類  阅读(823)  评论(0编辑  收藏  举报