python opengl 教程_【opengl 与 python】ogldev教程python实现 lesson 2.0 hello dots in glsl - 21San...

#Tutorial 2.0 这次内容很多很多,基本上都是关于opengl的指令,几乎未涉及图形原理(只是一个点嘛)

#考虑再三,还是把opengl全部import出来,因为opengl的函数都是以gl为前缀的,所以不会造成多少歧义,只是性能会影响点,#不过现在也不是考虑性能的时候,舒服地学就可以了

from OpenGL.GL import *

#这个是从pyopengl中导入的着色器编译包装,我准备在2.1进行讲解这两个函数

from OpenGL.GL.shaders importcompileShader,compileProgram#导入numpy,我们可以很方便将传入数据,有性能加成哦!

importnumpy as np#pyglet不多说了吧

importpyglet#提供指针,不要看的头疼,因为它仅是个跑龙套的

importctypes#照样1.0创建一个窗口

window = pyglet.window.Window(width = 800, height = 600, caption = "tutorial 02")#接下来就是准备着色器#分为以下步骤,编写GLSL程序源代码,GLSL全称opengl shading language,是着色语言,我们通过字符串形式读入,#再用opengl提供的一系列函数进行编译,链接,组成着色器程序,着色器程序运行在GPU,与我们程序相隔#每一个着色器程序最少提供两个shader,一个是vertex shader顶点着色器,另一个是fragment shader图元着色器#它们的工作原理以后再看,我们现在只要知道它最最基本的数据传输原理就行了#VERT -- 顶点着色器#FRAG -- 图元着色器

VERT = """#version 330 /* opengl的版本 */

in vec3 position; /* in,表示输入,从我们的数据存储区输入到GPU,

vec3,代表它是三维向量,包含三个数据x,y,z,我们可以用position.x访问第一个数据

position,则是它在vertex shader中的名字 */

void main(){

gl_Position = vec4(position,1.0) ; /* vertex shader基本任务就是计算出gl_Position值,

gl_Position在标准空间中,这个以后再说

注意gl_Position是vec4类型,也即包含四个数据,

这里我们不改变传入的值,直接传入position,外加一个1.0(w值)

另外vec4可以看作是一个构造函数*/

//计算出了gl_Position,vertex shader就完成了最基本的任务,也就可以水过了

//在初学的时候,千万不要从c语言的角度看待vertex shader哦,尽管它很像c

}"""FRAG= """#version 330 /* 同样是opengl版本 */

out vec4 outputF; /*vertex shader的基本任务是计算顶点位置,

而fragment shader的基本任务就是计算每个像素的颜色值,

注意,vertex shader处理我们输入的数据,

并输出经它更改后顶点数据(不光是位置,以后我们还会看到法线,纹理坐标等),

而fragment shader处理的是每一个像素,也即对每一个像素进行操作。

*/

void main()

{

outputF = vec4(1.0, 0.0, 0.0, 1.0);/* 比照下glClearColor四个参数吧 */

}"""

#pyopengl给我们提供的两个着色器函数#compile自然是编译的意思啦,我们下节翻译再��9c�compileProgram与compileShader的细节吧

shader =compileProgram(compileShader(VERT, GL_VERTEX_SHADER),

compileShader(FRAG,GL_FRAGMENT_SHADER))#链接成功后就可以开始用,声明glUseProgram(shader),接下来的绘图数据都将由该着色器程序完成

glUseProgram(shader)#从shader中获取position的位置,这里是重中之重,它代表着色器与我们程序的交流#函数原型为 glGetAttribLocation( GLuint ( program ) , const GLchar *( name ) )-> GLint#program就是变量所在的着色器程序,name就是它的名称#会不会很像我们拿着纸(_position),询问(glGetAttribLocation)住在shader的叫position的变量是什么地址呢?#最后一点:我们要在position前加b,指明我们传入的是字节byte类型,而非str类型

_position = glGetAttribLocation(shader, b"position")#准备顶点数据#用np.array转化list类型,并声明数据类型是np.float32,(与GLfloat不一致哦)#通常GLfloat就是32bit,但会因为机器不同而有所改变,如果你运行错误,可以试试np.float64

VERTEX = np.array([0.0, 0.0, 0.0], np.float32)#准备vertex buffer object#Gen就是generate的简称,glGenBuffers就是向opengl机制说,帮我申请1个缓存,我会通过VBO来操作它#原型是glGenBuffers( GLsizei ( n ) , GLuint * ( buffers ) )-> void#但是pyopengl帮我们包装了下,我们只需要用VBO = glGenBuffer(n)形式,如果n大于1,则VBO是个列表

VBO = glGenBuffers(1)#接着,glBindBuffer绑定我们申请的VBO,完成了两件事#1.告诉了机制,我们将会把VBO指向的缓存当作GL_ARRAY_BUFFER,也即视VBO那块缓存为存储顶点数据缓存#2.告诉机制,接下对缓存的所有操作,都作用在VBO指向的那块缓存

glBindBuffer(GL_ARRAY_BUFFER, VBO)#到底是什么操作?比如我们用glBufferData向刚刚绑定的缓存(也即VBO)传输我们的数据VERTEX#原型glBufferData( GLenum ( target ) , GLsizeiptr ( size ) , const GLvoid * ( data ) , GLenum ( usage ) )-> void#第一个还是用法,表示这块缓存是存顶点数据的,第二个是数据的字节数,因为pygl的包装,我们忽略掉了这个参数#第三个是我们要传的数据,就是刚刚定义的VERTEX,第四个数据的用法,为静态绘制,也就是我们不打算频繁改变它的值#很简单吧

glBufferData(GL_ARRAY_BUFFER, VERTEX, GL_STATIC_DRAW)#对VBO缓存完成了操作,我们可以解绑定,这是一种好习惯。实际上,如果你这时再绑定其它缓存句柄,VBO同样会被解绑定。

glBindBuffer(GL_ARRAY_BUFFER, 0)#这条语句可以查看VBO的值,VBO只是一个句柄而已,相当于缓存的把手(好吧,无视掉这比喻(╯_╰))

print(VBO)#glPointSize(5.0) #如果看不清楚点的大小,请去掉第一个#,好像glPointSize被标记了“不推荐”

glClearColor(0.0, 0.0, 0.0, 1.0)

@window.eventdefon_draw():#如1.1中说,每次进入draw开始绘制前,先要擦好黑板

glClear(GL_COLOR_BUFFER_BIT)#我们在上面得到了着色器中position的地址(其实不是地址),

#于是我们可以通过_position向GPU中的position写信啦!

#首先激活_position。就像glBindBuffer,glEnableVertexAttribArray也告诉了opengl状态机一些事

#好像只有一件,我不太确定!!! ^_^

#1.告诉opengl状态机,有权对_position进行设置了

glEnableVertexAttribArray(_position)#设置什么呢?就是设置GPU中position读取缓存数据的方式,读哪里的缓存呢?就是读当前opengl绑定的缓存

#之前因为我们的好习惯,我们把当前绑定的缓存清为0了,所以要重新绑定。照样用glBindBuffer。

#接着,介绍如何设置读取缓存数据的方式

#照样原型:glVertexAttribPointer( GLuint ( index ) , GLint ( size ) , GLenum ( type ) ,

#GLboolean ( normalized ) , GLsizei ( stride ) , const GLvoid * ( pointer ) )-> void

#长的有点吓人吧?其实都是琐碎的参数啦!

#index,就是变量所在位置,size就是每次读几个值,注意是“值”,在这个程序中是3个值,0.0,0.0,0.0,

#type,每个值的类型,就是GLfloat,在大部分机子中是4个字节,相当于每次读3个GLfloat值

#normalized,是否将其转化为GLfloat,因为VBO指的缓存中存的已经相当于GLfloat,所以我们写False

#stride,就是相邻size个type数据的间隔,也即第一个与下一块数据的第一个相邻的字节数,注意是字节byte

#我们将stride设为了0,乃们会在后续翻译中看到不同于0的值的

#最后一个pointer,即从缓存什么位置开始读,这里,我们当然是从0位开始读啦,

#因为传入的是指针,我们为方便起见,导入了ctypes,c_void_p(0)就是代表null

#如果是从第12字节开始读,就是c_void_p(12),这个还会再见的

glBindBuffer(GL_ARRAY_BUFFER, VBO)

glVertexAttribPointer(_position,3, GL_FLOAT, False, 0, ctypes.c_void_p(0))#终于混过了VAP的讲解,接下来就是最终绘制,之前所有的准备都是为了让glDrawArrays知道它该怎么画

#原型:glDrawArrays( GLenum ( mode ) , GLint ( first ) , GLsizei ( count ) )-> void

#mode,我们想画什么图元,这次是GL_POINTS,只画点图元而已,顶点和点可不一样哦,

#就像vertex shader与fragment shader处理的东西的区别

#first,从第几个顶点开始画,顶点自然是上面读出来的。

#第一个读出的顶点(实际上也只有一个)就要被画成点图元,所以first设为0

#count,指的是共有几个顶点参与绘制,1个点图元3个顶点,1个三角形图元3个顶点。这里只需1个顶点。

glDrawArrays(GL_POINTS, 0, 1)#绘毕,封印_position

glDisableVertexAttribArray(_position)#下面虽然是空白的,但还是隐藏着几个要点,有必要进行说明,

#1.pyglet什么时候调用on_draw?答:当pyglet觉得有必要调用时才会调用on_draw(废话?)

#2.只有on_draw中的指令是不会影响你屏幕的样子的,当刷屏指令执行后,屏幕才会有反应

#而这个刷新指令,你不会看到,有pyglet自主完成

#3.pyglet是默认双缓存的,至于什么是双缓存,额,百度吧,很简单的

#最后跑起来吧(只有一帧画面,没什么好跑的,别忘了,如果点太小,就反注释掉glPointSize哦)

pyglet.app.run()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值