OpenGL投影矩阵推导

概述#

计算机显示器是一个2D平面。OpenGL渲染时必须将一个3D的场景投影在屏幕上成为一个2D的图片。GL_PROJECTION矩阵的就是用作这种投影变换。首先,其将所有的顶点数据从视觉坐标系(眼坐标系)转化到裁剪空间。然后,通过除以裁剪坐标系中的w分量,将转化后的裁剪坐标变换成标准设备坐标系(NDC)。

因此,我们必须记住GL_PROJECTION集成了裁剪和转化成标准设备坐标系的功能。下面的模块讲述了,如何从以下6个参数建立投影矩阵:left、right、bottom、top、near和far等边界值。

需要注意的是,裁剪空间中的平头截体的裁剪过程发生在,除以wc分量之前。将裁剪坐标 xc, yczcwc进行比较,如果其中任意一个值小于wc或者大于wc,那么这个顶点将会被裁剪掉,渲染时会丢弃这个顶点。

wc<xc,yc,zc<wc

当裁剪发生时,OpenGL会重新构建裁剪的边界和边线。

透视投影#

在透视投影时,裁剪截体是一个锥形的,然后被映射到标准设备坐标系中(一个立方体),NDC的x、y和z轴的范围都是从-1至+1。

注意,视觉坐标系是右手坐标系,而NDC使用的是左手坐标系,它们的z值方向是相反的,在视觉坐标系中,摄像机在原点,观察方向为z轴的负值方向,但是在NDC中,摄像机的观察方向却是z轴的正值方向。因此,glFrustum()的near和far参数必须大于0,所以在构建GL_PROJECTION矩阵时,我们必须对它们取反。

在 OpenGL 中,视觉空间中的 3D 点被投影到近平面(投影平面)上。下图显示了视觉空间中的一个点 (xe,yeze ) 是如何在近平面上投影到 (xp,yp,zp) 的。

从视雉体的顶视图,眼睛空间的 x 坐标, xe 映射到 xp ,这是通过使用相似三角形的相似计算的;

xpxe=nzexp=nxeze=nxeze

从视锥体的侧视图来看, yp 也以类似的方式计算;

ypye=nzeyp=nyeze=nyeze

注意 xpyp 都依赖于 ze; 它们与 ze 呈反比。换句话说,它们都被 ze 除以。这是构建GLPROJECTION矩阵的第一个线索。当视觉坐标系的坐标通过乘以GLPROJECTION矩阵转化成裁剪坐标,转化后的裁剪坐标任然是一个齐次坐标。最后通过除以w分量,转化成标准设备坐标系(NDC)。(可以在OpenGL变换中了解更多细节。)

(xclip yclip zclip wclip )=Mprojection(xeye yeye zeye weye ),(xndc yndc zndc )=(xclip /wclip yclip /wclip zclip /wclip )

因此,我们可以将剪辑坐标的 w 分量设置为 ze 并且,矩阵GL_PROJECTION 4 变为 (0,0,1,0)

(xcyczcwc)=(0010)(xeyezewe),wc=ze

接下来,我们将 xpyp 映射到具有线性关系的 NDCxnyn[1,r][1,1][b,t][1,1]

然后,我们将 xpyp 替换为上述方程。

请注意,我们使每个方程的两个项都可以被 ze 整除以进行透视除法 (xC/wC,yC/wC) 。我们之前将 wc 设置为 ze, 括号内的术语将变为剪辑坐标的 xcyc

通过这些等式,我们可以得出GL_PROJECTION矩阵的第一行和第二行。

(xcyczcwc)=(2nrl0r+lrl002ntbt+btb00010)(xeyezewe)

现在,我们只有第 3 行GL_PROJECTION矩阵要求解。找到 zn 与其他的有点不同,因为眼睛空间中的 ze 总是在近平面上投影到n 。但是我们需要唯一的 z 值来进行裁剪和深度测试。另外,我们应该能够取消投影 (逆变换) 它。 由于我们知道 z 不依赖于 xy 值,因此我们借用 w 分量来查找 znze 之间的关系。因此,我们可以像这样指定

(xcyczcwc)=(2nrl0r+lrl002ntbt+btb000AB0010)(xeyezewe)

zn=zc/wc=Aze+Bweze

在视觉空间中, we 等于 1 。因此,等式变为;

zn=Aze+Bze

为了找到系数 AB ,我们使用 (ze,zn) 关系; (n,1)(f,1) ,并将它们放入上面的等式中。

{An+Bn=1Af+Bf=1{An+B=nAf+B=f

为了求解AB的方程,重写方程(1)为B

B=Ann

将方程(1')替换为方程(2)中的B,然后求解A;

Af+(Ann)=f(fn)A=f+nA=f+nfn

A放入方程 (1) 中以查找B;

(f+nfn)n+B=nB=n(f+nfn)n=(1+f+nfn)n=(fn+f+nfn)n=2fnfn

我们找到了 AB 。因此, zezn 之间的关系变为;

zn=f+nfnze2fnfnze

最后,我们找到了GL_PROJECTION矩阵的所有条目。完整的投影矩阵是

(2nrl0r+lrl002ntbt+btb000(f+n)fn2fnfn0010)

此投影矩阵适用于一般视雉体。如果观看体积是对称的,即 r=lt=b ,则可以将其简化为;

{r+l=0rl=2r (width) ,{t+b=0tb=2t (height) 

(nr0000nt0000(f+n)fn2fnfn0010)

在我们继续之前,请再次看一下 zezn 之间的关系。您注意到它是一个有理函数,并且是 zezn 之间 的非线性关系。这意味着在近平面上具有非常高的精度,但在远端平面上的精度非常低。如果范围 [n,f] 变大, 则会导致深度精度问题(z-fighting); ze 在远平面周围的微小变化不会影响 zn 值。所以n和f的差值尽量小,以尽量减少深度缓冲区精度问题。

正交投影#

眼球空间中所有的 xeyeze 分量都线性映射到 NDC。我们只需要将矩形体积缩放到立方体,然后将其移动到原点。让我们找出使用线性关系GL_PROJECTION的元素。

因为正射投影不需要w分量,所以矩阵的第四行仍然为(0, 0, 0, 1)。因此完整的GL_PROJECTION正射投影矩阵如下:

(2rl00r+lrl02tb0t+btb002fnf+nfn0001)

如果观看体积对称,则可以进一步简化它, r=l 并且 t=b

{r+l=0rl=2r (width) ,{t+b=0tb=2t (height) 

结果

(1r00001t00002fnf+nfn0001)

相关参考#

OpenGL Projection Matrix (songho.ca)

3D图形学中的矩阵变换(三) | 王鹏飞 (pengfeixc.com)

观察空间到裁剪空间的投影矩阵推导 · JDreamHeart-知识汇总

posted @   straywriter  阅读(722)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
主题色彩