OpenGL完全教程 第四章 矩阵变换_第1页
OpenGL完全教程 第四章 矩阵变换_第2页
OpenGL完全教程 第四章 矩阵变换_第3页
OpenGL完全教程 第四章 矩阵变换_第4页
OpenGL完全教程 第四章 矩阵变换_第5页
已阅读5页,还剩7页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、OpenGL 完全教程 第四章 矩阵变换作者:何咏 日期:2006-2-3 20:52:21 点击:3468如需转载本文,请声明作者及出处。第四章 矩阵变换通过前三章的学习,我们知道了如何使用OpenGL 在3D 空间中绘制基本图元,并把使用图元组成模型。然而,在我们绘制完一个物体或一个场景之后,我们总希望从多个角度观察这个物体,或者在场景中走动。这时,我们需要OpenGL 的另一个功能:变换。OpenGL 为我们提供了许多方面和类型的变换。你可以对投影方式进行变换,也可以对物体/模型 进行变换。你可以改变自己的位置和方向,也可以改变物体的大小和角度。学习本章内容,你将了解:OpenGL 中变

2、换的种类使用矩阵描述一个变换基本变换定义和使用自己的变换4.1 OpenGL中的变换变换(Transform,可以使3D 空间中的物体投影到2D 平面上。使用变换,你可以移动、旋转、缩放甚至弯曲一个物体。然而变换并没有直接修改顶点数据,取而代之,变换修改了坐标系。如果旋转一个坐标系,然后再在 旋转后的坐标系里绘图,绘制后的图形就好像被旋转了。在基本OpenGL 渲染流程中,将进行以下变换:视图变换 :用于指定观察者的位置和方向;模型视图变换:移动和变换场景中的模型;投影变换 :对视见空间进行裁剪和扭曲;视见区变换:对最终输出进行缩放。在一个场景中,我们希望改变观察者的位置和观察角度。用于改变观

3、察者方位和角度的变换,就是视图变换。默认情况下(没有执行任何变换时),观察者位于点(0,0,0,且视线朝着-Z方向。也就是说,只有在z<0的地方绘图,才有可能被观察到。此变换用于移动和旋转场景中的物体。使用模型视图变换完全可以代替视图变换。道理是很简单的:比如你想使用视图变换将观察者向-Z轴移动10个单位,此时场景中所有的物体都向+Z轴移动了10个单位。这跟你直接使用模型视图变换将场景中所有物体向+Z方向移动10个单位的效果是完全一样的。要把3D 场景投影到2D 平面上,就必须执行投影变换。投影变换有两种形式,即平行投影变换和透视投影变换。关于平行投影和透视投影,在第3中已进行了具体的介

4、绍,这里不在复述。现在要强调的是,投影也是一种变换,实现投影,本质上是对场景中所有物体进行特殊的变换,使得它们能够被画在一个平面上。比如透视投影变换会将场景中所有物体按照远近不同进行缩放和扭曲,使它们看起来具有立体感。这里又回到了第二章中的主题。视见区变换就是对投影后的2D 图象进行缩放和剪裁,使它能够被正确地显示在窗口上。你可以回到第二章了解视见区的具体概念。4.2 矩阵矩阵(M a t r ix 是那样的强大以至于几乎所有的变换都可以由矩阵来表达。矩阵是又n 行m 列的数组成的一个阵列(m、n1)。通过矩阵的乘法运算就可以运用各种变换。在OpenGL 中,统一使用大小为4×4的矩

5、阵。由于矩阵的运算法则和具体数学内容,和OpenGL 这一主题并没有太大关系(使用OpenGL 并不需要了解矩阵是怎样运算的,因为OpenGL 会帮你完成一切),所以这里不再介绍。但这并不代表这些数学知识是不重要的,灵活地运用矩阵,可以自己创造出许多OpenGL 没有提供的变换,并提高运算速度。你可以参看线性代数了解更多内容。 在OpenGL 进行变换操作时,会首先把顶点转换为1×4的矩阵(第1-3行分别存放顶点的x 、y 、z坐标,第4行存放w 坐标,即缩放因子,一般总为1.0),然后将这个点依次乘以模型视图变换矩阵、投影矩阵、视见区变换矩阵最后得到因出现在屏幕上的2D 屏幕坐标,

6、完成变换。幸运的是,你不需要任何数学基础,哪怕你对矩阵一无所知,也能顺利地完成这一流程。因为OpenGL 已经封装了高级函数,这使得你不用自己动手写矩阵,就能完成所有的基本变换。稍后就将介绍这些基本函数。4.3 基本变换对于每一种变换,OpenGL都有自己的函数用来生成这些变换的矩阵并应用它们。下面将一一介绍。这是本章最重要的内容。使用模型变换,你就可以完成物体的旋转和移动,并产生移动观察者的效果。这正是本章的主题。为了能够完成我们的示例,我们定义以下函数在原点绘制一个球体。以下函数涉及到二次曲面的内容,这是OpenGL 的另一个高级主题,我们将在今后的章节中 具体讲解,现在我们只用它来绘制球

7、体:pro c e du re DrawS p h ere(R:Sin gl e ; /R 代表球体的半径v ar S pO bj:GL UQu a d r ic O bj;b e gi nspObj:=gluNe wQu a d r ic;gluQu a d r icN orma l s(S pO bj ,GL U_SMOOT H ;gluQu a d r ic Or i en t a ti on(S pO bj ,GL U_O U T SI D E ;gluS p h ere(S pO bj , R , 50, 50 ;glu De l e t e Qu a d r ic (spObj ;

8、en d ;当我们调用Dra wS p h ere 时,会在原点绘制一个球体。现在我们想在点(0,10,0上绘制这个球体,就必须在绘制之前将坐标系沿+Y 方向平移10个单位。于是我们会写出这样的代码:/建立一个将坐标系沿+Y方向平移10个单位的矩阵:./用当前模型视图矩阵乘以这个矩阵:.DrawSphere(5;/绘制一个半径为5的球体但事实上,我们不需要这么麻烦。OpenGL为我们提供了这样一个函数:glTranslatef(x,y,z:Single;其中,x , y ,z 分别表示在X 、Y 、Z轴上平移的量。调用这个函数之后,OpenGL会自动生成一个平移矩阵,然后应用这个矩阵。因此,我

9、们可以这样写代码:glTranslatef(0,10,0;DrawSphere(5;这样就能在(0,10,0上绘制一个球体了。与平移类似,OpenGL也为我们提供了一个高级函数用于旋转物体:glRotatef(Angle,x,y,z:Single;这个函数将生成并应用一个将坐标系以向量(x , y ,z为轴,旋转an gl e 个角度的矩阵。如果我们想将一个球体以Y 轴自转50度,就可以调用:glRotatef(50,0,1,0;DrawSphere(5;缩放变换其实是将坐标系的x 、y 、z轴按不同的缩放因子展宽,从而实现缩放效果。函数glScalef(x,y,z:Single;把坐标系的X

10、 、Y 、Z轴分别缩放x 、y 、z倍。例如:glScalef(2,2,2;DrawSphere(5;将绘制一个半径为10的球体。使用变换时,我们应该注意的是,变换是叠加在上次变换的基础上的。也就是说,变换的效果会累积。每次调用变换函数时,会生成一个新的函数来乘以当前的模型视图矩阵,随后,新的矩阵将成为当前的模型变换矩阵,在下次执行变换时,会被新的矩阵相乘,因此作用效果将不断累积。举个例子就能很明白地说明这一点。例如,你想在(0,10,0)上绘制一个球体,完后在(10,0,0)上绘制另一个,得到如图4.3-1所示的图形: 图4.3-1你可能会写出如下代码:/沿Y 轴向上平移10个单位glTra

11、nslatef(0,10,0;/画第一个球体DrawSphere(5;/沿X 轴向左平移10个单位glTranslatef(10,0,0;/画第二个球体DrawSphere(5;然而,你不应该忘记,变换的作用效果是累积的。在绘制第二个球体时,由于此时坐标系已经向Y 轴移动了10个单位,再向X 方向移动10个单位之后,新的坐标系的原点应是绝对坐标系中的点(10,10。因此,上述程序将绘制出如图4.3-2所示的图形。 图4.3-2你可能会在绘制第二个球体之前调用gl Trans l a t ef(0,-10,0; 把坐标系往回移动10个单位。但这样会降低代码的可读性,还会给CPU 增加额外的运算。

12、这个时候,我们可以使用单位矩阵。我们可以调用gl Loa dId en tity (;函数将当前模型视图变换矩阵重置到初始状态,再进行新的绘制: procedure RenderScene(;beginglMatrixMode(GL_MODELVIEW;/沿Y 轴向上平移10个单位glTranslatef(0,10,0;/画第一个球体DrawSphere(5;/加载单位矩阵glLoadIdentity;/沿X 轴向上平移10个单位glTranslatef(10,0,0;/画第二个球体DrawSphere(5;end ;请看第一行代码。这里调用了glM a t r ixM o d e 函数。这个

13、函数的作用是通知OpenGL 我们将对模型视图变换矩阵进行操作。也就是要进行模型视图变换。glM a t r ixM o d e 可用参数如下:GL _PRO JEC T I O N :用于修改投影矩阵GL_MOD E L VIEW :用于修改模型视图变换矩阵如果每次变换前都把当前矩阵恢复到单位矩阵,也比较麻烦。更多时候,我们希望保存当前矩阵,执行一些变换之后,把当前矩阵恢复到上次保存时的状态。OpenGL 为我们提供了一个“矩阵堆栈”满足我们的这种要求。我们可以把当前矩阵压入堆栈中,然后执行一些变换,再弹出刚才压入的矩阵,从而把当前矩阵恢复到上次变换之前的状态。我们调用glPushMatri

14、x(;把当前矩阵压入矩阵堆栈,调用glPopMatrix(;弹出矩阵。我们还可以分别调用glGet(GL_MAX_MODELVIEW_STACK_DEPTH;glGet(GL_MAX_PROJECTION_STACK_DEPTH;来获取模型视图矩阵堆栈和投影矩阵堆栈的最大堆栈深度。一般情况下(在Wi n d o w s 平台上,模型视图的最大堆栈深度是32,而投影堆栈的最大深度是2。procedure RenderScene(;beginglMatrixMode(GL_MODELVIEW;/推入矩阵堆栈glPushMatrix;/沿Y 轴向上平移10个单位glTranslatef(0,10,0

15、;/画第一个球体DrawSphere(5;/恢复到上次保存时的状态glPopMatrix;/沿X 轴向左平移10个单位glTranslatef(10,0,0;/画第二个球体DrawSphere(5;无论是 2 维数组还是一维数组,都是按照列优先的顺序保存的。如图 4.4-1 所示。 图 4.4-1 矩阵定义完后,调用 glLoadMatrix(M; 可以用矩阵 M 替换当前矩阵,调用 glMultMatrix(M; 用当前矩阵乘以矩阵 M。 要说明的是,使用 glLoadMatrix 或 glMultMatrix 的速度没有 OpenGL 的高级变换函数快。所以如果不是高级变换 函数完成不了的

16、变换,就不要使用 glLoadMatrix 或者 glMultMatrix。 4.5 示例程序 这是一个经典的示例程序。它演示了太阳系中地月系与太阳之间的运动关系:月球饶地球转,整个地月系饶太阳 转,所有的星球都自转。这个例子很好地展示了矩阵变换的性质和矩阵堆栈的作用。为了增加视觉效果,本程序中加 入了光照渲染。 同时,我们也加入了纹理贴图,这是为了能看星球自转的景象。有关光照和纹理贴图的详细内容,我 们都将会在今后的章节中具体讲解。 以下是渲染过程的代码: procedure TfrmMain.RenderScene; begin glEnable(GL_CULL_FACE; glClear

17、(GL_COLOR_BUFFER_BIT OR GL_DEPTH_BUFFER_BIT; glLoadIdentity; glTranslatef(0,0,-110; glRotatef(yDeg,0,1,0; glRotatef(xDeg,1,0,1; RenderLights;/光照处理 /绘制太阳 glColor3ub(255,100,64; glPushMatrix; glRotate(SunSelfAng,0,1,0; DrawSphere(10; glPopMatrix; glPushMatrix;/推入当前矩阵 /绘制地月系 glRotatef(EarthCommonAng,0,1,0; glTranslatef(50,0,0; glPushMatrix;/绘制地球 glRotatef(EarthSelfAng,0,1,0; glColor3ub(2

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论