视图造型变换过程就是一个将顶点坐标从世界坐标系变换到视觉坐标系的过程。这里很重要的是对两个坐标系的认识。
世界坐标系,也称为全局坐标系。它是一个右手坐标系,可以认为该坐标系是固定不变的,在初始态下,其x轴为沿屏幕水平向右,y轴为沿屏幕垂直向上,z轴则为垂直屏幕面向外指向用户,当然,如果在程序中对视点进行了转换,就不能再认为是这样的了。
视觉坐标系,也称为局部坐标系。它是一个左手坐标系,该坐标系是可以活动的。在初始态下,其原点及x,y 轴分别与世界坐标系的原点及x,y 轴重合,而z 轴则正好相反,即为垂直屏幕面向内。
-
变换的顺序
先旋转后平移 先平移后旋转
图5-1-9 几何变换的顺序
当执行变换A和B时,如果按不同顺序执行时,结果往往会大不相同。例如变换A为旋转45度角,变换B为向x轴方向移动一个距离,不同的执行顺序产生不同的结果。
考察下面利用三个变换绘制顶点的代码:
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMultMatrixf(N); /* apply transformation N */
glMultMatrixf(M); /* apply transformation M */
glMultMatrixf(L); /* apply transformation L */
glBegin(GL_POINTS);
glVertex3f(v); /* draw transformed vertex v */
glEnd();
在这个过程中,GL_MODELVIEW状态相继引入了I(单位阵)、N、M、L矩阵。变换后的顶点为NMLv。因此,顶点的变换为N(M(Lv)),即是先作变换L,然后是变换M,最后才是N。这里,顶点v的实际变换顺序正好与指定的顺序相反。
-
造型变换
造型变换有三个基本的OpenGL命令:
glTranslate*() 平移
glRotate*() 旋转
glScale*() 缩放
OpenGL自动计算这三个命令的平移、旋转和缩放矩阵,这些命令的作用等价于调用glMultMatrix*(),参数设置为相应的矩阵。但前者比后者计算要快。
void glTranslate*(TYPE x,TYPE y,TYPE z);
该函数以平移矩阵乘当前矩阵。
x,y,z 指定沿世界坐标系x、y、z轴的平移量。
void glRotate*(TYPE angle,TYPE x,TYPE y,TYPE z);
该函数以旋转矩阵乘当前矩阵,其中:
angle 指定旋转的角度(以度为单位)。
x,y,z 指定旋转轴向量的三个分量(该向量位于世界坐标系中)。
void glScale*(TYPE x,TYPE y,TYPE z);
该函数以缩放矩阵乘当前矩阵,x、y、z 指定沿x、y和z轴的比例因子。
-
视图变换
视图变换改变视点的位置和方向,也就是改变视觉坐标系。在世界坐标系中,视点和物体的位置是一个相对的关系,对物体作一些平移、旋转变换,必定可以通过对视点作相应的平移、旋转变换来达到相同的视觉效果。完成视图变换可以有以下几种方法:
(1).利用一个或几个造型变换命令(即glTranslate*()和glRotate*())。由于这些命令也是在GL_MODELVIEW状态下执行的,所以较难和那些造型变换命令区分开来,移动视点的变换和移动物体的变换很容易混淆。为了便于建立清晰的物体和场景模型,可以认为只有其中一个变换在起作用,比如认为只有模型变换的话,那么glTranslate*()和glRotate*()将统一被视为对物体的变换。
(2).利用实用库函数gluLookAt()设置视觉坐标系。在实际的编程应用中,用户在完成场景的建模后,往往需要选择一个合适的视角或者不停地变换视角,以对场景作观察。实用库函数gluLookAt()就提供了这样的一个功能。
void gluLookAt(GLdouble eyex,GLdouble eyey,GLdouble eyez,
GLdouble centerx,GLdouble centery,GLdouble centerz,
GLdouble upx,GLdouble upy,GLdouble upz);
该函数定义一个视图矩阵,并与当前矩阵相乘。
eyex, eyey,eyez 指定视点的位置
centerx,centery,centerz 指定参考点的位置
upx,upy,upz 指定视点向上的方向
图5-1-10 函数gluLookAt()的设置
视点E、参考点C、视点向上的方向U实际上就是设定了一个视觉坐标系。这个视觉坐标系的原点是E,视线的方向(即z轴)是C-U,y轴方向就是视点向上的方向U,剩下的x轴方向就是向量((C-E)*U)。由于y轴和x轴是垂直的,所以也要求向量(C-U)和U互相垂直。这点在设置该函数参数时是必须注意的。
(3).创建封装旋转和平移命令的实用函数。有些应用需要用简便方法指定视图变换的定制函数。例如,在飞机飞行中指定滚动、俯仰和航向旋转角,或对环绕对象运动的照相机指定一种利用极坐标的变换。