OpenGL投影变换矩阵

前言

本文是对OpenGL Projection Matrix一文的中文翻译,初衷是因为自己学习OpenGL时,对投影变形的数学推导比较感兴趣,因此找到了该文章。而本文并不是对其一对一的翻译,其中会增加一些易于理解的内容。本文优先发布在我的 个人博客

正文

计算机显示器是2D平面的。如果3D场景通过OpenGL来渲染,那么就需要以2D图像的方式投影到计算机屏幕上。其中GL_PROJECTION矩阵就是用作投影变换。首先它将所有的顶点数据从观察(View)坐标变换到裁剪坐标;然后裁剪坐标还需要变换为归一化坐标(NDC,Normalized Device Coordinates),具体的方法就是裁剪坐标除以其w分量(默认是4分量向量,即x,y,z,w)。

因此需要注意的是裁剪(视锥剔除)和NDC变换都已经集成在了GL_PROJECTION矩阵中。而下面的内容主要就是描述如何从 left, right, bottom, top, near 和 far 这个6个值来构建投影矩阵。下图标明了这六个值:

这里要注意一下的是对于视锥剔除(也就是裁剪)是在裁剪坐标执行(即除以w分量)之前进行。在裁剪坐标中x、y、z分量都会尝试和w分量进行比较,如果其小于-w(注意这里是负数)或者大于w(这里是正数)都会认为这些顶点是无效的。即:

-w < x,y,z < w

然后OpenGl会在发生裁剪的地方重建多边形的边缘。

透视投影(Perspective Projection)

在透视投影中,处于视锥(上图左边,观察坐标)中的3D坐标会映射到立方体(上图右边,NDC坐标)中。x轴上[l,r]范围内的点映射到[-1,1]范围内;y轴上[b,t]范围内的点映射到[-1,1]范围内;z轴上[-n,-f]范围内的点映射到[-1,1]范围内。

注意:观察坐标使用的是右手坐标系,归一化坐标(NDC)使用的左手坐标系。

因此在观察空间中,处于原点的相机是望向-z轴方向,但是在NDC中却是望向+z轴方向。glFrustum()只接受为负数的near和far值(第二幅图中有标明),所以在构建GL_PROJECTION矩阵时需要对它们进行取反。

因此为了求得观察坐标到归一化坐标的变换矩阵,大致需要两步:首先需要将观察坐标(x_{e}, y_{e}, z_{e}) 投影到近平面上的点 (x_{p}, y_{p}, z_{p})。其次是将(x_{p}, y_{p}, z_{p})转换为(x_{n}, y_{n}, z_{n})

观察坐标到近平面的投影

在OpenGl中,观察空间中的3D坐标都会投影到近平面(near plane)中,下面两幅图展示了如何将观察空间中的某一个点(x_{e}, y_{e}, z_{e}) 投影到近平面上的点 (x_{p}, y_{p}, z_{p})

俯视图:

侧视图:

从视锥俯视图来看,x_{e} 映射到 x_{p}。通过相似三角形比例:

\frac{x_{p}}{x_{e}} = \frac{-n}{z_{e}} \rightarrow 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}} \rightarrow y_{p} = \frac{-n* y_{e}}{z_{e}} = \frac{n* y_{e}}{-z_{e}} (等式二)

也就是说,无论x_{e}y_{p}都是依赖于z_{e},即它们和-z_{e}相反。换句话说,它们都会除以-z_{e}。这是作为构造GL_PROJECTION矩阵非常重要的线索。在观察坐标在通过乘以GL_PROJECTION矩阵转换后,裁剪坐标依然是一个齐次坐标:

\begin{pmatrix} x_{clip}\\ y_{clip}\\ z_{clip}\\ w_{clip} \end{pmatrix} = M_{projection} \cdot \begin{pmatrix} x_{eye}\\ y_{eye}\\ z_{eye}\\ w_{eye} \end{pmatrix}

最后通过除以裁剪坐标的w分量变成归一化坐标(NDC):

\begin{pmatrix} x_{ndc}\\ y_{ndc}\\ z_{ndc}\\ \end{pmatrix} = \begin{pmatrix} x_{clip}/w_{clip}\\ y_{clip}/w_{clip}\\ z_{clip}/w_{clip}\\ \end{pmatrix}

因此我们可以设置裁剪坐标的w分量为-z_{e},GL_PROJECTION矩阵的第4行就变为(0, 0, -1, 0),并有如下的等式成立:

\begin{pmatrix} x_{clip}\\ y_{clip}\\ z_{clip}\\ w_{clip} \end{pmatrix} = \begin{pmatrix} . & . & . & . \\ . & . & . & . \\ . & . & . & . \\ 0 & 0 & -1 & 0 \end{pmatrix} \cdot \begin{pmatrix} x_{e}\\ y_{e}\\ z_{e}\\ w_{e} \end{pmatrix} , \rightarrow w_{c} = -z_{e}

下一步根据线性关系,我们将x_{p}, y_{p} 映射到 x_{n}, y_{n} (即NDC坐标):

[l, r] \Rightarrow [-1, 1],\ [b, t] \Rightarrow [-1, 1]

x_{p}x_{n}的映射

x_{p} 映射到 x_{n}

x_{n} = \frac{1 - (-1)}{r - l} \cdot x_{p} + \beta

然后用(r, 1) 来替换 (x_{p}, x_{n}),也就是在直线上取的特殊点:

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

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

将β带入等式一得到:

x_{n} = \frac{2}{r - l} \cdot x_{p} - \frac{r+l}{r - l} (等式三)

y_{p}y_{n}的映射

同理我们可以将y_{p} 映射到 y_{n}

最后得到的等式为:

y_{n} = \frac{2}{t - b} \cdot y_{p} - \frac{t+b}{t - b} (等式四)

然后我们将前面得到x_{p}(即等式一)带入到等式三中,我们的目的是要推导出和z_{e}的关系:

x_{n} = \frac{2*\frac{n*x_{e}}{-z_{e}}}{r-l} - \frac{r+l}{r-l}

x_{n} = \frac{2*n*x_{e}}{(r-l)*-z_{e}} - \frac{r+l}{r-l}

x_{n} = \frac{\frac{2*n*x_{e}}{r-l}}{-z_{e}} - \frac{r+l}{r-l}

x_{n} = \frac{\frac{2n}{r-l}}{-z_{e}} *x_{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} (等式五)

将前面得到y_{p}(即等式二)带入到等式四中,同样也是要推导出和z_{e}的关系:

y_{n} = \frac{2*\frac{n*y_{e}}{-z_{e}}}{t-b} - \frac{t+b}{t-b}

y_{n} = \frac{2*n*y_{e}}{(t-b)*-z_{e}} - \frac{t+b}{t-b}

y_{n} = \frac{\frac{2*n*y_{e}}{t-b}}{-z_{e}} - \frac{t+b}{t-b}

y_{n} = \frac{\frac{2n}{t-b}}{-z_{e}} *y_{e} + \frac{\frac{t+b}{t-b}* z_{e}}{-z_{e}}

y_{n} = (\frac{2n}{t-b} * y_{e} + \frac{t+b}{t-b} * z_{e})/-z_{e}(等式六)

上面我们为了进行透视划分(x_{c}/w_{c}, y_{c}/w_{c}),将等式的每一项都除以-z_{e},在前面我们有w_{c} = -z_{e}。因此对于等式五和等式六我们可以将括号内的设为x_{c}、y_{c},具体如下:

x_{c} = (\frac{2n}{r-l} * x_{e} + \frac{r+l}{r-l} * z_{e})

y_{c} = (\frac{2n}{t-b} * y_{e} + \frac{t+b}{t-b} * 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\\ \cdot & \cdot & \cdot & \cdot \\ 0 & 0 & -1 & 0 \end{pmatrix} \begin{pmatrix} x_{e}\\ y_{e}\\ z_{e}\\ w_{e} \end{pmatrix}

z_{e}z_{n}的映射

现在我们仅仅只有GL_PROJECTION矩阵3行(第1、2、4行)。寻找z_{n}(归一化坐标中的z值)相对于其他来说是有点不一样的,这是因为在观察坐标中的z_{e}总是投影在-n的近平面(near plane)上面(回想一下上面的那几张图)。但我们需要唯一的z值用于裁剪和深度测试,我们可以使用逆变换进行反投影。

我们知道z并不依赖于x和y的值,我们可以借用w分量来找到z_{n}、z_{e}之间的关系。因此我们可以对上面矩阵的第3行指定为如下形式:

\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\\ \cdot & \cdot & A & B \\ 0 & 0 & -1 & 0 \end{pmatrix} \begin{pmatrix} x_{e}\\ y_{e}\\ z_{e}\\ w_{e} \end{pmatrix}

我们可以得到下面的等式,等式的前半部分是我们前面提到的基础,对于归一化坐标而言,是用裁剪坐标除以其w分量:
z_{n} = z_{c}/w_{c} = \frac{A*z_{e} + B*w_{e}}{w_c} (等式七)

由于w_{c} = -z_{e},所以进一步替换上面的等式:

z_{n} = \frac{A*z_{e} + B*w_{e}}{-z_{e}}

在观察空间中w分量为1,也即是w_{e} = 1,因此等式可以进一步化简为:

z_{n} = \frac{A*z_{e} + B}{-z_{e}}

为了找到系数,A和B我们使用(z_{e}, z_{n})之间的关系:-n对应-1,即(-n,-1);-f对应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\\ -Af+B = f \end{matrix}\right.

得出B = An - n,然后将该等式带入到-Af+B = f中,得到:

A = -\frac{f+n}{f-n}

B = \frac{-2fn}{f-n}

透视投影矩阵

将A和B的值带入到矩阵中得到完整的透视投影矩阵:

\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,上面的矩阵可以化简为:

\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} (矩阵二)

同样的我们也可以根据等式七求出z_{n}的值

z_{n} = \frac{-1\frac{f+n}{f-n}*z_{e} - \frac{2fn}{f-n}}{-z_{e}} (等式八)

在我们继续后面的内容之前,我们先仔细看看上面z_{n}、z_{e}之间的关系。我们注意到它们是非线性关系的合理函数。这意味在近平面(near plane)上有着非常高的精度,但在远平面(far plane)上的精度却很低。如果[-n, -f]变大,那么就会导致深度(depth)的精度问题(z-fighting)。z_{e}在远平面附近小的变化不会影响到{z_{n}}值(这里可以简单地将z_{e} = -n代入到上面等式中)。n和f之间的距离应尽可能地短,以最大程度地减小深度缓冲区的精度问题。

正交投影(Orthographic Projection)

构建GL_PROJECTION的正投影矩阵要比透视投影要简单许多,观察坐标的x_{e}、y_{e}、z_{e}分量都是线性映射到归一化坐标中。因此我们只需要将长方形体积缩放到立方体中将其移动到原点即可。

现在我们可以根据线性关系求得GL_PROJECTION矩阵的各个元素。

x_ex_n的映射

x_{e}映射到x_{n}

x_{n} = \frac{1-(-1)}{r-l} * x_{e} + \beta

然后用点(r, +1) 替换 (x_{e}, x_{n})

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

将 β 代入到方程中得到:

x_{n} = \frac{2}{r-l} x_{e} - \frac{r+l}{r-l}

y_ey_n的映射

同理我们也可以求出y_{n}对应的函数表示:

y_{n} = \frac{1-(-1)}{t-b} * y_{e} + \beta

然后用点(t, +1) 替换 (y_{e}, y_{n})

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

将 β 代入到方程中得到:

y_{n} = \frac{2}{t-b} * y_{e} - \frac{t+b}{t-b}

z_ez_n的映射

这里需要注意的是它们都是取的负数。

z_{n} = \frac{1-(-1)}{-f-(-n)} * z_{e} + \beta

将(-f, 1)替换(z_{e}, z_{n})

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

将 β 代入到方程中得到:

z_{n} = \frac{-2}{f-n} z_{e} - \frac{f+n}{f-n}

正交投影矩阵

对于正交投影来说,w分量不是必须的。我们将GL_PROJECTION的中第4行设置为(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}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349