最近在学习OpenGL矩阵相关的操作,发现其中的透视矩阵使用glm::perspective生成,其参数分别为相机的垂直视场角FOV(field of view)、屏幕宽高比、近平面Z值、远平面Z值。但是在Games101中推导出的透视矩阵P如下,并没有前面的两个参数
不禁疑问两者有什么不同?同时在调查解开这个疑问的时候,也加深了对于已有知识点的理解和认识。闲话多少,直接上货!
理解过程中最大的阻碍主要是一下两点
(1)透视矩阵并不是只有一种结果,上面的P是基于一定的前提的;
(2)glm::perspective并不像其字面意思,只是生成了透视矩阵,其中还进行了归一化,还有最重要的是进行了坐标系的转换,将坐标系转换到NDC(Normalized device coordinates),转换前的坐标系是右手坐标系,而NDC是左手坐标系!!不了解这点的话,死活得不到和glm::perspective一致的结果
以下针对这两个重点进行详解
(1)通常我们所说的近平面和远平面的Z值,都是正数,但是相机所朝向的却是-Z方向,所以在计算透视矩阵的时候,必须将其清晰化。Games101中的透视矩阵就是一开始设定其为负值,所以结果为
而如果一开始设定是正值,则其结果是不一样的,glm::perspective默认的就是这种情况 ,其结果如下
最大的不同点就是最后一行的那个1,其符号是不同的,如果没弄明白这点就得不到-1!
两种推导方式是相同的,只不过需要将近平面和远平面的Z值取负数,详细的可参考:关于透视投影变换及相关应用的详细推导-云社区-华为云
(2)glm::perspective不仅做了正规化将所有的坐标都正规化到 [-1, +1]立方的范围内,同时将坐标转换到左手系的NDC上!
那么如何进行正规化呢?以2D的场景为例,如下图
在旧坐标系下:A的坐标为(x,y),相机的视角范围在 [l,r] * [t,b] 内,现在要将A转换到新的坐标系下,同时将新坐标系的范围正规化到 [-1,+1]内
所以在新坐标系下:A的x坐标为 (x-(l+r)/2) / ((r-l)/2),分子分母同时乘上2,则为(2x-(l+r)) / (r-l),同理,y坐标为:(2y-(t+b)) / (t-b),z坐标为:(2z-(n+f)/(n-f))
于是可得正规化矩阵为
但是!!该矩阵的结果同样涉及到n和f的符号问题,上述的结果中n和f是带符号的,两者都是负数,n的绝对值更小,所以n>f!
然而glm中n和f是正数!!所以其结果有细微差别,如下
至于转换到NDC中的矩阵则简单得多,但是很多的博文都没有提到glm,所以这个点却是困扰了我最长时间的,一直得不到和glm::perspective一致的结果,其矩阵如下,是乘法中最左边的矩阵
最终,得到的矩阵结果如下
公式中的l、r、t、b可通过垂直视场角FOV(field of view)、屏幕宽高比(aspect_ratio)、近平面Z值求得,即
t(top) = near * tan(fov / 2)
b(bottom) = -t
r(right)= t * aspect_ratio (w/h = l / t 可以推出 l = aspect_ratio * t,其中 X 轴方向朝向)
l(left)= –r
代入最终的公式中,再对比glm::perspective的计算结果(如下图所示),完全一致!注意GLM中矩阵存储是列优先!
鸣谢:
上述部分图像来源和内容参照
GAMES101作业1:旋转与投影_mb5fe948249bc3d的技术博客_51CTO博客
关于OpenCL中各个坐标系及互相的转化,可阅读
OpenGL中的坐标变换、矩阵变换_gl_position_孙群的博客-CSDN博客
对于以上文章的作者,在此一并表示感谢!!