说实话我们OpenGL的基础还远远没有学完,不过我在说下去大概就不会有人看了,所以,虽然稍稍有些早,开始我们的第一个程序吧。
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
def drawFunc():
glClear(GL_COLOR_BUFFER_BIT)
#glRotatef(1, 0, 1, 0)
glutWireTeapot(0.5)
glFlush()
glutInit()
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA)
glutInitWindowSize(400, 400)
glutCreateWindow("First")
glutDisplayFunc(drawFunc)
#glutIdleFunc(drawFunc)
glutMainLoop()
结果如上图,虽然很短,相信这里面很多函数大家会觉得陌生,不要紧,这些代码是每个程序中都需要的,基本看两个就OK了,下面来详细说明一下。
先把注释的两个函数给无视了,看看一开始三行的导入,OpenGL一开始是以C写成的,所以OpenGL的程序中绘图的代码也是一大堆的函数,没有什么对象类的,所以我们把相关函数全部倒入,虽然这有些污染环境的感觉和Python的设计机理有些不符,不过这么多年都过来了:),而且OpenGL中的函数都是有前缀的,问题也不大。
一般的函数命名如下:
<前缀><根函数><参数数目><参数类型>
前缀有gl、glu、aux、glut、wgl、glx、agl等等,分别表示该函数属于OpenGL那个开发库等。所谓开发库,要知道原生的OpenGL是跨平台的,跨平台意味着很多功能是无法实现的,比如说Windows和X-Window的窗口实现机制是不同的,OpenGL并不关心这些东西,只管画图。所以,OpenGL并没有窗口函数,比如无法创建窗口,无法获得输入……这些东西都需要其他的函数库来实现。我们可以用我之前讲述的Pygame来创建窗口,然后用PyOpenGL来绘图,不过咱不能要求每个看PyOpenGL教程的朋友都先看一遍Pygame教程(这样就是强买强卖了不是),所以这里就用OpenGL常用的工具函数库来实现。
我们主要使用两种,一个是GLU库,它提供了比较基础的命令的封装,可以很简单的实现比较多的复杂功能;而另外一个就是GLUT,glut是不依赖于窗口平台的OpenGL工具包,目的是隐藏不同窗口平台API的复杂度,提供更为复杂的绘制功能,我们会大量的使用它。
参数数和参数函数就很好理解了,有点像匈牙利命名法,f说明是个float,i说明是int等等。对Python来说可能不是很重要,不过还是要说明一下,OpenGL函数有d(double)的版本,C/C++语言一般默认浮点数就是double,使用d版本函数可能会显得比较简单,但是我们不推荐。因为OpenGL内部数据都是以float的形式存放的,如果使用double会对性能有一定的影响。
举个例子,glColor3f()表示了该函数属于gl库,参数是三个float型参数指针。类似的函数还有glColor3i,glColor4f等,我们用glColor*()来表示这一类函数。
11~17行基本也是固定的,
glutInit()是用glut来初始化OpenGL的,所有的问题都交给这个函数吧,基本不用管,虽说可以接受参数的,基本无用。
glutInitDisplayMode(MODE)非常重要,这里告诉系统我们需要一个怎样显示模式。至于其参数GLUT_RGBA就是使用(red, green, blue)的颜色系统。有没有写错?这里有个A啊,不应该是(red, green, blue, alpha)么?大概是历史原因,GLUT_RGBA和GLUT_RGB是其实是等价的(坑爹啊),要想实现Alpha还得用其他的参数。而GLUT_SINGLE意味着所有的绘图操作都直接在显示的窗口执行,相对的,我们还有一个双缓冲的窗口,对于动画来说非常合适。看看用Python和Pygame写游戏-从入门到精通(3)有些说明。
glutInitWindowSize(400, 400)这个函数很容易理解,设置出现的窗口的大小。实际上还有个glutInitWindowPosition()也很常用,用来设置窗口出现的位置。
glutCreateWindow(“First”),一旦调用了,就出现一个窗口了,参数就是窗口的标题。
glutDisplayFunc(func)是glut非常讨人喜欢的一个功能,它注册了一个函数,用来绘制OpenGL窗口,这个函数里就写着很多OpenGL的绘图操作等命令,也就是我们主要要学习的东西。
glutMainLoop(),主循环,一旦调用了,我们的OpenGL就一直运行下去了。和很多程序中的主循环一样,不停的运行,画出即时的图像,处理输入等。
看看drawFunc里的几句话,这里是实际绘图的函数。
glClear(GL_COLOR_BUFFER_BIT)是把先前的画面给清除,这基本是定律,每次重绘之前都要把原来的画面擦除,否则叠加起来什么都看不出了。glClear一看就知道是OpenGL原生的命令,而参数就是指明要清除的buffer。大家一定会有疑问,我们清除,不就是清除屏幕上的画面么,为什么还要指定?OpenGL的博大精深这里就体现出来了,buffer不仅仅有我们看到的那个GL_COLOR_BUFFER_BIT,OpenGL中还有其他的buffer类型,我们会在后面的章节讲到。
glutWireTeapot(0.5)是glut提供的绘制犹他茶壶的工具函数,茶壶还是相当复杂的一个几何体,用这个函数一下子就画出来了,不过基本也就演示用用。这里是用的线模型,因为没有说光照和材质,如果glutSolidTeapot()画出来就成纸片儿了。
glFlush()似乎不用多说,画了那么多,自然要刷新一下显示。不过,这里的刷新不仅仅是屏幕上的更新,实际上,它是处理OpenGL的渲染流水线,让所有排队中的命令得到执行。OpenGL的渲染流水线是一个很重要的概念,不过这里暂时还不打算多说明,否则对初学者来说,未免有些麻烦了。但是这并不意味着可以无视这些基础,知道怎么做只能让你优秀,知道为什么这么做才能让你卓越。
现在你可以把注释的两个语句打开了,执行以下看到什么?旋转的茶壶!不得不说帅多了~
glutIdleFunc(Func)又是一个激动人心的函数,可以让OpenGL在闲暇之余,调用一下注册的函数,这是是产生动画的绝好方法。
glRotatef(1, 0, 1, 0)是一个我们以后会详细讲的函数,简单来说四个参数第一个是角度,后三个是一个向量,意义就是绕着这个向量旋转,这里是绕着Y轴旋转1°。这一度一度的累加,最后使得茶壶围绕Y轴不停的旋转。从这里我们也能看出来,我们指定了一个旋转的角度后,重新绘制并不会复位,而是在上一次旋转的结果上继续旋转。这是一个非常重要的概念,OpenGL是一个状态机,一旦你指定了某种状态,知道再指定位置,它会保持那种状态。不仅仅是旋转,包括以后的光照贴图等等,都遵循这样的规律。
好了,我们有了第一个PyOpenGL程序了,虽然离我们详细中的五光十色的立体世界还有些差距,不过毕竟画了点东西出来了(要知道,犹他茶壶在3D技术发展之初,是里程碑一般的作品)。慢慢的,我们会充实自己的知识,绘制出更靓丽的画面。