【OpenGL Tutorial】Lesson2 移动与旋转

移动和旋转

我们上一个程序有些简单,我们不是要学3D编程吗?上一个程序看起来完全是2D啊,现在就让我们做一些有意思的事情——让图形旋转起来。

为了做到这一点,我们要理解OpenGL里面的移动和旋转。我们想象有一只鸟在场景里面飞行。它从原点开始,沿着z轴的负方向飞行,它可以移动、旋转、长大和缩小。当我们用glVertex将点传输给OpenGL的时候,OpenGL就用它来将鸟进行关联起来。所以,如果我们将这只鸟扩大两倍,并向右移动两个单元,从它的角度看,点(0,4,0)对它来说就是(1,2,0)。如果我们将这只鸟以x轴旋转90度,向上移动2个单元,点(0,0,-1)相对于它来说就是(0,-1,-2)。下面的图片就是上面的描述的情况,鸟是我通过橡皮泥捏成的。



此时你会想,“这个太愚蠢了,我为什么不直接画出已经移动好的点呢?”,不要着急,这节课后你将会明白为什么这么做。

我们将从上一节课的代码继续将其,有些注释将会删除。首先,我们将我们的“鸟”移动5个单位长度,用0作为z坐标的值,而不是原来用-5作为z坐标的值。我们调用glTranslatef来移动图形。

    glLoadIdentity(); //Reset the drawing perspective
    glTranslatef(0.0f, 0.0f, -5.0f); //Move forward 5 units
    
    glBegin(GL_QUADS);
    
    //Trapezoid
    glVertex3f(-0.7f, -1.5f, 0.0f);
    glVertex3f(0.7f, -1.5f, 0.0f);
    glVertex3f(0.4f, -0.5f, 0.0f);
    glVertex3f(-0.4f, -0.5f, 0.0f);
    
    glEnd();
    
    glBegin(GL_TRIANGLES);
    
    //Pentagon
    glVertex3f(0.5f, 0.5f, 0.0f);
    glVertex3f(1.5f, 0.5f, 0.0f);
    glVertex3f(0.5f, 1.0f, 0.0f);
    
    glVertex3f(0.5f, 1.0f, 0.0f);
    glVertex3f(1.5f, 0.5f, 0.0f);
    glVertex3f(1.5f, 1.0f, 0.0f);
    
    glVertex3f(0.5f, 1.0f, 0.0f);
    glVertex3f(1.5f, 1.0f, 0.0f);
    glVertex3f(1.0f, 1.5f, 0.0f);
    
    //Triangle
    glVertex3f(-0.5f, 0.5f, 0.0f);
    glVertex3f(-1.0f, 1.5f, 0.0f);
    glVertex3f(-1.5f, 0.5f, 0.0f);
    
    glEnd();

如果我们编译运行这段代码,结果与上一节课的结果是一样的,这也是我们所希望的结果。

让我们再来看看上节课遗留下来的glLoadIdentity函数,它的作用就是重置我们的“鸟”,让我们的“鸟”回到原点,朝向z的负方向。

现在,让我们利用一下移动这个功能,使得我们要对图形进行处理的时候都是以图形的中心为参考点的。

    glLoadIdentity(); //Reset the drawing perspective
    glTranslatef(0.0f, 0.0f, -5.0f); //Move forward 5 units
    
    glPushMatrix(); //Save the transformations performed thus far
    glTranslatef(0.0f, -1.0f, 0.0f); //Move to the center of the trapezoid
    
    glBegin(GL_QUADS);
    
    //Trapezoid
    glVertex3f(-0.7f, -0.5f, 0.0f);
    glVertex3f(0.7f, -0.5f, 0.0f);
    glVertex3f(0.4f, 0.5f, 0.0f);
    glVertex3f(-0.4f, 0.5f, 0.0f);
    
    glEnd();
    
    glPopMatrix(); //Undo the move to the center of the trapezoid
    glPushMatrix(); //Save the current state of transformations
    glTranslatef(1.0f, 1.0f, 0.0f); //Move to the center of the pentagon
    
    glBegin(GL_TRIANGLES);
    
    //Pentagon
    glVertex3f(-0.5f, -0.5f, 0.0f);
    glVertex3f(0.5f, -0.5f, 0.0f);
    glVertex3f(-0.5f, 0.0f, 0.0f);
    
    glVertex3f(-0.5f, 0.0f, 0.0f);
    glVertex3f(0.5f, -0.5f, 0.0f);
    glVertex3f(0.5f, 0.0f, 0.0f);
    
    glVertex3f(-0.5f, 0.0f, 0.0f);
    glVertex3f(0.5f, 0.0f, 0.0f);
    glVertex3f(0.0f, 0.5f, 0.0f);
    
    glEnd();
    
    glPopMatrix(); //Undo the move to the center of the pentagon
    glPushMatrix(); //Save the current state of transformations
    glTranslatef(-1.0f, 1.0f, 0.0f); //Move to the center of the triangle
    
    glBegin(GL_TRIANGLES);
    
    //Triangle
    glVertex3f(0.5f, -0.5f, 0.0f);
    glVertex3f(0.0f, 0.5f, 0.0f);
    glVertex3f(-0.5f, -0.5f, 0.0f);
    
    glEnd();
    
    glPopMatrix(); //Undo the move to the center of the triangle

如果我们再次编译运行这段代码,结果还是一样的。

在这段代码里面,我们用到了两个新的并且重要的函数:glPushMatrix和glPopMatrix,我们利用这两个函数保存和恢复我们“鸟”的状态。glPushMatrix保存它的状态,glPopMatrix恢复它的状态。值得注意的是,就像每一个glBegin后都有一个glEnd一样,每一个glPushMatrix后都要有一个glPopMatrix。我们利用glPushMatrix来保存“鸟”的状态,以至于不用重新将坐标移动到-5值。

其实我们可以保存多个“鸟”的状态,因为我们有一个状态栈。每一次我们调用glPushMatrix就是将状态放入栈顶,每次调用过来PopMatrix就是将状态弹出栈。OpenGL的状态栈最大可以存储32个状态。

glPopMatrix和glPushMatrix之所以这样命名是因为OpenGL通过矩阵来表示“鸟”的状态。现在,你无需担心矩阵是怎样工作的。

现在让我们改变一下我们的程序。让每一个图形都选择30度,五角星图形缩小到原来的70%。

float _angle = 30.0f;

//Draws the 3D scene
void drawScene() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    glMatrixMode(GL_MODELVIEW); //Switch to the drawing perspective
    glLoadIdentity(); //Reset the drawing perspective
    glTranslatef(0.0f, 0.0f, -5.0f); //Move forward 5 units
    
    glPushMatrix(); //Save the transformations performed thus far
    glTranslatef(0.0f, -1.0f, 0.0f); //Move to the center of the trapezoid
    glRotatef(_angle, 0.0f, 0.0f, 1.0f); //Rotate about the z-axis
    
    glBegin(GL_QUADS);
    
    //Trapezoid
    glVertex3f(-0.7f, -0.5f, 0.0f);
    glVertex3f(0.7f, -0.5f, 0.0f);
    glVertex3f(0.4f, 0.5f, 0.0f);
    glVertex3f(-0.4f, 0.5f, 0.0f);
    
    glEnd();
    
    glPopMatrix(); //Undo the move to the center of the trapezoid
    glPushMatrix(); //Save the current state of transformations
    glTranslatef(1.0f, 1.0f, 0.0f); //Move to the center of the pentagon
    glRotatef(_angle, 0.0f, 1.0f, 0.0f); //Rotate about the y-axis
    glScalef(0.7f, 0.7f, 0.7f); //Scale by 0.7 in the x, y, and z directions
    
    glBegin(GL_TRIANGLES);
    
    //Pentagon
    glVertex3f(-0.5f, -0.5f, 0.0f);
    glVertex3f(0.5f, -0.5f, 0.0f);
    glVertex3f(-0.5f, 0.0f, 0.0f);
    
    glVertex3f(-0.5f, 0.0f, 0.0f);
    glVertex3f(0.5f, -0.5f, 0.0f);
    glVertex3f(0.5f, 0.0f, 0.0f);
    
    glVertex3f(-0.5f, 0.0f, 0.0f);
    glVertex3f(0.5f, 0.0f, 0.0f);
    glVertex3f(0.0f, 0.5f, 0.0f);
    
    glEnd();
    
    glPopMatrix(); //Undo the move to the center of the pentagon
    glPushMatrix(); //Save the current state of transformations
    glTranslatef(-1.0f, 1.0f, 0.0f); //Move to the center of the triangle
    glRotatef(_angle, 1.0f, 2.0f, 3.0f); //Rotate about the the vector (1, 2, 3)
    
    glBegin(GL_TRIANGLES);
    
    //Triangle
    glVertex3f(0.5f, -0.5f, 0.0f);
    glVertex3f(0.0f, 0.5f, 0.0f);
    glVertex3f(-0.5f, -0.5f, 0.0f);
    
    glEnd();
    
    glPopMatrix(); //Undo the move to the center of the triangle

现在我们程序的运行结果是这样的:


我们引入了一个新的变量,_angle,它用来存储我们想要图形的旋转度数。我们这里用到了两个新的函数。我们调用glRotatef来旋转我们的“鸟”。我们调用glRotatef(_angle,0.0f,0.0f,1.0f)来将我们的“鸟”绕着z轴旋转_angle大小的角度,我们调用glRotatef(_angle,1.0f,2.0f,3.0f)来将我们的“鸟”绕着向量(1,2,3)旋转_angle大小的角度。我们调用glScalef(0.7f,0.7f,0.7f)将我们的“鸟”的x、y、z坐标缩小70%。如果我们调用glScalef(2.0f,1.0f,1.0f),它会将我们的“鸟”的x坐标扩大2倍。

非常重要的一点是:glTranslatef、glRotatef、glScalef不能在glBegin-glEnd块中调用。

现在让我们改变镜头的角度——向左偏转10度。

float _cameraAngle = 10.0f;

//Draws the 3D scene
void drawScene() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    glMatrixMode(GL_MODELVIEW); //Switch to the drawing perspective
    glLoadIdentity(); //Reset the drawing perspective
    glRotatef(-_cameraAngle, 0.0f, 1.0f, 0.0f); //Rotate the camera
    glTranslatef(0.0f, 0.0f, -5.0f); //Move forward 5 units

我们程序的运行结果将会是:


我们用了一个小技巧来改变镜头的角度——将镜头反方向旋转10度。这是一个很好用的技术,我们在3D编程中常常用到。

在我们进入下一小节之前,我们先来介绍一下glMatrixMode函数。我们调用glMatrixMode(GL_MODEL_VIEW),我们设置点的转化用于场景的转化。如果我们调用glMatrixMode(GL_PORJECTION),就像handleResize函数中实现的那样,我们设置点的转化用于除正常转化外的其他转化。让我们看看handleResize函数,我们转换成投影矩阵模式,调用glLoadIdentity()函数重置它的矩阵,然后调用gluPerspective函数。gluPerspective函数执行了一个怪异的转化——给我们的点一个“视图”。不要担心他们是怎么工作,你只需要知道我们用GL_PROJECTION来设置我们的视图,用GL_MODEL_VIEW来说做其他的事情。

计时器

现在,让我们利用GLUT计时器加入一些动画。计时器的基本思想就是我们希望某段代码经常性的运行,让我们每2毫秒增加2度。下面代码展示了我们怎么去做的。
void update(int value) {
    _angle += 2.0f;
    if (_angle > 360) {
        _angle -= 360;
    }
    
    glutPostRedisplay(); //Tell GLUT that the scene has changed
    
    //Tell GLUT to call update again in 25 milliseconds
    glutTimerFunc(25, update, 0);
}

这就是我们的更新函数。首先我们对角度增加2度。如果角度达到了360度,我们就减去360度。其实我们没有必要去这样做,但我们让角度尽量小,因为它涉及到浮点型的精度问题。这里我们不详细去介绍浮点精度。我们调用glutPostRedisplay函数去通知OpenGL我们的场景已经改变,让GLUT重新绘制。最后我们调用glutTimierFunc(25,update,0)让程序每25毫秒调用一次。
参数value是GLUT传递给update的值。它就是我们传递给glutTimerFunc函数的最后一个值,它永远都是0。我们不需要使用这个参数,所以我们不去管它。

glutTimerFunc(25, update, 0); //Add a timer

在我们的主函数中增加另一个函数glutTimerFunc,以至于当程序运行时每25毫秒调用一次update。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值