OpenGL投影矩阵
- 概述
- 透视投影
- 正交投影
概述
计算机显示器是一个2D平面。OpenGL渲染的3D场景必须以2D图像方式投影到计算机屏幕上。GL_PROJECTION矩阵用于该投影变换。首先,它将所有定点数据从观察坐标转换到裁减坐标。接着,这些裁减坐标通过除以w分量的方式转换到归一化设备坐标(NDC)。
因此,我们需要记住一点:裁减变换(视锥剔除)与NDC变换都保存在GL_PROJECTION矩阵中。下述章节描述如何从6个限定参数(左、右、下、上、近平面、远平面)构建投影矩阵。
注意,视锥剔除(裁减)在裁减坐标上执行,并且在除以wc之前。裁减坐标xc、yc、zc会与wc做比较检测。如果任一坐标小于-wc或大于wc,则该顶点将会抛弃。
接着,OpenGL重新构建那些裁减掉的多边形的边。
被视锥裁减的三角形
透视投影
OpenGL透视视锥体与NDC
在透视投影中,截棱锥体(观察坐标)中的3D点会被映射到立方体(NDC)中。x坐标的范围从[l,f]到[-1,1],y坐标的范围从[b,t]到[-1,1],z坐标的范围从[n,f]到[-1,1]。
注意,观察坐标为右手坐标系,NDC使用左右坐标系。也就是说,位于原点的照相机在观察坐标中看向-Z轴,而在NDC中看向+Z轴。因为glFrustum()只接收正的近平面与远平面距离值,我们需要在构建GL_PROJECTION矩阵时对他们取反。
OpenGL中,观察空间中的3D点被投影到近平面(投影平面)上。下图展示观察空间中的点(xe,ye,ze)如何投影到近平面上的点(xp,yp,zp)。
视锥体的俯视图
视锥体的侧视图
从视锥体的俯视图看出,使用相似三角形比率计算方式将观察空间的x坐标xe被映射到xp。
从视锥体的侧视图看出,yp也使用相同的方式计算出:
注意,xp与yp二者都依赖于ze,它们与-ze成反比例。也就是说,它们都被-ze除。这是构建GL_PROJECTION矩阵的第一点提示。在观察坐标通过与GL_PROJECTION矩阵相乘变换之后,裁减坐标依旧是其次坐标。它最终通过除以裁减坐标的w分量才变成归一化设备坐标(NDC)。(更详细描述参考OpenGL变换。)
,
因此,我们可以将裁减坐标的w分量设置为-ze。这样,GL_PROJECTION矩阵的第四行变为(0,0,-1,0)。
接着,我们通过线性关系将xp与yp映射到NDC中的xn与yn:[l,r]=>[-1,1],[b,t]=>[-1,1]。
映射xp到xn
映射yp到yn
然后,我们用上面的方程式替换xp与yp。
注意,我们为透视除法(xc/wc, yc/wc)将每个等式相被-ze整除。前面我们已经将wc设置为-ze,大括号中的项为裁减坐标中xc与yc。
从这个等式,我们可以发现GL_PROJECTION矩阵的第一与第二行。
现在,我们仅仅解决GL_PROJECTION矩阵的3行。由于观察空间中的ze总是投影到近平面上的-n点,zn的计算方法与其他坐标的计算方法有稍许不同。不过我们需要唯一的z值来进行裁剪与深度测试。此外,我们也会进行逆投影(逆变换)操作。因为,我们知道z并不依赖于x与y的值,我们借助w分量找寻zn与ze之间的关系。因此,我们可以像这样指定GL_PROJECTION矩阵的第三行:
在观察空间,we等于1。因此,等式变为:
为了计算系数A与B,我们使用(ze,zn)关系式(-n,-1)与(-f,1),且将它们带入到上述等式。
为了求解A与B,重写等式(1):
将等式(1')带入等式(2),然后求解A:
将A带入等式(1)中,求出B:
我们解出A与B。因此ze与zn的关系变为:
最后,我们解出GL_PROJECTION矩阵的所有元素。完整的投影矩阵为:
OpenGL透视投影矩阵
该投影矩阵为通用截面体。如果视锥体为对称的,即r=-l且t=-b,则矩阵可简化为:
在开始后面讲述之前,请回顾ze与zn之间的关系:等式(3)。你会注意到它是一个有理数方程且ze与zn并非线性关系。也就是说近平面具有非常高的精度,而远平面的精度很低。如果[-n,-f]的范围变得很大,会引起深度精度问题(深度冲突):远平面附近ze的小变化不会影响zn值。为了最小化深度缓存精度问题,n与f的距离应该尽可能小。
深度缓存精度比较
正交投影
正交椎体与归一化设备坐标(NDC)
构造正交投影的GL_PROJECTION矩阵比透视投影模式简单很多。
观察空间的xe、ye与ze分量都线性映射到NDC。我们只需将长方体缩放为正方体,然后移动它到原点。让我们使用线性关系推导出GL_PROJECTION中的所有元素。
映射xe到xn
映射ye到yn
映射ze到zn
因为对于正交投影并不需要w分量,GL_PROJECTION矩阵的第4行依旧为(0,0,0,1)。因此,正交投影完整的GL_PROJECTION矩阵为:
OpenGL正交投影矩阵
如果视锥体是对称的(r=-l且t=-b),它可以进一步简化。
英文原文:http://www.songho.ca/opengl/gl_projectionmatrix.html
OpenGL投影矩阵的更多相关文章
- OpenGL投影矩阵【转】
OpenGL投影矩阵 概述 透视投影 正交投影 概述 计算机显示器是一个2D平面.OpenGL渲染的3D场景必须以2D图像方式投影到计算机屏幕上.GL_PROJECTION矩阵用于该投影变换.首先,它 ...
- 关于Opengl投影矩阵
读 http://www.songho.ca/opengl/gl_projectionmatrix.html 0.投影矩阵的功能: 将眼睛空间中的坐标点 [图A的视椎体] 映射到 一个 ...
- 【脚下生根】之深度探索安卓OpenGL投影矩阵
世界变化真快,前段时间windows开发技术热还在如火如荼,web技术就开始来势汹汹,正当web呈现欣欣向荣之际,安卓小机器人,咬过一口的苹果,winPhone开发平台又如闪电般划破了混沌的web世界 ...
- OpenGL投影矩阵(Projection Matrix)构造方法
(翻译,图片也来自原文) 一.概述 绝大部分计算机的显示器是二维的(a 2D surface).在OpenGL中一个3D场景需要被投影到屏幕上成为一个2D图像(image).这称为投影变换(参见这或这 ...
- DX与OpenGL投影矩阵的区别
之前学习DX和OpenGL时到是知道一点,但是没仔细研究过,只是跟着教程抄个公式就过了,看双API引擎时发现转换时是个问题,必须搞懂,gamedev上找了个解释,希望用得上. https://www. ...
- OpenGL中两种计算投影矩阵的函数
OpenGL无意间同时看到两种创建投影矩阵的写法,可以说它们完成的是同样的功能,但写法完全不同,可以观摩一下什么叫做异曲同工之妙... 第一种: gltMakeShadowMatrix函数是重点 // ...
- Android OpenGL ES(六)----进入三维在代码中创建投影矩阵和旋转矩阵
我们如今准备好在代码中加入透视投影了. Android的Matrix类为它准备了两个方法------frustumM()和perspectiveM(). 不幸的是.frustumM()的个缺陷,它会影 ...
- [OpenGL](翻译+补充)投影矩阵的推导
1.简介 基本是翻译和补充 http://www.songho.ca/opengl/gl_projectionmatrix.html 计算机显示器是一个2D的平面,一个3D的场景要被OpenGL渲染必 ...
- OpenGL中投影矩阵的推导
本文主要是对红宝书(第八版)第五章中给出的透视投影矩阵和正交投影矩阵做一个简单推导.投影矩阵的目的是:原始点P(x,y,z)对应后投影点P'(x',y',z')满足x',y',z'∈[-1,1]. 一 ...
随机推荐
- jQuery专题
jQuery概述 ·为了简化JavaScript的开发,一些JavaScript库诞生了.JavaScript库封装了很多预定义的对象和实用函数.能帮助使用者建立有高难度交互的Web2.0特性的富客户 ...
- python进阶笔记 thread 和 threading模块学习
Python通过两个标准库thread和threading提供对线程的支持.thread提供了低级别的.原始的线程以及一个简单的锁.threading基于Java的线程模型设计.锁(Lock)和条件变 ...
- 更新UI界面的四种方法
一.runOnUiThread(new Runnable()): 二.Handler的sendMessage()系列: 三.Handler的post(): 四.View的post():
- java集合类的学习(二)
ArrayList,LinkedList,Vector都是List的实现类,前两都没有实现同步机制,Vector实现了同步机制.他们代码类似. ArrayList代表大小可变的数组,允许对元素进行快速 ...
- Java命名规则总结
1. JAVA源文件的命名 JAVA源文件名必须和源文件中所定义的类的类名相同. 2. Package的命名 Package名的第一部分应是小写ASCII字符,并且是顶级域名之一,通常是com.edu ...
- 【Python】实现简单循环
# -*- coding:utf-8 -*- #猜数字游戏 lucky_num = 6 count = 0 while count < 3: input_num = int(raw_input( ...
- 【Python】列表各种操作
# -*- coding:utf-8 -*- #列表操作name_list = ['zhangsan','lisi','wangwu'] #append 在最后插入name_list.append(& ...
- java并发编程学习笔记(一)初识并发原子性
1.并发的意义 现在是一个多核的时代,并发的存在意义就是为了能够充分利用多核计算机的优势,提高程序的运行效率: 2.并发的风险 竞争-----多个线程对内存数据数据进行读写操作时,对数据处理结果的一个 ...
- The file couldn't be opened because you don't have permission to view it
@import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...
- 安装和使用Visual Studio 2013并进行简单的单元测试
现在我正在安装visual studio 2013,我听说好多同学都在安装visual studio 2015,但是他好像只支持Win10吧,我就退而求其次安装了visual studio 2013. ...