OpenGL之坐标转换.docx
《OpenGL之坐标转换.docx》由会员分享,可在线阅读,更多相关《OpenGL之坐标转换.docx(15页珍藏版)》请在冰豆网上搜索。
OpenGL之坐标转换
OpenGL之坐标转换
下面这篇文章详细讲述了OpenGL里的坐标转换,清晰,明了。
但是其所谓的渲染管线只包括modelview转换和投影变换,我觉得不是这样的。
这只是从坐标角度吧。
比如什么顶点着色、光栅化、送至帧缓存都没有涉及到。
原文地址:
1. OpenGL 渲染管线
OpenGL渲染管线分为两大部分,模型观测变换(ModelViewTransformation)和投影变换(ProjectionTransformation)。
做个比喻,计算机图形开发就像我们照相一样,目的就是把真实的场景在一张照相纸上表现出来。
那么观测变换的过程就像是我们摆设相机的位置,选择好要照的物体,摆好物体的造型。
而投影变换就像相机把真实的三维场景显示在相纸上一样。
下面就分别详细的讲一下这两个过程。
1.1模型观测变换
让我们先来弄清楚OpenGL中的渲染管线。
管线是一个抽象的概念,之所以称之为管线是因为显卡在处理数据的时候是按照一个固定的顺序来的,而且严格按照这个顺序。
就像水从一根管子的一端流到另一端,这个顺序是不能打破的。
先来看看下面的图1:
图1OPENGL渲染管线
图中显示了OpenGL图形管线的主要部分,也是我们在进行图形编程的时候常常要用到的部分。
一个顶点数据从图的左上角(MC)进入管线,最后从图的右下角(DC)输出。
MC是ModelCoordinate的简写,表示模型坐标。
DC是DeviceCoordinate的简写,表示设备坐标。
当然DC有很多了,什么显示器,打印机等等。
这里DC我们就理解成常说的屏幕坐标好了。
MC当然就是3D坐标了(注意:
我说的3D坐标,而不是世界坐标),这个3D坐标就是模型坐标,也说成本地坐标(相对于世界坐标)。
MC要经过模型变换(ModelingTransformation)才变换到世界坐标,图2:
图2世界坐标系和模型坐标系
变换到世界坐标WC(WorldCoordinate)说简单点就是如何用世界坐标系来表示本地坐标系中的坐标。
为了讲得更清楚一些,这里举个2D的例子。
如图3:
图3世界坐标系和模型坐标系的计算
图中红色坐标系是世界坐标系WC,绿色的是模型坐标系MC。
现在有一个顶点,在模型坐标系中的坐标为(1,1),现在要把这个模型坐标转换到世界坐标中来表示。
从图中可以看出,点(1,1)在世界坐标系中的坐标为(3,4),现在我们来通过计算得到我们希望的结果。
首先我们要把模型坐标系MC在世界坐标系中表示出来,使用齐次坐标(HomogeneousCoordinate)可以表示为矩阵(注意,本教程中使用的矩阵都是以列向量组成):
其中,矩阵的第一列为MC中x轴在WC中的向量表示,第二列为MC中y轴WC中的向量表示,第三列为MC中的原点在WC中的坐标。
对齐次坐标系不了解的同学,请先学习游戏数学方面的知识。
有了这个模型变换矩阵后,用这个矩阵乘以在MC中表示的坐标就可以得到该坐标在世界坐标系中的坐标。
所以该矩阵和MC中的坐标(1,1)相乘有:
这也正是我们需要的结果。
现在让我们把相机坐标也加进去,相机坐标也称为观测坐标(ViewCoordinate),如图4和图5。
图4ModelView变换的三个坐标系
图5ModelView变换计算
来看看MC坐标中的点(1,1)如何在相机坐标中表示。
从图5中可以直接看出MC中的点(1,1)在相机坐标系VC中为(-2,-2)。
和上面同样的道理,我们可以写出相机坐标系VC在世界标系WC中可以表示为:
那么世界坐标系中的点转换为相机坐标系中的点我们就需求VC的逆矩阵:
那么世界坐标系WC中的点(3,4)在相机坐标系VC中坐标为:
上面的变换过程,就是可以把模型坐标变换为相机坐标。
在OpenGL中,当我们申明顶点的时候,有时候说的是世界坐标,这是因为初始化的时候世界坐标系、模型坐标系和相机坐标系是一样的,重合在一起的。
所以OpenGL中提供了模型观测变换,它是把模型坐标系直接转换为相机坐标系,如图4。
现在我们已经计算得到了VC-1和MC,如果把VC-1和MC相乘,就可以得到模型坐标在相机坐标中的表示。
为了得到模型坐标系中的坐标在相机坐标系中的表示,这就是OpenGL中的ModelView变换矩阵。
这也是ModelView变换的名字的由来,它是通过了上面两个步骤得到的。
那么这里,ModelView变换矩阵M为:
现在只要用上面的模型观测矩阵M乘以模型坐标系MC中的坐标就可以得到相机坐标系中的坐标了。
模型观测变换的关键就是要得到相机坐标系中的坐标,因为光照等计算都是在这个这个坐标系中完成的。
下面我们实际OpenGL程序中检查一下。
在程序中,为了计算方便,我们使用图6中的模型。
图6ModelView变换计算模型
根据图中的数据,我们分别可以写出对应MC和VC-1,从而求得观测变换矩阵M。
现在程序中用glGetFloatv()这个函数来获得当前矩阵数据来检查一下。
[cpp]viewplaincopy
1.float m[16] = {0}; //用来保存当前矩阵数据
2.glMatrixMode(GL_MODELVIEW);
3.glLoadIdentity();
4.glGetFloatv(GL_MODELVIEW_MATRIX, m);
5.//相机设置,View 变换
6.gluLookAt(0.0, 0.0, 5.0,
7.0.0, 0.0, 0.0,
8.0.0, 1.0, 0.0);
9.glGetFloatv(GL_MODELVIEW_MATRIX, m);
10.//投影设置
11.glMatrixMode(GL_PROJECTION);
12.glLoadIdentity();
13.glOrtho(-10,10,-10,10,-10,10);
14.glMatrixMode(GL_MODELVIEW);
15.//Modeling变换
16.glTranslatef(0, 0, -3);
17.glGetFloatv(GL_MODELVIEW_MATRIX, m);
18.glBegin(GL_POINTS);
19.glVertex3f(1,1,0);
20.glEnd();
如果在上面程序段中最后一个glGetFloatv(GL_MODELVIEW_MATRIX,m)处设定断点的话,就可以看到图7所显示的数据。
图7ModelView变换矩阵数据
到这里,整个ModelView变换就完成了。
通过ModelView变换后得到是相机坐标系内的坐标。
在这个坐标系内典型的计算就是法线了。
现在再来看看后面一个阶段。
//////////////////////////////////////////////////////////////
我的理解:
ModelView变换矩阵,就是完成从模型坐标到View坐标的转换,是坐标系之间的大变换。
注意,modelview既有model,也有view。
不只是一个model的矩阵。
只是对model的进行平移或旋转的函数为 glTranslatef等函数,称作模型变换!
它的坐标是基于模型本身的,即位于模型坐标系类,比如glTranslatef(0, 0, -3);的3个坐标值。
只是针对view进行设置的函数为 gluLookAt,它的坐标系是view坐标系,比如
1.gluLookAt(0.0, 0.0, 5.0,
2.0.0, 0.0, 0.0,
3.0.0, 1.0, 0.0);
它里面的坐标的原点位于相机坐标系的原点。
参看下面的投影变换。
/////////////////////////////////////////////////////////////////
1.2投影变换
先还是复习一下OpenGL的渲染管线。
图1中可以看到,在投影变换(ProjectionTransformation)中也分为两个部分,第一个部分是将上个阶段得到的坐标转换为平面坐标,第二个部分是将转换后的平面坐标在进行归一化并进行剪裁。
一般地,将三维坐标转换为平面坐标有两种投影方式:
正交投影(OrthogonalProjection)和透视投影(PerspectiveProjection)。
1.2.1 正交投影
正交投影很简单,如图8,对于三维空间中的坐标点和一个二维平面,要在对应的平面上投影,只需将非该平面上的点的坐标分量改为该平面上的坐标值,其余坐标不变。
图8正交投影
比如将点(1,1,5)正交投影到z=0的平面上,那么投影后的坐标为(1,1,0)。
在openGL中,设置正交投影可以使用函数:
[cpp]viewplaincopy
1.glOrtho (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar)
该函数可以设置正交投影的投影空间,在该空间以外的坐标点就不会被投影到投影平面上。
函数中的六个参数分是投影空间六个平面,如图9:
图9OpenGL正交投影空间和投影变换
在图9中,大的投影空间是根据这六个参数设置的投影空间,OpenGL会自动将该空间归一化,也就是将该空间或立方体转化为变长为1的正六面体投影空间,并且该证六面体的中心在相机坐标系的原点。
一旦设置使用glortho函数设置投影空间,OpenGL会生成投影矩阵。
这个矩阵的作用就是将坐标进行正交投影并且将投影后的坐标正规化(转换到-1到1之间)。
要注意的是,生成该矩阵的时候,OpenGL会把右手坐标系转换为左手坐标系。
原因很简单,右手坐标系的Z轴向平面外的,这样不符合我们的习惯。
该矩阵的矩阵推导这里就不详细说明了,不了解的同学可以参考游戏数学方面资料,这里只给出正交投影矩阵。
这个矩阵看来很复杂,其实计算很简单。
举个例子,现在设置了这样的正交投影空间glOrtho(-10,10,-10,10,-10,10),这是个正六面体空间,变长为10。
把这些参数带入上面的矩阵可以得到
现在还是在OpenGL程序中来检查一下。
在OpenGL程序中添加下面代码段:
[cpp]viewplaincopy
1.//投影设置
2.glMatrixMode(GL_PROJECTION);
3.glLoadIdentity();
4.glOrtho(-10,10,-10,10,-10,10);
5.glMatrixMode(GL_MODELVIEW);
6.glGetFloatv(GL_PROJECTION_MATRIX,m)
在glGetFloatv(GL_PROJECTION_MATRIX,m)处设定断点就可以看到图10中所显示的信息。
图10正交变换矩阵数据
1.2.2透视投影
透视投影和正交投影最大的区别就是透视投影具有远近感。
图11透视投影
透视投影采用了图11中的模型,这样的模型就是保证远的物体看起来小,近的物体看起来大。
在OpenGL中设置透视投影可以使用函数:
[cpp]viewplaincopy
1.void APIENTRY gluPerspective (GLdouble fovy, GLdouble aspect