【opengl 与 python】ogldev教程python实现 lesson 2.0 hello dots in glsl
1.1说的不那么清楚,我还是好好努力锻炼下吧
这次不会依次说所用函数,而是一条条代码往下读
在读代码前,我找了比较好的可编程opengl渲染管线工作图示,图很久远了,不知道出处,请原作者见谅吧!
你们随便看看吧,我也不是很懂!!!^_^)
大致浏览了,我们可以看ogldev tutorial2 hello dots的python实现版本了,贴代码(讲解全在注释中):
#Tutorial 2.0 这次内容很多很多,基本上都是关于opengl的指令,几乎未涉及图形原理(只是一个点嘛) #考虑再三,还是把opengl全部import出来,因为opengl的函数都是以gl为前缀的,所以不会造成多少歧义,只是性能会影响点, #不过现在也不是考虑性能的时候,舒服地学就可以了 from OpenGL.GL import * #这个是从pyopengl中导入的着色器编译包装,我准备在2.1进行讲解这两个函数 from OpenGL.GL.shaders import compileShader,compileProgram #导入numpy,我们可以很方便将传入数据,有性能加成哦! import numpy as np #pyglet不多说了吧 import pyglet #提供指针,不要看的头疼,因为它仅是个跑龙套的 import ctypes #照样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.event def on_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()好吧,2.0翻译到这里就结束了,byebye喽!
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。