设为首页 收藏本站
查看: 1216|回复: 0

[经验分享] 初试PyOpenGL一 (Python+OpenGL)

[复制链接]
发表于 2015-4-19 06:00:23 | 显示全部楼层 |阅读模式
  很早就一直想学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完成立方体贴图中的球的代码:


DSC0000.gif DSC0001.gif


1 #common.py
2 import math
3 from OpenGL.GL import *
4 from OpenGL.arrays import vbo
5 from OpenGL.GLU import *
6 from OpenGL.GLUT import *
7 #import OpenGL.GLUT as glut
8 import numpy as ny
9 #Python Imaging Library (PIL)
10 class common:
11     bCreate = False
12
13 #球的实现
14 class sphere(common):
15     def __init__(this,rigns,segments,radius):
16         this.rigns = rigns
17         this.segments = segments
18         this.radius = radius
19     def createVAO(this):
20         vdata = []
21         vindex = []
22         for y in range(this.rigns):
23             phi = (float(y) / (this.rigns - 1)) * math.pi
24             for x in range(this.segments):
25                 theta = (float(x) / float(this.segments - 1)) * 2 * math.pi
26                 vdata.append(this.radius * math.sin(phi) * math.cos(theta))
27                 vdata.append(this.radius * math.cos(phi))
28                 vdata.append(this.radius * math.sin(phi) * math.sin(theta))
29                 vdata.append(math.sin(phi) * math.cos(theta))
30                 vdata.append(math.cos(phi))
31                 vdata.append(math.sin(phi) * math.sin(theta))
32         for y in range(this.rigns - 1):
33             for x in range(this.segments - 1):
34                 vindex.append((y + 0) * this.segments + x)
35                 vindex.append((y + 1) * this.segments + x)
36                 vindex.append((y + 1) * this.segments + x + 1)
37                 vindex.append((y + 1) * this.segments + x + 1)
38                 vindex.append((y + 0) * this.segments + x + 1)
39                 vindex.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)
47         this.vbo = vbo.VBO(ny.array(vdata,'f'))
48         this.ebo = vbo.VBO(ny.array(vindex,'H'),target = GL_ELEMENT_ARRAY_BUFFER)
49         this.vboLength = this.segments * this.rigns
50         this.eboLength = len(vindex)
51         this.bCreate = True
52     def drawShader(this,vi,ni,ei):
53         if this.bCreate == False:
54             this.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)
62         this.vbo.bind()
63     def draw(this):
64         if this.bCreate == False:
65             this.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)
70         this.vbo.bind()
71         glInterleavedArrays(GL_N3F_V3F,0,None)
72         this.ebo.bind()
73         glDrawElements(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点,然后组织索引,看代码:





1 class plane(common):
2     def __init__(this,xres,yres,xscale,yscale):
3         this.xr,this.yr,this.xc,this.yc = xres - 1,yres - 1,xscale,yscale
4     def createVAO(this):
5         helfx = this.xr * this.xc * 0.5
6         helfy = this.yr * this.yc * 0.5
7         vdata = []
8         vindex = []
9         for y in range(this.yr):
10             for x in range(this.xr):
11                 vdata.append(this.xc * float(x) - helfx)
12                 vdata.append(0.)
13                 vdata.append(this.yc * float(y) - helfy)
14         for y in range(this.yr - 1):
15             for x in range(this.xr - 1):
16                 vindex.append((y + 0) * this.xr + x)
17                 vindex.append((y + 1) * this.xr + x)
18                 vindex.append((y + 0) * this.xr + x + 1)
19                 vindex.append((y + 0) * this.xr + x + 1)
20                 vindex.append((y + 1) * this.xr + x)
21                 vindex.append((y + 1) * this.xr + x + 1)
22         print len(vdata),len(vindex)
23         this.vbo = vbo.VBO(ny.array(vdata,'f'))
24         this.ebo = vbo.VBO(ny.array(vindex,'H'),target = GL_ELEMENT_ARRAY_BUFFER)
25         this.eboLength = len(vindex)
26         this.bCreate = True
27     def draw(this):
28         if this.bCreate == False:
29             this.createVAO()
30         this.vbo.bind()
31         glInterleavedArrays(GL_V3F,0,None)
32         this.ebo.bind()
33         glDrawElements(GL_TRIANGLES,this.eboLength,GL_UNSIGNED_SHORT,None)   
网络  哈哈,大家发现了,我都是把以前写的javascripe,F#代码拿来改写的,毕竟我也只是一个python新手,把别的语言拿来改写我认为是最快熟悉一门语言的方法。同样,下面第一,第三人称漫游代码也是我前面Opengl绘制我们的小屋(四)第三人称漫游,Opengl绘制我们的小屋(二)第一人称漫游里的代码改写的,具体思路请转至这二篇文章。





1 class camera:
2     origin = [0.0,0.0,0.0]
3     length = 1.
4     yangle = 0.
5     zangle = 0.
6     __bthree = False
7     def __init__(this):
8         this.mouselocation = [0.0,0.0]
9         this.offest = 0.01
10         this.zangle = 0. if not this.__bthree else math.pi
11     def setthree(this,value):
12         this.__bthree = value
13         this.zangle = this.zangle + math.pi
14         this.yangle = -this.yangle         
15     def eye(this):
16         return this.origin if not this.__bthree else this.direction()
17     def target(this):
18         return this.origin if this.__bthree else this.direction()
19     def direction(this):
20         if this.zangle > math.pi * 2.0 :
21             this.zangle < - this.zangle - math.pi * 2.0
22         elif this.zangle < 0. :
23             this.zangle < - this.zangle + math.pi * 2.0
24         len = 1. if not this.__bthree else this.length if 0. else 1.
25         xy = math.cos(this.yangle) * len
26         x = this.origin[0] + xy * math.sin(this.zangle)
27         y = this.origin[1] + len * math.sin(this.yangle)
28         z = this.origin[2] + xy * math.cos(this.zangle)        
29         return [x,y,z]
30     def move(this,x,y,z):
31         sinz,cosz = math.sin(this.zangle),math.cos(this.zangle)        
32         xstep,zstep = x * cosz + z * sinz,z * cosz - x * sinz
33         if this.__bthree :
34             xstep = -xstep
35             zstep = -zstep
36         this.origin = [this.origin[0] + xstep,this.origin[1] + y,this.origin[2] + zstep]        
37     def rotate(this,z,y):
38         this.zangle,this.yangle = this.zangle - z,this.yangle + y if not this.__bthree else -y
39     def setLookat(this):
40         ve,vt = this.eye(),this.target()
41         #print ve,vt
42         glLoadIdentity()
43         gluLookAt(ve[0],ve[1],ve[2],vt[0],vt[1],vt[2],0.0,1.0,0.0)        
44     def keypress(this,key, x, y):
45         if key in ('e', 'E'):
46             this.move(0.,0.,1 * this.offest)
47         if key in ('f', 'F'):
48             this.move(1 * this.offest,0.,0.)
49         if key in ('s', 'S'):
50             this.move(-1 * this.offest,0.,0.)
51         if key in ('d', 'D'):
52             this.move(0.,0.,-1 * this.offest)
53         if key in ('w', 'W'):
54             this.move(0.,1 * this.offest,0.)
55         if key in ('r', 'R'):
56             this.move(0.,-1 * this.offest,0.)
57         if key in ('v', 'V'):
58             #this.__bthree = not this.__bthree
59             this.setthree(not this.__bthree)
60         if key == GLUT_KEY_UP:
61             this.offest = this.offest + 0.1
62         if key == GLUT_KEY_DOWN:
63             this.offest = this.offest - 0.1
64     def mouse(this,x,y):  
65         rx = (x - this.mouselocation[0]) * this.offest * 0.1
66         ry = (y - this.mouselocation[1]) * this.offest * -0.1
67         this.rotate(rx,ry)
68         print x,y
69         this.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的长度.其实就是相当二个三元运算符,但是感觉理解起来更方便.
    下面来看具体调用并显示的代码:





1 from OpenGL.GL import *
2 from OpenGL.GLUT import *
3 from OpenGL.GLU import *
4
5 import common
6 import sys
7
8 window = 0
9 sph = common.sphere(16,16,1)
10 camera = common.camera()
11 plane = common.plane(12,12,1.,1.)
12 def InitGL(width,height):
13     glClearColor(0.1,0.1,0.5,0.1)
14     glClearDepth(1.0)
15     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
16     glMatrixMode(GL_PROJECTION)
17     glLoadIdentity()
18     gluPerspective(45.0,float(width)/float(height),0.1,100.0)   
19     camera.move(0.0,3.0,-5)   
20     
21 def DrawGLScene():
22     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
23     glMatrixMode(GL_MODELVIEW)     
24     camera.setLookat()
25     plane.draw()
26     glTranslatef(-1.5,0.0,0.0)
27     glBegin(GL_QUADS)                  
28     glVertex3f(-1.0, 1.0, 0.0)         
29     glVertex3f(1.0, 1.0, 0.0)           
30     glVertex3f(1.0, -1.0, 0.0)         
31     glVertex3f(-1.0, -1.0, 0.0)        
32     glEnd()   
33     glTranslatef(3.0, 0.0, 0.0)
34     sph.draw()                        
35     glutSwapBuffers()
36
37 def mouseButton( button, mode, x, y ):   
38     if button == GLUT_RIGHT_BUTTON:
39         camera.mouselocation = [x,y]
40
41 def ReSizeGLScene(Width, Height):
42     glViewport(0, 0, Width, Height)        
43     glMatrixMode(GL_PROJECTION)
44     glLoadIdentity()
45     gluPerspective(45.0, float(Width)/float(Height), 0.1, 100.0)
46     glMatrixMode(GL_MODELVIEW)
47     
48 def main():
49     global window
50     glutInit(sys.argv)
51     glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
52     glutInitWindowSize(640,400)
53     glutInitWindowPosition(800,400)
54     window = glutCreateWindow("opengl")
55     glutDisplayFunc(DrawGLScene)
56     glutIdleFunc(DrawGLScene)
57     glutReshapeFunc(ReSizeGLScene)
58     glutMouseFunc( mouseButton )
59     glutMotionFunc(camera.mouse)
60     glutKeyboardFunc(camera.keypress)
61     glutSpecialFunc(camera.keypress)
62     InitGL(640, 480)
63     glutMainLoop()
64
65 main()
显示效果  代码很简单,把球,网络,漫游摄像机应用进去。注意glutMouseFunc( mouseButton )与glutMotionFunc(camera.mouse)组合用才能达到原来OpenTK提供的鼠标检测功能,因为glutmousefunc只检测鼠标的按下等动作,意思你一直按下移动他是不会引用的,在这引用的是glutmotionfunc,这个大家可以自己去试验。
  下面放出效果图:
DSC0002.png
  效果很简单,主要是为了下文先做一个基本的效果,同时也是用pyOpengl对前面的一点总结。
  代码下载:代码 和上面说的一样,其中EDSF前后左右移动,WR分别向上与向下,鼠标右键加移动鼠标控制方向,V切换第一人称与第三人称。UP与DOWN切换前面操作的移动幅度。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-58467-1-1.html 上篇帖子: Python基础04 运算 下篇帖子: python数据采集与多线程效率分析
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表