初試PyOpenGL一 (Python+OpenGL)

  很早就一直想學Python,看到一些書都有介紹,不論是作爲遊戲的腳本語言,仍是作爲開發項目的主要語言都有說起(最主要的CUDA都開始支持Python,CUDA後面必定要學),作爲先熟悉一下Python,本文用PyOpenGL實現一些基本的顯示效果,一個網格,一個球體,加一個能切換第一與第三人稱的攝像機。html

  PyOpenGL是一個用Python實現的多平臺的OpenGL的API,爲了學習Python與PyOpengl,本文也是用的Python,而不是.net版本的IronPython.java

      先看一下,相關環境的搭建:python

  首先咱們須要下載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.htmlapp

  Python Imaging Library (PIL)下載:http://www.pythonware.com/products/pil/編程語言

  當上面環境安裝完成後,咱們先來實現一個基本的球體VBO實現,代碼請參考前面的WebGL 利用FBO完成立方體貼圖中的球的代碼:ide

 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,這個你們能夠本身去試驗。

  下面放出效果圖:

  效果很簡單,主要是爲了下文先作一個基本的效果,同時也是用pyOpengl對前面的一點總結。

  代碼下載:代碼 和上面說的同樣,其中EDSF先後左右移動,WR分別向上與向下,鼠標右鍵加移動鼠標控制方向,V切換第一人稱與第三人稱。UP與DOWN切換前面操做的移動幅度。

相關文章
相關標籤/搜索