OpenGL 的空间变换(下):空间变换
通过本文的上篇 OpenGL 的空间变换(上):矩阵在空间几何中的应用 ,我们了解到矩阵的基础概念。并且掌握了矩阵在空间几何中的应用。接下来,我们将结合矩阵来了解 OpenGL 的空间变换。
在使用 OpenGL 的应用程序中,当我们指定了模型的顶点后,顶点依次会变换到不同的 OpenGL 空间中:
- 世界空间
- 模型空间(也称为对象空间)
- 视图空间(也称为视点空间、摄像机空间)
- 裁剪空间
- 标准设备坐标空间
- 窗口空间
在经过这一系列的空间变换之后,顶点才会被显示在屏幕上。
世界空间(World Space)
世界空间相对于其他坐标空间来说是固定不变的。所以,它也被用作空间变换的参考系。我们把它的坐标系称为世界坐标系。在没有特别说明的情况下,我们用来描述一个几何对象(点、向量或坐标)的数值数据,都是基于世界坐标系来设定的。
世界坐标系用矩阵来表示就是一个单位矩阵。
模型空间(Model Space)
模型空间,也称为对象空间。如果把世界空间比作现实世界的话,那么模型就好比一座房子或者房子里的一个人等等。假设以人的重心为原点,正面向前的方向为 z-轴,头顶的方向为 y-轴,左侧方向为 x-轴来构建一个坐标系。我们可以用这个坐标系来描述这个人自身的模型空间。这个坐标系也称为模型坐标系(或对象坐标系)。
模型坐标系并不是绝对的,如果以人的鼻尖为原点,头顶的方向改为 z-轴,正面向前的方向为 y-轴,右侧方向为 z-轴来构建其模型坐标系。这个模型坐标系同样可以用来描述这个人自身的模型空间。只不过用不同坐标系来描述时,描述出来的数据不一定相同。
一般来说,我们都是基于世界坐标系来描述模型坐标系的。在这种情况下,世界坐标系可以看作是模型坐标系的父坐标系:
其中,黑色的坐标系为世界坐标系;灰色的坐标系为模型坐标系。
模型变换(模型-世界变换)
默认情况下模型坐标系的原点位于世界坐标系的原点,并且坐标轴的方向与世界坐标系的坐标轴一致。我们可以通过一系列的缩放、旋转和平移,将模型以任意角度摆在任意位置上。这种情况下,模型上的顶点以及模型自身的坐标系都会相对世界坐标系发生了变化。
模型变换的实质就是将模型上的顶点在模型空间中的描述,转换为在世界空间中的描述。假设有一个模型坐标系表示为矩阵 M(基于世界坐标系来描述),一个顶点在该模型坐标系上的坐标表示为列向量 D。 那么,该顶点在世界坐标系中的坐标 D‘,有如下变换关系:M·D = D’。M 也称为模型矩阵。模型矩阵本质上是一系列缩放、旋转和平移矩阵的复合矩阵。
视图空间(View Space)
视图空间,也称为视点空间或摄像机空间。它是以摄像机(或者观察者)的角度来定义的一个空间。视图空间可以通过视图坐标系(也称为视点坐标系或摄像机坐标系)来描述。从摄像机的角度来看,视图坐标系 x-轴和 y-轴的正方向分别指向摄像机右方和上方,而 z-轴的负方向则指向摄像机的镜头指向。在透视投影中,摄像机位于视图坐标系的原点。所以 z 坐标为正的模型位于摄像机的背后,摄像机无法拍到它;而在正交投影中,摄像机被认为是位于在视图坐标系 z-轴正方向上的无穷远处。
透视投影中,摄像机与视图坐标系的位置关系图
正交投影中,摄像机与视图坐标系的位置关系图
与模型坐标系类似,我们一般也会基于世界坐标系来描述视图坐标系的。所以在这种情况下,世界坐标系可以看作是视图坐标系的父坐标系:
其中,黑色的坐标系为世界坐标系;蓝色的坐标系为视图坐标系。
视图变换(世界-视图变换)
默认情况下,视图坐标系位于世界坐标系的原点,并且坐标轴的方向与世界坐标系的坐标轴一致。我们可以通过一系列的缩放、旋转和平移,将摄像机以任意角度摆在任意位置,以方便我们的拍摄。当摄像机的位置、角度发生变化时,拍摄到的场景也会发生变化(也就是说空间中的模型相对于视图坐标系来说都发生了变化)。
视图变换的实质就是将某个顶点在世界空间中的描述,转换为在视图空间中的描述。假设有一个顶点在在世界坐标系中的坐标表示为列向量 D‘,一个视图坐标系表示为矩阵 V(基于世界坐标系来描述的),那么该顶点在视图坐标系 V 中的坐标 D,有如下变换关系:V·D = D’(道理和模型变换类似)。设 V 的逆矩阵为 V’,可以推导出变换关系 V‘·D‘ = D,V’ 也被我们称为视图矩阵。
模型视图矩阵(Model-View)
为了渲染一个模型,我们通常会先将它从模型空间变换到世界空间,然后再从世界空间变换到视图空间。这两个过程都有对应的变换矩阵:模型矩阵和视图矩阵。我们可以将这两个矩阵结合起来用一个复合矩阵来表示,这样的一个复合矩阵我们称为模型视图矩阵。通过模型视图矩阵,我们可以将模型上的顶点从模型空间直接变换到摄像机的视图空间。这样做有两个好处:
假如在世界空间中有许多模型,并且每个模型包含许多顶点。那么用一个复合矩阵把单个模型的所有顶点一次性变换到视图空间,比对所有顶点都做两次矩阵变换要高效得多。
因为一个空间可以是无限大的,所以将顶点从模型空间变换到世界空间中,所做的变换常常需要用到不同的精度去计算,这主要依赖于顶点离世界坐标的原点有多远。同样的,当我们再把顶点从世界空间变换到视图空间时,所做变换的精度也需要依赖于顶点到摄像机的距离有多远。对那些距离摄像机很近的顶点采用高精度是合适的,但对于距离摄像机很远的顶点同样采用高精度则会产生精度损失。想象一下,当模型与摄像机离得很近,并且两者又离世界坐标系的原点很远时,变换两次容易产生精度损失。用复合矩阵可以有效减少精度损失,提高结果的精确性。
裁剪空间(Clip Space)
不难理解,摄像机是无法同时拍摄到场景内所有的东西的。每个摄像机都会定义一个视景体,视景体决定了摄像机可以看到的空间。完全位于视景体内的模型将会被保留;完全位于视景体外的模型将被剔除;而与视景体裁剪平面相交的模型则会被裁减(即保留在视景体内的部分,剔除在视景体外的部分)。这个决定模型是保留还是剔除的过程,我们称之为裁剪。
视景体指的是空间中的一块区域,它由六个平面包围而成,这些平面被统称为裁剪平面。其中,有两块平面比较重要,它们都垂直都于摄像机的镜头指向,离摄像机比较近的那块被称为近裁剪平面,而离摄像机比较远的那块则被称为远裁剪平面。这两块平面决定了摄像机可以看到的深度范围。
透视投影的视景体示意图
正交投影的视景体示意图
对于不同的投影,其对应视景体的形状也有所不同。比如正交投影的视景体是一个长方体。而透视投影的视景体则是一个平截头体(像一个顶部被平截掉的金字塔)。如果我们直接使用视景体定义空间来进行裁剪,那么对不同的视景体就要采用不同的处理过程,特别是判断一个顶点是否处于一个平截头体内是比较麻烦的。因此,我们采用更加通用的裁剪空间来描述不同视景体所定义的空间。在裁剪空间中,我们可以用统一的方式来处理不同视景体的裁剪。
裁剪变换
将一个顶点从视图空间变换到裁剪空间的过程叫作裁剪变换,我们可以通过一个裁剪矩阵(也称为投影矩阵)来实现该变换。这里有个迷惑点,虽然裁剪矩阵也叫投影矩阵,但其实它并没有进行真正的投影工作,只是在为投影做准备工作,真正的投影工作发生在下一阶段的变换中。
透视投影的裁剪变换
透视投影的视景体为一个平截头体,我们可以通过下面几个参数来定义该平截头体:近裁剪平面、远裁剪平面在视图坐标系中的 z 坐标:near、far,近裁剪平面上下两条边在视图坐标系中的 y 坐标:top、bottom,还有近裁剪平面左右两条边在视图坐标系中的 x 坐标:left、right。
我们可以根据以上这些已知的参数,来给出透视投影的裁剪矩阵:
对于视图空间坐标为 (x, y, z, 1) 的顶点,经过透视裁剪矩阵变换后的结果如下:
该结果表示顶点在裁剪空间中的坐标。我们可能注意到了,此时顶点的 w 分量不再是 1 了,而是变换前 z 分量的取反结果。虽然我们在裁剪空间之前,就使用齐次坐标来表示空间中的几何对象(点、向量或坐标系),而且 w 分量一直是固定的:点位置的 w 分量为 1,向量的 w 分量表示为 0。在变换到裁剪空间之后,我们将赋予齐次坐标的 w 分量更加丰富的含义:作为一个临界值来判断一个经过裁剪变换后的顶点是否位于景视体内。如果变换后的坐标值 x、y、z 均在区间 [-w, w] 内,则表明该顶点在视景体内。否则,表明该顶点不在视景体内,将会被抛弃。
正交投影的裁剪变换
正交投影的视景体为一个长方体,平截头体的参数概念对于长方体来说同样适用。现在,我们根据这些已知的参数,来给出正交投影的裁剪矩阵:
对于视图空间坐标为 (x, y, z, 1) 的顶点,经过正交裁剪矩阵变换后的结果如下:
很明显,当变换前的 x 坐标在 right 与 left 之间时,变换后的 x 坐标在区间 [-1, 1] 内。对于 y 和 z 坐标同理可证。也就是说,如果变换后的坐标值 x、y 、z 均在区间 [-1, 1] 内,则表明该顶点在视景体内。这与透视投影中的判断规则是一致的。
裁剪空间使用同一套空间标准来描述不同视景体所定义的空间(通过执行不同的裁剪矩阵变换),把不同视景体的裁剪方式进行了统一。除此之外,裁剪空间也为后面真正的投影工作提供了方便。
标准设备坐标空间(NDC Space)
如今,显示屏幕的分辨率多不胜数,我们很难在每一种分辨率上都能把顶点显示在正确的位置上。
为了解决这个硬件适配的问题,我们在顶点被渲染到屏幕上之前,将其变换到标准设备坐标空间中。标准设备坐标空间采用一种无量纲的单位坐标来代替设备坐标,直到顶点被输出到屏幕时,单位坐标才会转换为具体的设备坐标。标准设备坐标系的 x、y、z 轴的范围被定义为 [-1, 1]。这样可以将应用程序与具体的显示设备隔离开来,应用程序无需关心显示设备的分辨率,增加了应用程序的可移植性。
对于正交投影,任意顶点在裁剪空间的坐标值 x、y 、z 均在区间 [-1, 1] 内,这种情况下无需任何变换,裁剪空间本身也是标准设备坐标空间。
对于透视投影,我们只需要对顶点在裁剪空间的坐标执行齐次坐标标准化,使其 w 分量变为 1。对应的 x、y、z 也将会缩小到范围 [-1, 1] 内。这种情况下,标准化的过程其实也是将顶点从裁剪空间坐标变换到到标准设备坐标空间的过程。
而且,因为标准化前的 w 分量与顶点到摄像机的距离成正比。所以,顶点离摄像机越远则 w 越大,w 越大则在标准化时 x、y、z 被缩得越小,这样就达到了透视收缩和投影的效果。所以,标准化的过程也是真正的投影过程。
窗口空间(Windows Space)
窗口空间,实际上指的就是显示屏幕或屏幕上某块区域(比如屏幕桌面上的窗口)的空间。窗口空间不仅取决于屏幕的分辨率或窗口的大小,也取决于窗口的位置。因为在标准设备坐标空间中,任意顶点的坐标值都在范围 [-1, 1] 内。所以,无论窗口空间是什么样的,我们都能很好的把顶点的标准设备坐标映射到具体的屏幕或屏幕的指定显示区域上。
OpenGL 的空间变换(下):空间变换的更多相关文章
- U3D屏幕空间到世界空间变换
using UnityEngine; using System.Collections; public class FPSCam : MonoBehaviour { Vector3 lastPos; ...
- OpenGL学习笔记(五)变换
目录 变换 向量 向量的运算 向量与标量运算 向量取反 向量加减 求向量长度 向量的单位化 向量相乘 点乘(Dot Product) 叉乘 矩阵 矩阵的加减 矩阵的数乘 矩阵相乘 矩阵与向量相乘 与单 ...
- windows phone (16) UI变换 下
原文:windows phone (16) UI变换 下 上一篇中说到四个变换类,都是比较简单的,这里要说到四个变换类,分别为: MatrixTransfrom矩阵变换,一句标准矩阵表示的变换 Tra ...
- opengl剪裁空间和视口空间中不遵从右手定则,而是遵从左手定则
opengl剪裁空间和视口空间中不遵从右手定则,而是遵从左手定则. 比如说要在视口空间判断一个三角形是否是正面朝向用户,就需要用左手定则而非右手定则.
- 为什么要进行傅立叶变换?傅立叶变换究竟有何意义?如何用Matlab实现快速傅立叶变换
写在最前面:本文是我阅读了多篇相关文章后对它们进行分析重组整合而得,绝大部分内容非我所原创.在此向多位原创作者致敬!!!一.傅立叶变换的由来关于傅立叶变换,无论是书本还是在网上可以很容易找到关于傅立叶 ...
- Talairach空间、MNI空间、Native空间、Stereotaxic空间
Talairach空间.MNI空间.Native空间.Stereotaxic空间 Native空间就是原始空间. 图像没有做任何变换时就是在原始空间.在这个空间中图像的维度.原点.voxel size ...
- Ora-01536:超出了表空间users的空间限量(转)
Ora-01536:超出了表空间users的空间限量(转) 正在开会,同事跑过来说数据库有问题,通讯程序不能入库,赶快获取一条insert into a values()语句后在toad工具中手动插入 ...
- 在Linux用户空间做内核空间做的事情
导读 我相信,Linux 最好也是最坏的事情,就是内核空间(kernel space)和用户空间(user space)之间的巨大差别.如果没有这个区别,Linux 可能也不会成为世界上影响力最大的操 ...
- Linux用户空间与内核空间(理解高端内存)
Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数 ...
- Linux用户空间与内核空间
源:http://blog.csdn.net/f22jay/article/details/7925531 Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针 ...
随机推荐
- 使用Three.js网页引擎创建酷炫的3D效果的标签墙
使用Three.js引擎(这是开源的webgl三维引擎,gitgub)进行一个简单应用. 做一个酷炫的3d效果的标签墙(已经放在我的博客首页,大屏幕可见), 去我的博客首页看看实际效果 www.son ...
- 1774: [Usaco2009 Dec]Toll 过路费
1774: [Usaco2009 Dec]Toll 过路费 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 263 Solved: 154[Submit ...
- swift -- 单例
方式一: (类似OC) class SingletonDispatch{ class var shareInstance : SingletonDispatch { //结构体 struct Stat ...
- git remote log error
使用git pull的时候收到以下信息: error: there are still refs under 'refs/remotes/origin/xxxx'From 10.1.25.57:yyy ...
- html中submit和button的区别(总结) [ 转自欣步同学 ]
html中submit和button的区别(总结) submit是button的一个特例,也是button的一种,它把提交这个动作自动集成了. 如果表单在点击提交按钮后需要用JS进行处理(包括输入验证 ...
- js提交表单错误:document.form.submit() is not a function
今天在写JS时,遇上这么个错误:"document.form.submit() is not a function",经过一番搜索,最终找到了修复方法. 这个错误一般是由于表单&l ...
- WINFROM 无边框窗体的移动和改变大小
因为去掉了边框 移动和调整大小都用不了了,可以调用WIN32的API来实现 1.定义必须常量 ; ; ; ; ; ; const int Guying_HTBOTTOMLEFT = 0x10; ; ...
- 部署在服务器中的WebService
1.继上篇之后,我把我的WebService部署在了比较小的Tomcat中,首先要把Jax-ws.jar架包放在lib里面 2.在我的WEB-INF里面增加了一个xml文件:sun-jaxws.xml ...
- Hive基础知识梳理
Hive简介 Hive是什么 Hive是构建在Hadoop之上的数据仓库平台. Hive是一个SQL解析引擎,将SQL转译成MapReduce程序并在Hadoop上运行. Hive是HDFS的一个文件 ...
- 20155206 2016-2017-2 《Java程序设计》第5周学习总结
20155206 2016-2017-2 <Java程序设计>第5周学习总结 教材学习内容总结 Java中所有错误都会被打包为对象,运用try.catch,可以在错误发生时显示友好的错误信 ...