zoukankan      html  css  js  c++  java
  • 初试PyOpenGL一 (Python+OpenGL)

      很早就一直想学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.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切换前面操作的移动幅度。

  • 相关阅读:
    316 Remove Duplicate Letters 去除重复字母
    315 Count of Smaller Numbers After Self 计算右侧小于当前元素的个数
    313 Super Ugly Number 超级丑数
    312 Burst Balloons 戳气球
    309 Best Time to Buy and Sell Stock with Cooldown 买股票的最佳时间含冷冻期
    Java 类成员的初始化顺序
    JavaScript 全局
    HTML字符实体
    Java中的toString()方法
    JavaScript 弹窗
  • 原文地址:https://www.cnblogs.com/zhouxin/p/3526402.html
Copyright © 2011-2022 走看看