透视变换矩阵详解

原文: OpenGLProjection Matrix (songho.ca)

看这篇文章主要是因为对learnopengl深度测试这一章的些许疑惑,

为什么在片段着色器中,存储的每一个fragment的深度值并不是线性分布?体现在想要显示出场景里面所有物体的深度值,深度值是能在片段着色器的内建变量gl_FragCoord变量中查到,这个变量中存储的是z-buffer中该片段对应的坐标值(0-1.0)(当然是通过顶点插值出来的,这一部分OPENGL自动完成),那么它为什么是非线性的呢?主要是因为透视投影导致。下面这篇文章就能回答问题。

可以观察透视投影矩阵第四行是00-10,这其实就是在给透视除法做准备,经过透视矩阵或者说裁剪矩阵之后,透视坐标w会被赋予特殊的意义(-z,z是view空间内的该点z坐标);

这是实际上通过投影变换之后ze到zn的变换,可以看出来这个映射是非线性的。

另外注意投影矩阵会改变空间的旋向性:空间从右手坐标系变换到了左手坐标系。这意味着,离摄像机越远,z值将越大。

也就是坐标系会变成

从view坐标到NDC空间,其实是透视投影矩阵projection+ 透视除法,只用projection矩阵之后其实是在裁剪空间,这个时候的w分量是-z,在这一步可以进行裁剪,将xc,yc,zc与w进行比较,在这之外的点全部去除,并且构造新的点。这就完成了裁剪。

除以w,即透视除法,也就到了NDC空间。

关于深度测试:gl_FragCoord给的是0-1之间的值,是将NDC中的深度值,映射到了0-1.0之间(线性的),那么要还原这个值,就需要先还原到NDC空间

float z = depth * 2.0 - 1.0;

然后通过投影矩阵的逆变换还原

注意这个式子跟我之前推出来的不一样,差个负号,这是因为:要注意!从view坐标到NDC坐标会发生空间旋性变化,从右手系到了左手系,里面原本越大的深度值,换过去会变成一个越小的负数,所以应该乘一个负号,变成正值,这样就可以比较大小了。

正文开始

概述

计算机显示器是二维表面。OpenGL渲染的3D场景必须作为2D图像投影到计算机屏幕上。GL_PROJECTION矩阵用于此投影变换。首先,它将所有顶点数据从view坐标转换为裁剪坐标。然后,这些裁剪坐标也通过除以裁剪坐标的w分量而变换为归一化设备坐标(NDC)。

因此,我们必须记住,裁剪(截头体剔除)和NDC转换都集成到GL_PROJECTION矩阵中。以下部分描述了如何从6个参数构建投影矩阵;左、右、下、上、近和远边界值。

注意,截头体剔除(剪裁)是在剪裁坐标中执行的,就在除以wc之前。通过与wc的比较,测试了剪辑坐标xc、yc和zc。如果任何剪裁坐标小于-wc或大于wc,则顶点将被丢弃。

然后,OpenGL将重建发生剪切的多边形的边缘。

透视投影矩阵:

在透视投影中,截头棱锥体(view坐标)中的3D点被映射到立方体(NDC);

x坐标从[l,r]到[-1,1]的范围,y坐标从[b,t]到[-1,1],z坐标从[-n,-f]到[-1,1]。

注意,view坐标在右手坐标系中定义,但NDC使用左手坐标系。也就是说,原点的相机在view空间中沿-Z轴观看,但在NDC中沿+Z轴观看。由于glFrustum()只接受near和far的正值,因此我们需要在GL_PROJECTION矩阵的构造过程中对它们求反。

在OpenGL中,眼睛空间中的3D点被投影到近平面(投影平面)上。下图显示了眼睛空间中的点(xe,ye,ze)如何投影到近平面上的(xp,yp,zp)。

从截头体的俯视图来看,眼睛空间的x坐标xe被映射到xp,这是通过使用相似三角形的比率来计算的;

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

注意xp和yp都依赖于ze;它们与-ze成反比。换句话说,它们都被-ze除。这是构建GL_PROJECTION矩阵的第一条线索。在通过乘以GL_PROJECTION矩阵变换view坐标之后,裁剪坐标仍然是齐次坐标(http://www.songho.ca/math/homogeneous/homogeneous.html)。它最终成为归一化设备坐标(NDC)是通过除以裁剪坐标的w分量。(请参阅有关OpenGL转换的详细信息。http://www.songho.ca/opengl/gl_transform.html

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

接下来,我们用线性关系将xp和yp映射到NDC的xn和yn;[l,r]⇒ [-1,1]和[b,t]⇒ [-1, 1].

然后,我们将xp和yp代入上述方程。

注意,我们使每个方程的两个项都可以被-ze整除,以进行透视除法(xc/wc,yc/wc)。我们之前将wc设置为-ze,括号内的部分变为裁剪坐标的xc和yc。从这些方程中,我们可以找到GL_PROJECTION矩阵的第一行和第二行。

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

在view空间中,We等于1。因此,方程式变为:

为了找到系数A和B,我们使用(ze,zn)关系;(-n,-1)和(-f,1),并将它们放入上述方程中。

为了求解A和B的方程,重写B的方程(1);

将方程(1’)代入方程(2)中的B,然后求解A;

将A代入方程(1),求出B;

我们找到了A和B。因此,ze和zn之间的关系变为;

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

此投影矩阵用于一般平截头体。如果观看体积viewing volume是对称的,也就是r = -l, t = -b,则可以简化为:

在我们继续之前,请再次查看ze和zn之间的关系,方程式(3)。你注意到它是一个有理函数,是ze和zn之间的非线性关系。这意味着近平面的精度非常高,但远平面的精度很低。如果范围[-n,-f]越来越大,则会导致深度精度问题(z-fighting);远平面附近的ze的微小变化不影响zn值。n和f之间的距离应尽可能短,以最小化深度缓冲精度问题。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值