阅读:1349回复:0
OpenGL系列讲座(9)
5.5 视口变换
在前面几节内容中已相继提到过视口变换,这一节将针对OpenGL来讲述视口变换的原理 及其相关函数的用法。运用相机模拟方式,我们很容易理解视口变换就是类似于照片的放大 与缩小。在计算机图形学中,它的定义是将经过几何变换、投影变换和裁剪变换后的物体显 示于屏幕窗口内指定的区域内,这个区域通常为矩形,称为视口。OpenGL中相关函数是: glViewport(GLint x,GLint y,GLsizei width, GLsizei height); 这个函数定义一个视口。函数参数(x,y)是视口在屏幕窗口坐标系中的左下角点坐标,参 数width和height分别是视口的宽度和高度。缺省时,参数值即(0,0,winWidth,winHeight) 指的是屏幕窗口的实际尺寸大小。所有这些值都是以象素为单位,全为整型数。 注意,在实际应用中,视口的长宽比率总是等于视景体裁剪面的长宽比率。如果两个比 率不相等,那么投影后的图像显示于视口内时会发生变形,如图2-5-14所示。另外,屏幕窗 口的改变一般不明显影响视口的大小。因此,在调用这个函数时,最好实时检测窗口尺寸, 及时修正视口的大小,保证视口内的图像能随窗口的变化而变化,且不变形。 图2-5-14 视景体到视口的映射 5.6 矩阵堆栈 学过计算机的人也许都知道这个使用频率极高的名词——“堆栈”。固名思义,堆栈指的 是一个顶部打开底部封闭的柱状物体,通常用来存放常用的东西。这些东西从顶部依次放入, 但取出时也只能从顶部取出,即“先进后出,后进先出”。在计算机中,它常指在内存中开辟 的一块存放某些变量的连续区域。因此,OpenGL的矩阵堆栈指的就是内存中专门用来存放矩 阵数据的某块特殊区域。 实际上,在创建、装入、相乘模型变换和投影变换矩阵时,都已用到堆栈操作。一般说 来,矩阵堆栈常用于构造具有继承性的模型,即由一些简单目标构成的复杂模型。例如,一 辆自行车就是由两个轮子、一个三角架及其它一些零部件构成的。它的继承性表现在当自行 车往前走时,首先是前轮旋转,然后整个车身向前平移,接着是后轮旋转,然后整个车身向 前平移,如此进行下去,这样自行车就往前走了。 矩阵堆栈对复杂模型运动过程中的多个变换操作之间的联系与独立十分有利。因为所有 矩阵操作函数如glLoadMatrix()、glMultMatrix()、glLoadIdentity()等只处理当前矩阵或 堆栈顶部矩阵,这样堆栈中下面的其它矩阵就不受影响。堆栈操作函数有以下两个: void glPushMatrix(void); void glPopMatrix(void); 第一个函数表示将所有矩阵依次压入堆栈中,顶部矩阵是第二个矩阵的备份;压入的矩 阵数不能太多,否则出错。第二个函数表示弹出堆栈顶部的矩阵,令原第二个矩阵成为顶部 矩阵,接受当前操作,故原顶部矩阵被破坏;当堆栈中仅存一个矩阵时,不能进行弹出操作, 否则出错。由此看出,矩阵堆栈操作与压入矩阵的顺序刚好相反,编程时要特别注意矩阵操 作的顺序。 为了更好地理解这两个函数,我们可以形象地认为glPushMatrix()就是“记住自己在 哪”,glPopMatrix()就是“返回自己原来所在地”。请看下面一例 arm.c: 例 2-7 堆栈操作例程 arm.c #include "glos.h" #include <GL/gl.h> #include <GL/glu.h> #include <GL/glaux.h> void myinit(void); void drawPlane(void); void CALLBACK elbowAdd (void); void CALLBACK elbowSubtract (void); void CALLBACK shoulderAdd (void); void CALLBACK shoulderSubtract (void); void CALLBACK display(void); void CALLBACK myReshape(GLsizei w, GLsizei h); static int shoulder = 0, elbow = 0; void CALLBACK elbowAdd (void) { elbow = (elbow + 5) % 360; } void CALLBACK elbowSubtract (void) { elbow = (elbow - 5) % 360; } void CALLBACK shoulderAdd (void) { shoulder = (shoulder + 5) % 360; } void CALLBACK shoulderSubtract (void) { shoulder = (shoulder - 5) % 360; } void CALLBACK display(void) { glClear(GL_COLOR_BUFFER_BIT); glColor3f(0.0, 1.0, 1.0); glPushMatrix(); glTranslatef (-0.5, 0.0, 0.0); glRotatef ((GLfloat) shoulder, 0.0, 0.0, 1.0); glTranslatef (1.0, 0.0, 0.0); auxWireBox(2.0, 0.2, 0.5); glTranslatef (1.0, 0.0, 0.0); glRotatef ((GLfloat) elbow, 0.0, 0.0, 1.0); glTranslatef (0.8, 0.0, 0.0); auxWireBox(1.6, 0.2, 0.5); glPopMatrix(); glFlush(); } void myinit (void) { glShadeModel (GL_FLAT); } void CALLBACK myReshape(GLsizei w, GLsizei h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(65.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef (0.0, 0.0, -5.0); /* viewing transform */ } void main(void) { auxInitDisplayMode (AUX_SINGLE | AUX_RGBA); auxInitPosition (0, 0, 400, 400); auxInitWindow ("Composite Modeling Transformations"); 2 myinit (); auxKeyFunc (AUX_LEFT, shoulderSubtract); auxKeyFunc (AUX_RIGHT, shoulderAdd); auxKeyFunc (AUX_UP, elbowAdd); auxKeyFunc (AUX_DOWN, elbowSubtract); auxReshapeFunc (myReshape); auxMainLoop(display); } 从以上例程可以看出,复杂的机械手臂是由两个简单的长方体依据一定的继承关系构 成的,而这个继承关系是由矩阵堆栈的顺序决定的。 图2-5-15 简单机械手臂的符合运动 |
|
|