您的位置:首页 > 运维架构

在BlackBerry上使用OpenGL绘图(五):动画的产生

2011-09-24 21:25 288 查看

5 动画的产生

上一节我们讨论了3D模型的建立,所建立的3D模型是静态的,不像原来的OpenGLDemo中的立方体一样可以旋转。为了让3D模型运动起来,我们需要对程序做一些修改。不过,在开始之前,我们首先需要简单认识一下动画的产生。



用非专业的语言描述,动画是由静态图片的不断更新显示产生的。因为人的眼睛有视觉暂留,刚看见的图像在很短的一段时间内不会消失,只要静态图片更新的足够快,观看的人就会认为所看见是连续的动态图片。一般的要求是一秒内显示24张图片,就可以让观看的人感觉到连续的动态图片。



OpenGLDemo样例被设计成一秒内显示30张图片,其中OpenGLScreen在循环线程中每隔33毫秒就显示一张新的静态图片,在每张静态图片中所绘制的3D模型都旋转一个很小的角度,这样用户就看到一个连续旋转的立方体。



为了实现在33毫秒显示一张新的静态图片,同时不浪费CPU时间,样例需要进行一些节流操作。如果显示完前一张静态图片后,后一张静态图片在33毫秒内准备好了,这时不需要马上将第二张图片显示出来,可以让线程休眠一段时间,直到用完了33毫秒的时间简隔。这样就可以在保证动画流畅的同时释放一定的CPU资源。比如, 在显示完第一张静态图片后,第二张静态图片在10毫秒内准备好了,则这个线程可以休眠23毫秒(33-10=23),休眠23毫秒后再显示第二张静态图片仍保证了每隔33毫秒显示一张新图片,保证了动画的流畅性。



当然,如果计算发现后一张静态图片的准备时间超过了33毫秒,就需要在准备好图片后马上将它显示出来,不再进行休眠。



OpenGLDemo样例在OpenGLScreen实例的run()方法中就使用了这种节流手段,具体代码见代码清单18-7

代码清单18-7

while (_running)
{// 记录当前时间
throttle = (int)System.currentTimeMillis();
// 更新立方体的角度
_renderer.update();
// 绘制立方体
renderFrame();
// 省略其它代码……
//显示所绘制的内容
_egl.eglSwapBuffers(_eglDisplay, _eglSurface);

// 计算绘制和显示立方体使用了多长时间
throttle = (int)System.currentTimeMillis() - throttle;


//计算33毫秒和花费时间的差值,如果差值大于零则表明下一张图片在33毫秒内
//准备好了,可以休眠一段时间,休眠时间为33毫秒和花费时间的差值
throttle = 33 - throttle;

if (throttle > 0)
{
// 差值大于零,休眠一段时间
try
{
Thread.sleep(throttle);
}
catch (InterruptedException ie)
{
}
}
}
代码在OpenGLScreen的循环线程中通过throttle记录了绘制前的时间,然后对立方体进行绘制。绘制结束后再通过throttle变量比较绘制后的时间,得出立方体绘制所需要的时间。如果绘制时间小于33毫秒,则表示程序在33毫秒内完成了计算,可以让程序休眠一段时间,休眠时间等于33减去绘制时间。



了解了OpenGLDemo样例中动画的实现,再看看样例中是如何实现3D模型的旋转的。先关注CubeRenderer的update方法,因为OpenGLScreen循环线程在每次循环中都调用该方法实现3D模型的变化。update()方法的代码如代码清单18-8

代码清单18-8

public void update() {
long currentTime = System.currentTimeMillis();
float t = (float) (currentTime - _lastUpdate) / 5000.0f;

if ((_angle += 360.0f * t) > 360.0f) {
_angle = 360.0f - _angle;
}
_lastUpdate = currentTime;
}

update方法的作用是通过成员变量_angle记录需要转动的角度,每次调用的时候都增加一定的角度,在绘制的时候将初始状态的3D模型转动_angle所记录的角度就可以实现3D模型的转动了。



然而,因为设备CPU需要处理其它进程,OpengGLScreen循环线程完成每次循环的时间是不同的,如果在每次循环中都增加相同的角度,则显示出来的立方体的转动速度会时快时慢。为了解决这个问题,update方法记录了上次调用update方法的时间,将两次调用的时间差值乘于360/5000。因为通过360/5000可以得出1毫秒需要转动多少度才能在5秒内转动360度,将两次调用的时间差乘于360/5000可以得出两次update调用之间需要转动多少度。



当然,在update方法中还需要对变量_angle进行判断,如果立方体已经转动一圈,即_angle的值超出360度则需要进行处理,避免_angle的数值不断变大。



update方法只是记录和更新变量_angle的值,真正使用_angle的值的是render方法中的以下语句:

gl.glRotatef(_angle, 1.0f, 1.0f, 0.0f);
glRotatef用于旋转模型,带四个参数,第一个参数是需要旋转的角度,后面三个参数用于指定围着X,Y,Z三个轴的旋转系数。将三个轴的旋转系数乘于旋转角度都可以获得在不同的轴需要旋转的角度。



在上一节中的简化样例中,所建立的三个三角形之所以没有旋转是因为在简化样例中没有调用glRotatef。为了在简化样例中将模型旋转起来,可以简单地加入glRotatef方法,代码片段如下:

//...
gl.glTranslatef(0, 0, -3.5f);
gl.glRotatef(_angle, 1.0f, 1.0f, 0.0f);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
//...

运行结果如图18-7,模型旋转之后就可以看到上一节加入的新的三角平面,运行结果是三个三角形组成的锥面在屏幕上旋转。








图18-7 简化样例,旋转的三角形
可以注意到当模型旋转之后三角形的背面在屏幕上并不显示,因为我们通过法向量数组指定了平面的正面,系统只绘制平面的正面并不绘制平面的反面。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: