OpenGL模型视图变换,投影变换及视口变换

来源:互联网 发布:大数据测试用例 编辑:程序博客网 时间:2024/05/08 22:26
 

用照相机比喻从绘制三维物体到二维屏幕显示的变换过程:

1 视图(视点)变换:把相机放在三角架上,并对准场景;

2 模型变换:对场景进行安排,使物体在照片中位于你所希望的位置;

3 投影变换:选择相机的镜头,调整放大倍数;

4 视口变换:确定照片的大小。

在代码中,视图变换必须放在模型变换之前,但可以在绘图之前的任何时候执行投影变换和视口变换。视图变换函数gluLookAt()和模型变换函数glTranslate()等通常放在函数display()中,而视口变换函数glViewport()和投影函数glFrustum()等放在reshape()中。

display()函数里包含了视图变换、模型变换、以及绘制场景的函数。这样display()函数被重复调用,以绘制窗口的内容。display()潜在的重复使用性强调了执行视图和模型变换之前必须载入单位模型视图矩阵(即调用glLoadIdentity()函数)。reshape()函数包含了视口变换和投影变换,当窗口初次创建、移动或改变形状时,reshape()函数就会被重复调用。void glMatrixMode(GLenum mode)指定模型视图、投影或纹理矩阵是否被修改,mode值可为GL_MODELVIEW,    GL_PROJECTION GL_TEXTURE.

两种坐标系:为了使被显示的三维物体数字化,要在被显示的物体所在的空间中定义一个坐标系。这个坐标系的长度单位和坐标轴的方向要适合对被显示物体的描述,这个坐标系称为视觉坐标系。视觉坐标系是始终固定不变的。与此对应,还定义了局部坐标系概念。所谓局部坐标系,也就是坐标系以物体的中心为坐标原点,物体的旋转或平移等操作都是围绕局部坐标系进行的,这时,当整个物体模型进行旋转或平移等操作时,局部坐标系也执行相应的旋转或平移操作。

模型和视图变换

把视图变换和模型变换分裂开来是没有意义的。这两种变换可以互相替换,比如你不必移动相机(视图变换)来观察场景,而是通过移动场景(模型变换)。这也是把两种变换组合为模型视图矩阵的原因。

全局固定坐标系(视觉坐标系):

比如一个方体的中心位于原点,要对它进行如下变换:先绕Z轴旋转45度,再沿X轴正向平移3个单位,那么使用全局固定坐标系,代码如下:

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

glMultMatrix(T); //translation

glMultMatrix(R); //rotation

drawObject();

上述代码表示将物体先旋转后平移,但代码的书写顺序是相反的。这里顺便介绍一下glMultMatrix()函数,它表示把当前矩阵与参数表示的矩阵相乘并赋给当前矩阵,C是当前矩阵,glMultMatrix(M)后的当前矩阵为C*M。所以使用全局坐标系统考虑问题时,书写代码就必须考虑这种顺序问题了。

移动局部坐标系统(局部坐标系):

抛弃用于变换模型的全局固定坐标系统,而是想象一个绘图时固定到物体上的局部坐标系统,这时所有的操作都是相对与该局部系统进行的。于是矩阵的乘法很自然的貌似按照代码的顺序进行。

使用移动局部坐标系,上例的代码该写为:

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

glTranslatef(3.0, 0.0, 0.0); //translation

glRotatef(45, 0.0, 0.0, 1.0); //rotation

drawObject();

使用全局和局部系统比较:代码书写顺序基本一致,隐含的矩阵乘法顺序也是相同的,但采用移动局部系统更符合人们思考问题的习惯。实质不变:矩阵乘法以相反的顺序出现,如:TRv:对顶点v先执行旋转变换,后平移操作。

在程序中一般是在进行任何模型变换之前发布视图变换命令。这样,模型中的顶点首相根据模型变换变换到你所需要的方向,然后根据视图操作进行变换到合适的视角。我们可以选择默认的观察点位置(原点)和观察方向(Z轴的负方向)还可以用下述的两种方法作为视图命令:

1 使用一个或多个模型变换函数(glTranslate()等函数)。可以把这些变换的效果看成移动相机或在全局范围内移动场景。模型变换是在世界坐标系中进行的。缺省时,物体模型的中心定位在视觉坐标系的中心处。

常用的模型变换函数:

平移:glTranslate{fd}(TYPE x,TYPE y,TYPE z);

旋转:glRotate{fd}(TYPE angle,TYPE x,TYPE,y,TYPE z);

缩放:glScale{fd}(TYPE x,TYPE y,TYPE z);

2 使用gluLookAt()函数定义视线。该函数有三个变量,分别定义了视点的位置、相机瞄准方向的参考点以及相机的向上方向。该函数的原型为:

void gluLookAt(GLdouble eyex,GLdouble eyey,GLdouble eyez,GLdouble centerx,GLdouble centery,GLdouble upx,GLdouble upy,GLdouble upz);

该函数定义了视点矩阵,并用该矩阵乘以当前矩阵。eyex,eyey,eyez定义了视点的位置;centerxcenterycenterz变量指定了参考点的位置,该点通常为相机所瞄准的场景中心轴线上的点;upxupyupz变量指定了向上向量的方向。在默认情况下,相机位于原点,指向Z轴的负方向,以Y轴的正方向为朝上方向。

投影变换

投影变换就像为照相机选择镜头,可以认为这种变换的目的是确定视野或定义一个可视空间。可视空间有两个用途:确定投影类型(正投影还是透视投影)、定义裁剪范围。

在正投影下,可视空间是一个长方体,通俗地说是一个箱子。投影函数glOrtho(left, right, bottom, top, near, far).nearfar表示可视空间的近远裁剪平面分别距离视点的距离,如果为负,则表示位于视点之后,默认情况下,视点的前方是Z轴的负方向。在正投影下,视点的位置不影响投影的效果。如果是把二维图像投影到二维屏幕这种特殊情况,可以使用OpenGL工具库函数gluOrtho2D(left, right, bottom, top).

透视投影函数glFrustum(left, right, bottom, top, near, far)定义的可视空间是一个平截头体,只有nearfar是相对于观察点定义的,它们的值必须是正的。注意glFrustum函数并不需要定义一个对称的可视空间。平截头体在三维空间中有一个默认方向(Z轴负方向)。可以在投影矩阵上执行旋转或移动来改变方向,但是这种做法难度较大,应该避免。

 

尽管从概念上理解起来非常轻松,但glFrustum函数用起来并不是非常直观。OpenGL工具库函数gluPerspective(fovy, aspect, near, far)产生一个与调用glFrustum函数所产生的相同的可视空间,但可以用一种不同的方式来指定它。这个函数并不需要指定近裁剪平面的角的坐标,而是指定在Y方向的视野角度fove和纵横比(x/yaspect.注意gluPerspective仅限于沿视线方向同时在X轴和Y轴上对称的平截头体,这通常就是所需的。使用gluPerspective必须挑选合适的视野值,否则图像看上去就会变形。

视口变换

       视口变换类似Windows GDI 编程中的窗口-视口变换。在默认情况下,视口被设置成被打开的窗口的整个像素矩形。可以使用glViewport(x, y, width, height)定义视口大小。视口的纵横比一般要和可视空间的纵横比相同,否则图形就会变形。

 

      
原创粉丝点击