【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喽!

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。