初试PyOpenGL一(Python+OpenGL)

王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: 小  |  中  |  大  |  超大  

很早就一直想学Python,看到一些书都有介绍,不管是做为游戏的脚本语言,还是做为开发项目的主要语言都有提及(最主要的CUDA都开始支持Python,CUDA后面一定要学),做为先熟悉一下Python,本文用PyOpenGL实现一些基本的显示效果,一个网格,一个球体,加一个能切换第一与第三人称的摄像机。

PyOpenGL是一个用Python实现的多平台的OpenGL的API,为了学习Python与PyOpengl,本文也是用的Python,而不是.net版本的IronPython.

先看一下,相关环境的搭建:

首先我们需要下载Python: http://www.python.org/getit/

然后是PyOpenGL库:https://pypi.python.org/pypi/PyOpenGL

和PyOpenGL库常连在一起用的二个库,一个库numpy,提供常用的科学计算包含矩阵运算,数组转换与序列化,还有一个是3D常用的图片处理库:Python Imaging Library (PIL)。

numpy下载:http://sourceforge.net/PRojects/numpy/files/ 简介:http://sebug.net/paper/books/scipydoc/numpy_intro.html

Python Imaging Library (PIL)下载:http://www.pythonware.com/products/pil/

当上面环境安装完成后,我们先来实现一个基本的球体VBO实现,代码请参考前面的WebGL 利用FBO完成立方体贴图中的球的代码:

1#common.py2importmath3fromOpenGL.GLimport*4fromOpenGL.arraysimportvbo5fromOpenGL.GLUimport*6fromOpenGL.GLUTimport*7#import OpenGL.GLUT as glut8importnumpy as ny9#Python Imaging Library (PIL)10classcommon:11bCreate =False1213#球的实现14classsphere(common):15def__init__(this,rigns,segments,radius):16this.rigns =rigns17this.segments =segments18this.radius =radius19defcreateVAO(this):20vdata =[]21vindex =[]22foryinrange(this.rigns):23phi = (float(y) / (this.rigns - 1)) *math.pi24forxinrange(this.segments):25theta = (float(x) / float(this.segments - 1)) * 2 *math.pi26vdata.append(this.radius * math.sin(phi) *math.cos(theta))27vdata.append(this.radius *math.cos(phi))28vdata.append(this.radius * math.sin(phi) *math.sin(theta))29vdata.append(math.sin(phi) *math.cos(theta))30vdata.append(math.cos(phi))31vdata.append(math.sin(phi) *math.sin(theta))32foryinrange(this.rigns - 1):33forxinrange(this.segments - 1):34vindex.append((y + 0) * this.segments +x)35vindex.append((y + 1) * this.segments +x)36vindex.append((y + 1) * this.segments + x + 1)37vindex.append((y + 1) * this.segments + x + 1)38vindex.append((y + 0) * this.segments + x + 1)39vindex.append((y + 0) * this.segments +x)40#this.vboID = glGenBuffers(1)41#glBindBuffer(GL_ARRAY_BUFFER,this.vboID)42#glBufferData (GL_ARRAY_BUFFER, len(vdata)*4, vdata, GL_STATIC_DRAW)43#this.eboID = glGenBuffers(1)44#glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,this.eboID)45#glBufferData (GL_ELEMENT_ARRAY_BUFFER, len(vIndex)*4, vIndex,46#GL_STATIC_DRAW)47this.vbo = vbo.VBO(ny.array(vdata,'f'))48this.ebo = vbo.VBO(ny.array(vindex,'H'),target =GL_ELEMENT_ARRAY_BUFFER)49this.vboLength = this.segments *this.rigns50this.eboLength =len(vindex)51this.bCreate =True52defdrawShader(this,vi,ni,ei):53ifthis.bCreate ==False:54this.createVAO()55#glBindBuffer(GL_ARRAY_BUFFER,this.vboID)56#glVertexAttribPointer(vi,3,GL_FLOAT,False,24,0)57#glEnableVertexAttribArray(vi)58#glVertexAttribPointer(ni,3,GL_FLOAT,False,24,12)59#glEnableVertexAttribArray(ni)60#glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,this.eboID)61#glDrawElements(GL_TRIANGLES,this.eboLength,GL_UNSIGNED_INT,0)62this.vbo.bind()63defdraw(this):64ifthis.bCreate ==False:65this.createVAO()66#glBindBuffer(GL_ARRAY_BUFFER,this.vboID)67#glInterleavedArrays(GL_N3F_V3F,0,None)68#glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,this.eboID)69#glDrawElements(GL_TRIANGLES,this.eboLength,GL_UNSIGNED_INT,None)70this.vbo.bind()71glInterleavedArrays(GL_N3F_V3F,0,None)72this.ebo.bind()73glDrawElements(GL_TRIANGLES,this.eboLength,GL_UNSIGNED_SHORT,None)

球

这段代码画球,不同于我最开始用的每个三角形分成四个三角形的代码,基本思想是和地球仪上的经纬线一样,画出经纬线的点(顶点位置),然后有顺序的连接在一起(顶点索引)就可以了。

这里先说下python,和我之前接触的语言来看,我发现这个出乎意料的最和F#比较接近,虽然他们一个是动态语言,一个是静态语言,但是他们首先都是强类型语言,并且都支持多范式(对象式,过程式,函数式),同做为强类型语言,默认都不需要声明类型,不知Python是否和F#一样,是用的类型推导,有个比较明显的地方和F#一样的地方就是,在这里def __init__(this,rigns,segments,radius),首先righs,segments,radius开始鼠标移上去都是unknow type,但是在别的地方调用common.sphere(16,16,1)后,他就能推断出righs,segments,radius都为int.并且和F#一样,声明类的方法时,都需要带一个表示自己的参数,且都和C#不一样(限定this)可以自定义这个参数的名称.当然还有最大的共同点,他们都是用缩进来控制语言块(满分),现在写C#代码有些不满意的地方都是因此而起啊。基于以上这些,写python感觉很亲切,也很爽,和F#一样,能写出很简洁的代码,相信一个学习过F#的人来写python,肯定也有此类感觉。当然python做为动态语言,比F#,C#来说,开发效率更高,比如,在上面一段中,this.vboLength = this.segments * this.rigns,这里直接动态声明一个属性vboLength,而不需要和F#与C#一样来先声明一个这样的属性,当然,net4.0中的DLR来说,也是能实现这种效果,但是用起来感觉就不一样了。

大家如果有兴趣了解各编程语言,强烈推荐郑晖大神的冒号课堂系列文章 第一篇冒号课堂§1.1:开班发言

下面是网格的代码,代码也可以参考前面柏林噪声实践(一) 海波,一样是生成网格上所有的x,z点,然后组织索引,看代码:

1classplane(common):2def__init__(this,xres,yres,xscale,yscale):3this.xr,this.yr,this.xc,this.yc = xres - 1,yres - 1,xscale,yscale4defcreateVAO(this):5helfx = this.xr * this.xc * 0.56helfy = this.yr * this.yc * 0.57vdata =[]8vindex =[]9foryinrange(this.yr):10forxinrange(this.xr):11vdata.append(this.xc * float(x) -helfx)12vdata.append(0.)13vdata.append(this.yc * float(y) -helfy)14foryinrange(this.yr - 1):15forxinrange(this.xr - 1):16vindex.append((y + 0) * this.xr +x)17vindex.append((y + 1) * this.xr +x)18vindex.append((y + 0) * this.xr + x + 1)19vindex.append((y + 0) * this.xr + x + 1)20vindex.append((y + 1) * this.xr +x)21vindex.append((y + 1) * this.xr + x + 1)22printlen(vdata),len(vindex)23this.vbo = vbo.VBO(ny.array(vdata,'f'))24this.ebo = vbo.VBO(ny.array(vindex,'H'),target =GL_ELEMENT_ARRAY_BUFFER)25this.eboLength =len(vindex)26this.bCreate =True27defdraw(this):28ifthis.bCreate ==False:29this.createVAO()30this.vbo.bind()31glInterleavedArrays(GL_V3F,0,None)32this.ebo.bind()33glDrawElements(GL_TRIANGLES,this.eboLength,GL_UNSIGNED_SHORT,None)

网络

哈哈,大家发现了,我都是把以前写的javascripe,F#代码拿来改写的,毕竟我也只是一个python新手,把别的语言拿来改写我认为是最快熟悉一门语言的方法。同样,下面第一,第三人称漫游代码也是我前面Opengl绘制我们的小屋(四)第三人称漫游,Opengl绘制我们的小屋(二)第一人称漫游里的代码改写的,具体思路请转至这二篇文章。

1classcamera:2origin = [0.0,0.0,0.0]3length = 1.4yangle =0.5zangle =0.6__bthree=False7def__init__(this):8this.mouselocation = [0.0,0.0]9this.offest = 0.0110this.zangle = 0.ifnotthis.__bthreeelsemath.pi11defsetthree(this,value):12this.__bthree=value13this.zangle = this.zangle +math.pi14this.yangle = -this.yangle15defeye(this):16returnthis.originifnotthis.__bthreeelsethis.direction()17deftarget(this):18returnthis.originifthis.__bthreeelsethis.direction()19defdirection(this):20ifthis.zangle > math.pi * 2.0:21this.zangle < - this.zangle - math.pi * 2.022elifthis.zangle <0. :23this.zangle < - this.zangle + math.pi * 2.024len = 1.ifnotthis.__bthreeelsethis.lengthif0.else1.25xy = math.cos(this.yangle) *len26x = this.origin[0] + xy *math.sin(this.zangle)27y = this.origin[1] + len *math.sin(this.yangle)28z = this.origin[2] + xy *math.cos(this.zangle)29return[x,y,z]30defmove(this,x,y,z):31sinz,cosz =math.sin(this.zangle),math.cos(this.zangle)32xstep,zstep = x * cosz + z * sinz,z * cosz - x *sinz33ifthis.__bthree:34xstep = -xstep35zstep = -zstep36this.origin = [this.origin[0] + xstep,this.origin[1] + y,this.origin[2] +zstep]37defrotate(this,z,y):38this.zangle,this.yangle = this.zangle - z,this.yangle + yifnotthis.__bthreeelse-y39defsetLookat(this):40ve,vt =this.eye(),this.target()41#print ve,vt42glLoadIdentity()43gluLookAt(ve[0],ve[1],ve[2],vt[0],vt[1],vt[2],0.0,1.0,0.0)44defkeypress(this,key, x, y):45ifkeyin('e','E'):46this.move(0.,0.,1 *this.offest)47ifkeyin('f','F'):48this.move(1 *this.offest,0.,0.)49ifkeyin('s','S'):50this.move(-1 *this.offest,0.,0.)51ifkeyin('d','D'):52this.move(0.,0.,-1 *this.offest)53ifkeyin('w','W'):54this.move(0.,1 *this.offest,0.)55ifkeyin('r','R'):56this.move(0.,-1 *this.offest,0.)57ifkeyin('v','V'):58#this.__bthree = not this.__bthree59this.setthree(notthis.__bthree)60ifkey ==GLUT_KEY_UP:61this.offest = this.offest + 0.162ifkey ==GLUT_KEY_DOWN:63this.offest = this.offest - 0.164defmouse(this,x,y):65rx = (x - this.mouselocation[0]) * this.offest * 0.166ry = (y - this.mouselocation[1]) * this.offest * -0.167this.rotate(rx,ry)68printx,y69this.mouselocation = [x,y]

摄像机漫游

代码很简单,当然,做为一个类来说,其实setLookat,keypress,mouse这三个方法应该分离出去的,不过用了使用方便,也便于放在一起理解。其中keypress与mouse实现键盘启用常用的基本漫游,其中EDSF前后左右移动,WR分别向上与向下,鼠标右键加移动鼠标控制方向,V切换第一人称与第三人称。UP与DOWN切换前面操作的移动幅度。其中len = 1. if not this.__bthree else this.length if 0. else 1.这个解释下,在不是第三人称漫游下,长度为1,否则在第三人称漫游下,长度取length的长度,如果length为0,则取1的长度.其实就是相当二个三元运算符,但是感觉理解起来更方便.

下面来看具体调用并显示的代码:

1fromOpenGL.GLimport*2fromOpenGL.GLUTimport*3fromOpenGL.GLUimport*45importcommon6importsys78window =09sph = common.sphere(16,16,1)10camera =common.camera()11plane = common.plane(12,12,1.,1.)12defInitGL(width,height):13glClearColor(0.1,0.1,0.5,0.1)14glClearDepth(1.0)15glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)16glMatrixMode(GL_PROJECTION)17glLoadIdentity()18gluPerspective(45.0,float(width)/float(height),0.1,100.0)19camera.move(0.0,3.0,-5)2021defDrawGLScene():22glClear(GL_COLOR_BUFFER_BIT |GL_DEPTH_BUFFER_BIT)23glMatrixMode(GL_MODELVIEW)24camera.setLookat()25plane.draw()26glTranslatef(-1.5,0.0,0.0)27glBegin(GL_QUADS)28glVertex3f(-1.0, 1.0, 0.0)29glVertex3f(1.0, 1.0, 0.0)30glVertex3f(1.0, -1.0, 0.0)31glVertex3f(-1.0, -1.0, 0.0)32glEnd()33glTranslatef(3.0, 0.0, 0.0)34sph.draw()35glutSwapBuffers()3637defmouseButton( button, mode, x, y ):38ifbutton ==GLUT_RIGHT_BUTTON:39camera.mouselocation =[x,y]4041defReSizeGLScene(Width, Height):42glViewport(0, 0, Width, Height)43glMatrixMode(GL_PROJECTION)44glLoadIdentity()45gluPerspective(45.0, float(Width)/float(Height), 0.1, 100.0)46glMatrixMode(GL_MODELVIEW)4748defmain():49globalwindow50glutInit(sys.argv)51glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE |GLUT_DEPTH)52glutInitWindowSize(640,400)53glutInitWindowPosition(800,400)54window = glutCreateWindow("opengl")55glutDisplayFunc(DrawGLScene)56glutIdleFunc(DrawGLScene)57glutReshapeFunc(ReSizeGLScene)58glutMouseFunc( mouseButton )59glutMotionFunc(camera.mouse)60glutKeyboardFunc(camera.keypress)61glutSpecialFunc(camera.keypress)62InitGL(640, 480)63glutMainLoop()6465main()

显示效果

代码很简单,把球,网络,漫游摄像机应用进去。注意glutMouseFunc( mouseButton )与glutMotionFunc(camera.mouse)组合用才能达到原来OpenTK提供的鼠标检测功能,因为glutmousefunc只检测鼠标的按下等动作,意思你一直按下移动他是不会引用的,在这引用的是glutmotionfunc,这个大家可以自己去试验。

下面放出效果图:

效果很简单,主要是为了下文先做一个基本的效果,同时也是用pyOpengl对前面的一点总结。

代码下载:代码 和上面说的一样,其中EDSF前后左右移动,WR分别向上与向下,鼠标右键加移动鼠标控制方向,V切换第一人称与第三人称。UP与DOWN切换前面操作的移动幅度。

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航