5.5 用OpenGL制作动画
使用OpenGL的动机来源于CPU处理能力的限制,限制体现在当我们面临一项要可视化成千上万个数据点的工作,并且要求其快速执行(有时甚至是实时的)的时候。
现代计算机拥有强大的GPU用于加速与可视化相关的计算(比如游戏)。它们没有理由不能用于科学相关的可视化。
实际上,编写硬件加速的软件至少有一个缺点。就硬件的依赖而言,现代图形卡要求有专有的驱动,有时候驱动在目标平台/机器(例如用户的笔记本)上是无法使用的;即使是可用的,有时候你也不想呆在那花大把的时间去安装驱动所依赖的软件,相反,你想把时间花费在展示你的发现,并演示你的研究成果上。虽然这并不会成为编写硬件加速软件的障碍,但是你还是需要考虑一下这件事情,并且衡量一下在项目中引入这个复杂性的成本和收益。
解释完缺点后,我们可以对硬件加速可视化说“是”,可以对OpenGL,这一图形加速的工业标准说“是”。
我们将使用OpenGL来完成本节的内容,因为它是跨平台的,因此所有的例子应该在Linux、Mac或者Windows上都是工作的,就像我们所演示的那样。这里假定你已经安装了所需的硬件和操作系统级别的驱动。
5.5.1 准备工作
如果你从来没有使用过OpenGL,现在我们将做一个快速的介绍来帮助你理解。但是要真正的了解OpenGL,至少要阅读并理解一整本书。OpenGL是一个规范,而不是一个实现,因此OpenGL本身并没有任何实现代码,所有的实现是遵循该规范而开发的库。这些库是跟随你的操作系统,或者由如NVIDIA或者AMD/ATI等不同的显卡厂商发布的。
此外,OpenGL只关注图形渲染而不是动画、定时和其他复杂的事情,这些事情是留给其他库来完成的。
OpenGL动画基础
因为OpenGL是一个图形渲染库,所以它不知道我们在屏幕上绘制的是什么。它不关心我们画的是否是一只猫、一个球,或者一条线,还是所有这些对象。因此,要移动一个已经渲染的对象,需要清除并重绘整个图像。为了让某个物体动起来,我们需要很快地循环绘制和重绘所有内容,并把它显示给用户,这样用户就认为他/她正在观看一个动画。
在机器上安装OpenGL是一件和平台相关的过程。在Mac OS X上,OpenGL的安装通过系统升级来完成,但是开发库(所谓的“头文件”)是Xcode开发包的一部分。
在Windows系统上,最好的方式是安装电脑的显卡厂商的最新显卡驱动程序。OpenGL可能并不需要它们就可以工作,但那样的话你就很可能失去了原版驱动程序的最新特性。
在Linux平台上,如果你不反对安装闭源软件,在操作系统发行版自身的软件管理器中,或者显卡厂商网站上的二进制安装文件,都提供了可供下载的特定厂商的驱动。Mesa3D几乎一直都是OpenGL的标准实现,它也是最有名的OpenGL实现,使用Xorg来为Linux、FreeBSD和类似操作系统的OpenGL提供支持。
基本上,在Debian/Ubuntu系统中,应当安装下列软件包及其依赖。
$ sudo apt-get install libgl1-mesa-dev libgl-mesa-dri
然后,你就可以使用一些开发库和/或者框架来实际地编写OpenGL支持的应用程序了。
我们在这里只关注Python中的OpenGL绘图,因此我们将回顾在Python中使用最多的一些构建在OpenGL之上的库和框架。我们会提到matplotlib及其当前和将来对OpenGL的支持。
- Mayavi:这是一个专门用于3D的库。
- Pyglet:这是一个纯Python的图形库。
- Glumpy:这是一个构建在Numpy之上的快速图形渲染库。
- Pyglet和OpenGL:这是用来可视化大数据(百万级数据点)的。
5.5.2 操作步骤
专业化的项目Mayavi是一个功能全面的3D图形库,它主要用于高级3D渲染。它包含在已经提到的 Python包中,如EPD(虽然没有免费许可)。这也是在Windows和Mac OS X操作系统上的推荐安装方式。在Linux平台上,也可以通过pip轻松地安装,代码如下。
$ pip install mayavi
Mayavi可以作为一个开发库/框架,或者一个应用程序来使用。Mayavi应用程序包含了一个可视化编辑器,可以用于简单的数据研究和一些交互可视化。
作为一个图形库,Mayavi的用法和matplotlib相似。它可以从一个脚本接口中,或者作为一个完全的面向对象的库来使用。Mayavi的大多数接口在mlab模块中,可以使用它们来制作动画。例如,可以像下面代码那样来完成一个简单的Mayavi动画。
import numpy
from mayavi.mlab import *
# Produce some nice data.
n_mer, n_long = 6, 11
pi = numpy.pi
dphi = pi/1000.0
phi = numpy.arange(0.0, 2*pi + 0.5*dphi, dphi, 'd')
mu = phi*n_mer
x = numpy.cos(mu)*(1+numpy.cos(n_long*mu/n_mer)*0.5)
y = numpy.sin(mu)*(1+numpy.cos(n_long*mu/n_mer)*0.5)
z = numpy.sin(n_long*mu/n_mer)*0.5
# View it.
l = plot3d(x, y, z, numpy.sin(mu), tube_radius=0.025,
colormap='Spectral')
# Now animate the data.
ms = l.mlab_source
for i in range(100):
x = numpy.cos(mu)*(1+numpy.cos(n_long*mu/n_mer +
numpy.pi*(i+1)/5.)*0.5)
scalars = numpy.sin(mu + numpy.pi*(i+1)/5)
ms.set(x=x, scalars=scalars)
上述代码将生成如图5-5所示的带旋转图形的窗口。
5.5.3 工作原理
我们生成了数据集合,并创建了x、y和z三个函数。这些函数被用在plot3d函数中作为图形的起始位置。
然后,导入mlab_source对象,以便能在点和标量的级别上操作图形。然后使用这个特性在循环中设置特定的点和标量来创建一个100帧的旋转动画。
5.5.4 补充说明
如果你想实验更多的内容,最简单的方式是打开IPython,导入myayvi.lab,并运行一些名字为test_*的函数。
为了了解到底发生了什么,你可以借助IPython的功能来检查和研究Python源码,像下面代码显示的这样。
In [1]: import mayavi.mlab
In [2]: mayavi.mlab.test_simple_surf??
Type: function
String Form:<function test_simple_surf at 0x641b410>
File:/usr/lib/python2.7/dist-packages/mayavi/tools/helper_
functions.py
Definition: mayavi.mlab.test_simple_surf()
Source:
def test_simple_surf():
"""Test Surf with a simple collection of points."""
x, y = numpy.mgrid[0:3:1,0:3:1]
return surf(x, y, numpy.asarray(x, 'd'))
这里,我们看到如何通过在函数名后面添加两个问号(“??”)让IPython找到函数的源码并显示。这是一个真实的探索性计算,经常在可视化社区中被使用,因为它是了解数据和代码的一个快速的方式。
Pyglet快速入门
Pyglet是另一个著名的Python库,可以让编写图形和与窗口相关的应用程序变得轻松起来。它通过模块pyglet.gl来支持OpenGL,但是为了能使用Pyglet的威力你不必直接使用这个模块。通过pyglet.graphics来使用它是最方便的用法。
Pyglet采用了一种和Mayavi不同的方式。它没有可视化的IDE,你要负责从创建窗口,到发出一个低级别的OpenGL调用来配置OpenGL上下文环境的所有工作。它有时比Mayavi慢,但是你所获得的是控制应用程序的每个部分的能力。这有时候也意味着会投入更多的工作时间,但是通常来讲,它也意味着你的应用程序有更高的质量和性能。
可以通过下面的代码来得到一个简单的应用程序(图像查看器)。
import pyglet
window = pyglet.window.Window()
image = pyglet.resource.image('kitten.jpg')
@window.event
def on_draw():
window.clear()
image.blit(0, 0)
pyglet.app.run()
上述代码创建了一个窗口,加载了一幅图像,并指定了当我们绘制一个窗口对象时所发生的事件(换言之,我们为on_draw事件定义了一个事件处理器)。最后,运行我们的程序(piglet.app.run())。
在实现的内部,程序使用OpenGL在窗口上进行绘制。此接口可以从pyglet.gl模块获得。然而直接使用它是不高效的,因此pyglet在pyglet.graphics中提供了一个更简单的接口,在这个接口内部使用了顶点数组(vertex arrays)和缓冲区(buffers)。
Glumpy快速入门
Glumpy是一个OpenGL+NumPy库,它用OpenGL来进行快速Numpy可视化。它是一个由Nicolas Rougier启动的开源项目,致力于高效可视化。为了使用它,我们需要Python OpenGL绑定(bindings)、SciPy,当然还有Glumpy。安装命令如下。
sudo apt-get install python-opengl
sudo pip install scipy
sudo pip install glumpy
Glumpy使用OpenGL纹理(textures)来表示阵列,因为这恐怕是在现代图形硬件上最快的可视化方法了。
Pyprocessing 简介
Pyprocessing和Processing(http://processing.org )的工作方式极其相似。Pyprocessing中的大多数函数和Processing函数是相同的。如果你熟悉Processing和Python,你就已经知道了编写Pyprocessing程序所需的几乎所有知识。为了使用它,我们唯一需要做的事情是导入pyprocessing包,用Pyprocessing函数和数据结构来编写剩余的代码,然后调用run()函数来执行。
有很多关于OpenGL以及如何在C/C++或者任何其他语言binding中使用它的免费教程。在OpenGL官方wiki上提供了一个清单,地址为http://www.opengl.org/wiki/Getting_started#Tutorials_and_How_To_Guides。
总之,还有许多处理Python、OpenGL和3D可视化的项目,其中有一些比较年轻,有一些已经不再维护了,但是如果你发现有项目应该被提到,请告诉我们。