转自:https://blog.csdn.net/wangdingqiaoit/article/details/51589825

本文主要翻译并整理自 songho OpenGL Projection Matrix一文,这里对他的推导思路稍微进行了整理。

通过本节可以了解到

  • 透视投影矩阵的推导
  • 正交投影矩阵的 推导
  • 视口变换矩阵的推导
  • zFighting问题

投影变换

OpenGL最终的渲染设备是2D的,我们需要将3D表示的场景转换为最终的2D形式,前面使用模型变换和视变换将物体坐标转换到照相机坐标系后,需要进行投影变换,将坐标从相机—》裁剪坐标系,经过透视除法后,变换到规范化设备坐标系(NDC),最后进行视口变换后,3D坐标才变换到屏幕上的2D坐标,这个过程如下图所示:

投影变换通过指定视见体(viewing frustum)来决定场景中哪些物体将可能会呈现在屏幕上。在视见体中的物体会出现在投影平面上,而在视见体之外的物体不会出现在投影平面上。投影包括很多类型,OpenGL中主要考虑透视投影(perspective projection)和正交投影( orthographic projection)。两者之间存在很大的区别,如下图所示(图片来自Modern OpenGL):

上面的图中,红色和黄色球在视见体内,因而呈现在投影平面上,而绿色球在视见体外,没有在投影平面上成像。

指定视见体通过(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble nearVal, GLdouble farVal)6个参数来指定。注意在相机坐标系下,相机指向-z轴,nearVal和farVal表示的剪裁平面分别为:近裁剪平面z=−nearValz=−nearVal,以及远裁剪平面z=−farValz=−farVal。推导投影矩阵,就要利用这6个参数。在OpenGL中成像是在近裁剪平面上完成。

透视投影矩阵的推导

透视投影中,相机坐标系中点被映射到一个标准立方体中,即规范化设备坐标系中,其中[l,r]映射到[−1,1][l,r]映射到[−1,1],[b,t][b,t]映射到[-1,1]中,以及[n,f][n,f]被映射到[−1,1][−1,1],如下图所示: 

注意到上面的相机坐标系为右手系,而NDC中+z轴向内,为左手系。

我们的目标

求出投影矩阵的目标就是要找到一个透视投影矩阵P使得下式成立:

⎡⎣⎢⎢⎢xcyczcwc⎤⎦⎥⎥⎥=P∗⎡⎣⎢⎢⎢xeyezewe⎤⎦⎥⎥⎥[xcyczcwc]=P∗[xeyezewe]

⎡⎣⎢xnynzn⎤⎦⎥=⎡⎣⎢xc/wcyc/wczc/wc⎤⎦⎥[xnynzn]=[xc/wcyc/wczc/wc]

上面的除以wclipwclip过程被称为透视除法。要找到我们需要的矩阵P,我们需要利用两个关系:

  • 投影位置xpxp,ypyp和相机坐标系中点xexe,ye之间关系。投影后对于z分量都是ye之间关系。投影后对于z分量都是z_{p}=-nearVal$。
  • 利用xpxp,ypyp和xndc,yndcxndc,yndc关系求出xclip,yclipxclip,yclip。
  • 利用znzn与zeze关系得出zclipzclip

计算投影平面上的位置

投影时原先位于相机坐标系中的点p=(xe,ye,ze)p=(xe,ye,ze)投影到投影平面后,得到点p′=(xp,yp,−nearVal)p′=(xp,yp,−nearVal)。具体过程如下图所示: 

需要空间想象一下,可以得出左边的图是俯视图,右边是侧视图。 
利用三角形的相似性,通过俯视图可以计算得到: 
xpxe=−nzexpxe=−nze 
即:xp=xen−ze(1.1)(1.1)xp=xen−ze
同理通过侧视图可以得到: 
yp=yen−ze(1.2)(1.2)yp=yen−ze

由(1)(2)这个式子可以发现,他们都除以了−ze−ze这个量,并且与之成反比。这可以作为透视除法的一个线索,因此我们的矩阵P的形式如下:

⎡⎣⎢⎢⎢xcyczcwc⎤⎦⎥⎥⎥=⎡⎣⎢⎢⎢...0...0...−1...0⎤⎦⎥⎥⎥∗⎡⎣⎢⎢⎢xeyezewe⎤⎦⎥⎥⎥[xcyczcwc]=[............00−10]∗[xeyezewe]

也就是说wc=−zewc=−ze。 
下面利用投影点和规范化设备坐标的关系计算出矩阵P的前面两行。 
对于投影平面上xpxp满足[l,r][l,r]线性映射到[−1,1][−1,1]对于ypyp满足[b,t][b,t]线性映射到[−1,1][−1,1]。

其中xpxp的映射关系如下图所示:

则可以得到xpxp的线性关系: 
xn=2r−lxp+β(1.3)(1.3)xn=2r−lxp+β
将(r,1)带入上式得到: 
β=−r+lr−lβ=−r+lr−l 
带入式子3得到: 
xn=2r−lxp−r+lr−l(1.4)(1.4)xn=2r−lxp−r+lr−l
将式子1带入式子5得到:

xn=2xenr−l∗1−ze−r+lr−l=(2xenr−l+r+lr−l∗ze)−ze(1.5)(1.5)xn=2xenr−l∗1−ze−r+lr−l=(2xenr−l+r+lr−l∗ze)−ze

由式子6可以得到: 
xc=2nr−lxe+r+lr−l∗ze(1.6)(1.6)xc=2nr−lxe+r+lr−l∗ze

对于ypyp的映射关系如下: 
 
同理也可以计算得到:

yn=2yent−b∗1−ze−t+bt−b=(2yent−b+t+bt−b∗ze)−ze(1.7)(1.7)yn=2yent−b∗1−ze−t+bt−b=(2yent−b+t+bt−b∗ze)−ze

yc=2nt−bye+t+bt−b∗ze(1.8)(1.8)yc=2nt−bye+t+bt−b∗ze

由式子7和9可以得到矩阵P的前两行和第四行为:

⎡⎣⎢⎢⎢xcyczcwc⎤⎦⎥⎥⎥=⎡⎣⎢⎢⎢⎢⎢2nr−l0.002nt−b.0r+lr−lt+bt−b.−100.0⎤⎦⎥⎥⎥⎥⎥∗⎡⎣⎢⎢⎢xeyezewe⎤⎦⎥⎥⎥[xcyczcwc]=[2nr−l0r+lr−l002nt−bt+bt−b0....00−10]∗[xeyezewe]

由于zeze投影到平面时结果都为−n−n,因此寻找znzn与之前的x,y分量不太一样。我们知道znzn与x,y分量无关,因此上述矩阵P可以书写为:

⎡⎣⎢⎢⎢xcyczcwc⎤⎦⎥⎥⎥=⎡⎣⎢⎢⎢⎢⎢2nr−l00002nt−b00r+lr−lt+bt−bA−100B0⎤⎦⎥⎥⎥⎥⎥∗⎡⎣⎢⎢⎢xeyezewe⎤⎦⎥⎥⎥[xcyczcwc]=[2nr−l0r+lr−l002nt−bt+bt−b000AB00−10]∗[xeyezewe]

则有:zn=Aze+Bwe−zezn=Aze+Bwe−ze,由于相机坐标系中we=1we=1,则可以进一步书写为: 
zn=Aze+B−ze(1.9)(1.9)zn=Aze+B−ze

要求出系数A,B则,利用znzn与zeze的映射关系为:(-n,-1)和(-f,1),代入式子10得到: 
A=−f+nf−nA=−f+nf−n和B=−2fnf−nB=−2fnf−n, 
则znzn与zeze的关系式表示为: 
zn=−f+nf−nze−2fnf−n−ze(1.10)(1.10)zn=−f+nf−nze−2fnf−n−ze
将A,B代入矩阵P得到:

P=⎡⎣⎢⎢⎢⎢⎢⎢2nr−l00002nt−b00r+lr−lt+bt−b−(f+n)f−n−100−2fnf−n0⎤⎦⎥⎥⎥⎥⎥⎥(透视投影矩阵)(透视投影矩阵)P=[2nr−l0r+lr−l002nt−bt+bt−b000−(f+n)f−n−2fnf−n00−10]

上述矩阵时一般的视见体矩阵,如果视见体是对称的,即满足r=−l,t=−br=−l,t=−b,则矩阵P可以简化为:

P=⎡⎣⎢⎢⎢⎢⎢nr0000nt0000−(f+n)f−n−100−2fnf−n0⎤⎦⎥⎥⎥⎥⎥(简化的透视投影矩阵)(简化的透视投影矩阵)P=[nr0000nt0000−(f+n)f−n−2fnf−n00−10]

使用Fov指定的透视投影

另外一种经常使用 的方式是通过视角(Fov),宽高比(Aspect)来指定透视投影,例如旧版中函数gluPerspective,参数形式为:

API void gluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar);

其中指定fovy指定视角,aspect指定宽高比,zNear和zFar指定剪裁平面。fovy的理解如下图所示(来自opengl 投影): 

这些参数指定的是一个对称的视见体,如下图所示(图片来自Working with 3D Environment): 

由这些参数,可以得到: 
h=near∗tan(θ2)h=near∗tan(θ2) 
w=h∗aspectw=h∗aspect 
对应上述透视投影矩阵中: 
r=−l,r=wr=−l,r=w 
t=−b,t=ht=−b,t=h 
则得到透视投影矩阵为:

P=⎡⎣⎢⎢⎢⎢⎢⎢⎢cot(θ2)aspect0000cot(θ2)0000−(f+n)f−n−100−2fnf−n0⎤⎦⎥⎥⎥⎥⎥⎥⎥(Fov透视投影矩阵)(Fov透视投影矩阵)P=[cot(θ2)aspect0000cot(θ2)0000−(f+n)f−n−2fnf−n00−10]

正交投影矩阵的推导

相比于透视投影,正交投影矩阵的推导要简单些,如下图所示: 

对于正交投影,有xp=xe,yp=yexp=xe,yp=ye,因而可以直接利用xexe与xnxn的映射关系:[l,−1],[r,1][l,−1],[r,1],利用yeye和ynyn的映射关系:[b,−1],[t,1][b,−1],[t,1],以及zeze和znzn的映射关系:[−n,−1],[−f,1][−n,−1],[−f,1]。例如xexe与xnxn的映射关系表示为如下图所示:

利用[l,−1],[r,1][l,−1],[r,1]得到:

xn=2r−lxe−r+lr−l(2.1)(2.1)xn=2r−lxe−r+lr−l
同理可得到y,z分量的关系式为: 
yn=2t−bye−t+bt−b(2.2)(2.2)yn=2t−bye−t+bt−b
zn=−2f−nze−f+nf−n(2.3)(2.3)zn=−2f−nze−f+nf−n

对于正交投影而言,w成分是不必要的,保持为1即可,则所求投影矩阵第四行为(0,0,0,1),w保持为1,则NDC坐标和剪裁坐标相同,从而得到正交投影矩阵为:

O=⎡⎣⎢⎢⎢⎢⎢⎢2r−l00002t−b0000−2f−n0−r+lr−l−t+bt−b−f+nf−n1⎤⎦⎥⎥⎥⎥⎥⎥(正交投影矩阵)(正交投影矩阵)O=[2r−l00−r+lr−l02t−b0−t+bt−b00−2f−n−f+nf−n0001]

如果视见体是对称的,即满足r=−l,t=−br=−l,t=−b,则矩阵O可以简化为:

O=⎡⎣⎢⎢⎢⎢⎢1r00001t0000−2f−n000−f+nf−n1⎤⎦⎥⎥⎥⎥⎥(简化正交投影矩阵)(简化正交投影矩阵)O=[1r00001t0000−2f−n−f+nf−n0001]

利用平移和旋转推导正交投影矩阵

还可以看做把视见体的中心移动到规范视见体的中心即原点处,然后缩放视见体使得它的每条边长度都为2,进行这一过程的变换表示为:

O=S(2/(r−l),2/(t−b),2/(near−far))∗T(−(r+l)/2,−(t+b)/2,(f+n)/2)=⎡⎣⎢⎢⎢⎢⎢2r−l00002t−b00002n−f00001⎤⎦⎥⎥⎥⎥⎥∗⎡⎣⎢⎢⎢⎢⎢100001000010−r+l2−t+b2f+n21⎤⎦⎥⎥⎥⎥⎥=⎡⎣⎢⎢⎢⎢⎢⎢2r−l00002t−b0000−2f−n0−r+lr−l−t+bt−b−f+nf−n1⎤⎦⎥⎥⎥⎥⎥⎥O=S(2/(r−l),2/(t−b),2/(near−far))∗T(−(r+l)/2,−(t+b)/2,(f+n)/2)=[2r−l00002t−b00002n−f00001]∗[100−r+l2010−t+b2001f+n20001]=[2r−l00−r+lr−l02t−b0−t+bt−b00−2f−n−f+nf−n0001]

视口变换矩阵的推导

视变换是将NDC坐标转换为显示屏幕坐标的过程,如下图所示:

 
视口变化通过函数: 
glViewport(GLint sxsx , GLint sysy , GLsizei wsws , GLsizei hshs); 
glDepthRangef(GLclampf nsns , GLclampf fsfs );

两个函数来指定。其中(sxsx,sysy)表示窗口的左下角,nsns和 fsfs指定远近剪裁平面到屏幕坐标的映射关系。 
使用线性映射关系如下:

(−1,sx),(1,sx+ws)(x分量映射关系)(x分量映射关系)(−1,sx),(1,sx+ws)
(−1,sy),(1,sy+hs)(y分量映射关系)(y分量映射关系)(−1,sy),(1,sy+hs)
(−1,ns),(1,fs)(z分量映射关系)(z分量映射关系)(−1,ns),(1,fs)

求出线性映射函数为: 
xs=ws2xn+sx+ws2(3.1)(3.1)xs=ws2xn+sx+ws2
ys=hs2yn+sy+hs2(3.2)(3.2)ys=hs2yn+sy+hs2
zs=fs−ns2zn+ns+fs2(3.3)(3.3)zs=fs−ns2zn+ns+fs2
则由上述式子得到视口变换矩阵为:

viewPort=⎡⎣⎢⎢⎢⎢⎢ws20000hs20000fs−ns20sx+ws2sy+hs2ns+fs21⎤⎦⎥⎥⎥⎥⎥(视口变换矩阵)(视口变换矩阵)viewPort=[ws200sx+ws20hs20sy+hs200fs−ns2ns+fs20001]

Zfighting问题

回过头去看透视投影部分,znzn与zeze的关系式1.10: 
zn=−f+nf−nze−2fnf−n−ze(1.10)(1.10)zn=−f+nf−nze−2fnf−n−ze
这是一个非线性关系函数,作图如下: 
 
从左边图我们可以看到,在近裁剪平面附近znzn值变化比较大,精确度较好;而在远裁剪平面附近,有一段距离内,znzn近乎持平,精确度不好。当增大远近裁剪平面的范围[−n,−f][−n,−f]后,如右边图所示,我们看到在远裁剪平面附近,不同相机坐标zeze对应的znzn相同,精确度低的现象更为明显,这种深度的精确度引起的问题称之为zFighting。要尽量减小[-n,-f]的范围,以减轻zFighting问题。

本节参考资料

  1. songho OpenGL Projection Matrix
  2. GLSL Programming/Vertex Transformations
  3. glOrtho
  4. glFrustum
  5. gluPerspective

相关资源

1.The Perspective and Orthographic Projection Matrix 
2.OpenGL 101: Matrices - projection, view, model 
3.Calculating the gluPerspective matrix and other OpenGL matrix maths

OpenGL学习: 投影矩阵和视口变换矩阵(math-projection and viewport matrix)相关推荐

  1. OpenGL学习脚印: 投影矩阵和视口变换矩阵

    OpenGL中的视图可以利用照相机来进行比拟.产生目标的场景的变过过程类似于相机拍照.此步骤大概分为三个: 1)把相机固定在三角架上,并让它对准场景(视图变换) 2)对场景安排,使得各个物体在招片中的 ...

  2. 深入理解OpenGL之投影矩阵推导

    深入理解OpenGL之投影矩阵推导 OpenGL流水线中的投影矩阵以及坐标变换 OpenGL中,投影矩阵在Vertex shader中使用,用于变换顶点.一般和Model, View矩阵结合成MVP矩 ...

  3. OpenGL中投影矩阵(Projection Matrix)详解

    在游戏开发中,一个物体模型从它自身的坐标系转换至我们在屏幕上所见的样子,需要进行一系列的坐标变换以及其他的操作.该过程称为渲染管线.以OpenGL为例: 该过程在以前是被封装的,不能访问.但是现在我们 ...

  4. opengl 投影矩阵和深度测试

    写在前面  上一节我们使用AssImp加载了3d模型,效果已经令人激动了.但是绘制效率和场景真实感还存在不足,接下来我们还是要保持耐心,继续学习一些高级主题,等学完后面的高级主题,我们再次来改进我们加 ...

  5. OPENGL学习脚印

    OPENGL学习脚印 声明:本文转载自wangdingqiao的博客专栏–OPENGL学习脚印,仅仅对OPENGL学习脚印专栏的文章进行了整理,方便初学者按照顺序学习.同时也在这里感谢原创博主提供的学 ...

  6. 【脚下生根】之深度探索安卓OpenGL投影矩阵

    世界变化真快,前段时间windows开发技术热还在如火如荼,web技术就开始来势汹汹,正当web呈现欣欣向荣之际,安卓小机器人,咬过一口的苹果,winPhone开发平台又如闪电般划破了混沌的web世界 ...

  7. OpenGL投影矩阵与相机模型(Set Projection Matrix for Pinhole Camera Model)

    出处:http://oliver.zheng.blog.163.com/blog/static/1424115952013349234838/ (转载自"结冰的雪"百度空间) 目的 ...

  8. 从0开始的OpenGL学习(九)-FPS摄像机

    本文主要解决一个问题: 如何创建一个FPS摄像机? 1.引言 在前一章中,我们讨论了观察矩阵以及如何使用变换矩阵移动场景(虽然仅仅是往后移了一点点).本章中,我们要创建一个类似FPS的摄像机,它可以移 ...

  9. 【OpenGL-矩阵】投影矩阵(ProjectionMatrix)理解

    参考资料: https://jsantell.com/3d-projection/ http://www.songho.ca/opengl/gl_projectionmatrix.html 说明: 投 ...

最新文章

  1. 机器学习入门(01)— 感知机概念、实现、局限性以及多层感知机
  2. EZ430 Chronos 如何提高开发调试效率探讨
  3. Adb shell命令打电话测试4G
  4. Mac下制作Ubuntu的启动U盘
  5. 【HDU - 1266 】Reverse Number(模拟,数字分位数处理)
  6. 判断手机机型和浏览器内核
  7. windows访问mysql57_windows下 Mysql5.5升级5.7(其实就是安装了两个版本的mysql)
  8. vnc服务器注销了怎么登陆_vnc登录服务器常见问题
  9. 用PWA构建一个完全离线的网站
  10. 27. netstat
  11. zookeeper集群节点为什么是奇数个
  12. freemaker if 多个条件_小函数,大用处!巧用AND函数,避开IF函数嵌套
  13. 机器学习——k邻近算法(kNN)
  14. 零基础转行网络安全可以做什么工作
  15. springboot+vue开发小说网站及作品写作发布
  16. Excel中如何往上/往下全选(Mac)
  17. 利用OpenCV实现一个简单的实时人脸检测项目并显示FPS
  18. 图片转文字怎么转?这些方法我只告诉你
  19. Linux 安全设置
  20. 教你彻底屏蔽百度的广告与智能推荐

热门文章

  1. 解决Connection closed by remote host
  2. PLC控制电路设计中常用的电气符号和文字符号
  3. Android高级图片滚动控件,编写3D版的图片轮播器
  4. 高考学日语对报考计算机专业,高考日语受限的专业高考日语能报什么大学
  5. 计算机基础理论优秀书籍收集
  6. 最新贴吧云签系统源码+全开源版
  7. android旋转木马轮播图,vue实现旋转木马轮播
  8. 笔记本计算机虚拟技术,教大家联想笔记本电脑如何打开Intel 虚拟化技术
  9. 如何将视频转化为gif
  10. 分享Everything下载安装地址和操作 步骤