OpenGL投影矩阵

作者: 光棍狗没有可持续发展 | 来源:发表于2020-12-28 18:10 被阅读0次

概述

电脑显示器是2D平面,一个3D物体通过OpenGL渲染投影到2D显示器平面形成图像,GL_PROJECTION是OpenGL用于投影转换的矩阵。首先,转换3D物体的顶点数据从相机空间到裁剪空间,然后,通过除以W(齐次坐标)从裁剪空间再转换到标准设备空间(NDC)。
因此,我们需要意识到裁剪计算和NDC转换都是整合到了GL_PROJECTION矩阵里了。接下来的内容将探讨怎么构建投影矩阵,使用6个代表边界值的参数分别是:left,right,bottom,top,near , far(下面简写为l、r、b、t、n、f)。有一点注意一下,裁剪计算是在转换成NDC坐标之前发生的,即在除以w_c(齐次坐标)之前。通过裁剪坐标x_c,y_c,z_cw_c(这里的c下标表示裁剪空间)做对比,如果裁剪坐标小于-w_c或大于w_c将被裁减掉(-w_c < x_c,y_c,z_c < w_c),然后OpenGL会重新构建网格边缘。

一个被平头视锥裁剪的三角形

透视投影

在透视投影里,一个3D点在一个平头视锥体里面(相机空间)是要被映射到一个正方体(NDC空间)里的,x坐标的区间从[l,r]映射到[-1,1],y坐标的区间从[b,t]映射到[-1,1],z坐标的区间从[-n,-f]映射到[-1,1]。

投影视锥和标准设备空间(NDC)
注意一点,相机空间是使用右手坐标系统,但是NDC坐标空间使用左手坐标系统,这就是相机在原点沿着-Z方向看,但是在NDC空间是反过来的。因为glFrustum()仅仅接收正的near和far值,在构造GL_PROJECTION矩阵时我们需要给他们变成负值。
在OpenGL里,一个3D点在相机空间是被投影到near平面(projection plane 即下图的-n平面)。下面的图片展示一个点(x_e,y_e,z_e)在相机空间投影到near平面上的点(x_p,y_p,z_p)。
从上往下看视锥体
从侧面看视锥体
从上往下看视锥体,相机空间的x坐标点x_e投影到x_p,通过相似三角形原理得: x_p \over x_e = -n \over z_e => x_p = -n * x_e \over z_e = n*x_e \over -z_e
同理,从侧边看视锥体可得:y_p \over y_e = -n \over z_e => y_p = -n * y_e \over z_e = n * y_e \over -z_e
注意到,x_py_p都依赖z_e,他们和-z_e成相反的比例,也就是说他们都需要除以-z_e。这是非常有用的线索,用于构建GL_PROJECTION矩阵上,通过乘以GL_PROJECTION矩阵从相机空间转换到裁剪空间,坐标仍然是齐次坐标,除以裁剪空间的w最终变成规范化设备坐标空间(NDC)(更多资料请查阅OpenGL Transformation
投影矩阵乘以相机空间坐标得到裁剪空间坐标 裁剪空间坐标除以W得到NDC空间坐标
因此,我们可以设置裁剪空间的w坐标等于-z_e,然后投影矩阵GL_PROJECTION的第四行就变成(0,0,-1,0)。
根据矩阵乘法公式计算得,裁剪空间的w值等于相机空间的-z值

下一步,我们通过线性关系映射x_p(p下标表示投影空间未有裁剪的)和y_p(p下标表示投影空间未有裁剪的)到NDC坐标空间的x_n(n下标表示NDC空间)和y_n(n下标表示NDC空间),即:[l,r] => [-1,1] [b,t] => [-1,1]

为了直观点,可以把映射值连成的直线平移经过原点再平移回来 映射的最大点和最小点是已知的,代入上式可以求出平移值推导出映射公式 为了直观点,可以把映射值连成的直线平移经过原点再平移回来 映射的最大点和最小点是已知的,代入上式可以求出平移值推导出映射公式
然后使用上面的x_py_p两个等式替换到x_ny_n等式里:
求出NDC空间坐标,构造成除以w的形式 求出NDC空间坐标,构造成除以w的形式
注意到,我们故意把等式构造成除以-z_e的形式,即与透视除法(x_c/w_c,y_c/w_c)等同,只要把w_c看成-z_e上面等式括号的值就是裁剪空间的x_cy_c
从上面的等式总结,我们可以找到GL_PROJECTION矩阵第一行和第二行的构造方法
裁剪空间坐标等于投影矩阵乘以相机空间坐标
现在GL_PROJECTION矩阵只剩下第三行没构造好,构造z_n稍微不同于x_ny_n,因为在相机空间ze总是投影在near平面的-n点上,由于我们需要z值方便做深度测试和裁剪,另外也需要逆转换投影,我们知道z是不依赖于x或y的值,我们可以借用w表示z_nz_e之间的关系,因此我们可以构造GL_PROJECTION矩阵的第三行如下图: z是不依赖于x和y的,因此第三行矩阵x和y的值是0
在相机空间,w_e等于1,因此等式变成:
在相机空间的坐标w为1,因此可以略去
为了求出系数A和B,根据上面等式得出z_ez_n的关系,假设z_e等于-n(即近平面)那么z_n等于-1(即映射到NDC空间的坐标应为-1),z_e等于-f(即远平面)那么z_n等于1(映射到NDC空间的坐标应为1),带入上面等式:

由等式(1)推出:
把B等式代入等式(2):

把A等式代入等式(1):


得到A和B的等式可导出z_ez_n的关系等式: NDC空间坐标的z值和相机空间坐标的z值的关系等式 最终,可得到整个GL_PROJECTION矩阵: 完整的投影矩阵公式

当视锥体是个对称视锥体时,r = -l , t = -b 所以可以简化矩阵公式:


最后,再审视一下上面等式(3)表达的z_ez_n的关系。注意到这个等式函数是非线性关系的,意思是在近平面时会有非常高的精度,反过来在远平面会有非常低的精度。如果区间[-n,-f]是个很大的范围会导致出现深度(z值)精度的问题(z-fighting),z_e改变很小的值不会计算得到对应的z_n的值。所以,n和f之间的区间应该小点以减少z深度的精度问题。
z-fighting,可以看到右图跨度大,打圈处的f值由于精度问题都会对应1

正交投影

构造正交投影矩阵 GL_PROJECT比构造透视投影矩阵简单多了,只需要在相机空间里线性的映射x_e,y_e,z_e到NDC空间。我们只需要缩放长方体变成正方体,然后移到原点位置,让我们看看GL_PROJECTION的各元素之间的线性关系吧。


相机空间的x映射到NDC空间的x,同透视投用一样,可以先平移到原点再平移回来 注意,没有投影,NDC空间的x直接和相机空间的x成线性关系 相机空间的y映射到NDC空间的y,同透视投用一样,可以先平移到原点再平移回来 注意,没有投影,NDC空间的y直接和相机空间的y成线性关系 相机空间的z映射到NDC空间的z,可以先平移到原点再平移回来 注意,没有投影,NDC空间的z直接和相机空间的z成线性关系
因为正交投影矩阵是不需要用w计算z值的(z值也是线性关系直接求出来,透视投影需要用w来间接计算z_e和z_n的关系),矩阵GL_PROJECTION的第四行保留为(0,0,0,1),因此完整的正交投影矩阵为:
正交投影矩阵公式
当视锥体是对称长方体,即r = -l、t = -b,有:

坐标转换:模型坐标 -> 世界坐标 -> 相机坐标 -> 裁剪坐标 -> NDC坐标
原文链接

相关文章

  • OpenGL 投影矩阵

    OpenGL Projection MatrixOpenGL投影矩阵 概述 透视投影 正交投影 概述 计算机显示器...

  • OpenGL投影矩阵

    概述 电脑显示器是2D平面,一个3D物体通过OpenGL渲染投影到2D显示器平面形成图像,GL_PROJECTIO...

  • 【翻译】OpenGL 投影矩阵

    概述 电脑显示器是一个 2D 平面。一个通过 OpenGL 渲染的场景必须作为一个 2D 图像投影到电脑屏幕上。矩...

  • OpenGL矩阵堆栈处理

    为什么要使用矩阵堆栈? OpenGL在进行渲染的时候是通过模型视图矩阵和投影矩阵运算得到最终显示的坐标。 模型矩阵...

  • 3D变换:模型,视图,投影与Viewport

    OpenGL 使用列主序矩阵,即列矩阵,因此我们总是倒过来算的(左乘矩阵,变换效果是按从右向左的顺序进行): 投影...

  • OpenGL 学习系列---投影矩阵

    原文链接:https://glumes.com/post/opengl/opengl-tutorial-proje...

  • OpenGL投影变换矩阵

    前言 本文是对OpenGL Projection Matrix一文的中文翻译,初衷是因为自己学习OpenGL时,对...

  • OpenGL的渲染架构

    OpenGL的渲染架构 数据传递的三种方式 Attributes :顶点数据(x,y,z,w) / 投影矩阵,模型...

  • OpenGL 出栈压栈理解分析

    1.OpenGL压栈出栈作用概念 1.压栈出栈操作的是矩阵,一般分为模型视图矩阵和投影矩阵 2.出栈压栈是针对顶点...

  • OpenGL-向量/矩阵变换/投影

    基本概念 向量 从坐标原点指向这个位置点的一个向量(带箭头的线段) 向量在OpenGL里对应的数据类型M3DVec...

网友评论

    本文标题:OpenGL投影矩阵

    本文链接:https://www.haomeiwen.com/subject/vcbtbktx.html