PyOpenGL的安装与操作

一、安装

        在PyOpenGL中,不能直接用pip指令安装,因为pip默认安装的是32位版本的,如果直接安装,运行一段GL代码以后,会报错,具体报错内容如下:

NullFunctionError: Attempt to call an undefined function glutInit, check for bool(glutInit) before calling

        所以这个库不能直接通过pip安装,只能去官网下载,地址:

https://www.lfd.uci.edu/~gohlke/pythonlibs/

         进入后,找到PyOpenGL:bindings to OpenGL,GLUT,and GLE. 蓝色字样标题,然后在下面下载对应自己python版本的文件(一共两个,本人用的是3.8版本)。

        下载好以后,打开cmd,cd到这两个文件所在的位置,然后通过pip来安装,安装时,pip会把释放的文件自动存在site-package中,具体的指令如下:

pip install PyOpenGL-3.1.6-cp38-cp38-win_amd64.whl
pip install PyOpenGL_accelerate-3.1.6-cp38-cp38-win_amd64.whl

二、语法以及详细注释

博文参考   
https://blog.csdn.net/weixin_42954615/article/details/113747847?spm=1001.2014.3001.5502   实践教程往这看!!!!!!!
https://blog.csdn.net/xufive/article/details/86565130  各个函数的实用方法往这看 !!!!!!!!!!

        OpenGL 函数库相关的 API 有核心库(gl)、实用库(glu)、实用工具库(glut)、辅助库(aux)、窗口库(glx、agl、wgl)和扩展函数库等。
        gl是核心,glu是对gl的部分封装。glut是为跨平台的OpenGL程序的工具包,比aux功能强大。glx、agl、wgl 是针对不同窗口系统的函数。
        扩展函数库是硬件厂商为实现硬件更新利用OpenGL的扩展机制开发的函数。

        常见的库前缀有 gl、glu、glut、aux、wgl、glx、agl 等。库前缀表示该函数属于 OpenGL 哪一个开发库。
        从函数名后面中还可以看出需要多少个参数以及参数的类型。I 代表 int 型,f 代表 float 型,d 代表 double 型,u 代表无符号整型。
        例如 glColor3f() 表示了该函数属于gl库,参数是三个浮点数。

 1. 一个实际操作与详细解释(绘制一个2d窗口)

from OpenGL.GL import *         # 核心库
from OpenGL.GLU import *        # 实用库
from OpenGL.GLUT import *       # 实用工具库


# 渲染用的函数,初始化的时候要用到
def renderfunc():
    pass

# 绘图前必须要干的事情
glutInit()    # 初始化实用工具库、主要是为了初始化窗口
glutInitDisplayMode(GLUT_RGBA|GLUT_SINGLE)
        # https://blog.csdn.net/u010087338/article/details/119343961?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165933097816782350811597%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=165933097816782350811597&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-119343961-null-null.142^v37^pc_rank_v37&utm_term=glutInitDisplayMode&spm=1018.2226.3001.4187
        # 参数有GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL
            # GLUT_RGBA表示颜色模式,另外还有GLUT_RGB 和 GLUT_INDEX模式,其中GLUT_RGBA为默认的颜色模式
        # 缓冲区类型:GLUT_DOUBLE表示使用双缓冲窗口,与之对应的是GLUT_SINGLE模式
            # 单缓冲,实际上就是将所有的绘图指令在窗口上执行,就是直接在窗口上绘图,这样的绘图效率是比较慢的,
                # 如果使用单缓冲,而电脑比较慢,屏幕会发生闪烁。一般只用于显示单独的一副非动态的图像
            # 双缓冲,实际上的绘图指令是在一个缓冲区完成,这里的绘图非常的快,在绘图指令完成之后,
                # 再通过交换指令把完成的图形立即显示在屏幕上,这就避免了出现绘图的不完整,同时效率很高。一般用于生成动画效果。
glutInitWindowSize(800,700)     # 指定窗口大小 width height
glutCreateWindow('HELLO WORlD!!终于有中文了'.encode('gbk'))    # 这里起窗口的名字,中文会乱码,要指定字体库,或者指定解码
glutDisplayFunc(renderfunc)     # 回调函数,用来响应刷新消息
glutIdleFunc(renderfunc)    # 回调函数,用来响应刷新消息,当事件队列中没有事件需要处理时,该空闲回调函数得到执行
glClearColor(0,0,0,0)       # 设置背景颜色
gluOrtho2D(-1.0,-1.0,-1.0,1.0)      # 截取图像的大小,gluOrtho2D(x_mix, x_max, y_mix, y_max)
glutMainLoop()      # 阻断了系统的正常流程,在关闭glutCreateWindow()创建的窗口后,glutMainLoop()直接用exit(0)退出程序,
                    # 而不会继续执行glutMainLoop()后的语句,有可能会造成内存泄漏,处理内存泄露方法如下
                    # 1、使用glutLeaveMainLoop()代替glutMainLoop()。
                    # 2、处理代码中exit(0)的部分。
                    # 3、在glutMainLoop之前先设置:
                    #           glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS);
                    #           然后再用glutMainLoop();则 glutMainLoop()会在退出后,继续执行其后的代码。
                    # 原文链接:https://blog.csdn.net/ronggang175/article/details/6068854

2. 创建一个3D渲染窗口,因为窗口里没东西,所以打开后会闪退

w,h = (800,800)
glutInit()
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
glutInitWindowSize(w,h)
glutCreateWindow('title')
glEnable(GL_DEPTH_TEST)     # 开启更新深度缓冲区的功能,也就是,如果通过比较后深度值发生变化了,会进行更新深度缓冲区的操作。
                            # 启动它,OpenGL就可以跟踪再Z轴上的像素,这样,它只会再那个像素前方没有东西时,才会绘画这个像素

glDepthFunc(GL_LEQUAL)      # 设置深度测试函数(GL_LEQUAL只是选项之一)
glEnable(GL_TEXTURE_2D)     # 激活纹理单元,这玩意很重要!!!!!!!!!!!!
glClearColor(0, 0, 0, 0.0)
glClearDepth(1.0)           # 指定初始深度值,如果是1,由于真实物体的每个像素都小于等于1,所以整个物体都会显示出来
                            #               如果是0,由于真实物体的每个像素都大于等于0,所以整个物体就不会显示出来
                            #               如果是0.5,真实物体深度值小于0.5的那部分才可以被看到

glDepthFunc(GL_LESS)        # 目标像素与当前像素在z方向上值大小比较,符合该函数关系的目标像素才进行绘制,否则对目标像素不予绘制,缺省GL_LESS
                            # GL_NEVER,不通过(输入的深度值不取代参考值)
                            # GL_LESS,如果输入的深度值小于参考值,则通过
                            # GL_EQUAL,如果输入的深度值等于参考值,则通过
                            # GL_LEQUAL,如果输入的深度值小于或等于参考值,则通过
                            # GL_GREATER,如果输入的深度值大于参考值,则通过
                            # GL_NOTE_QUAL,如果输入的深度值不等于参考值,则通过
                            # GL_GEQUAL,如果输入的深度值大于或等于参考值,则通过
                            # GL_ALWAYS,总是通过(输入的深度值取代参考值)

glShadeModel(GL_SMOOTH)     # 用于控制opengl中绘制指定两点间其他点颜色的过渡模式,参数一般为GL_SMOOTH(默认)
                            # 如果两点的如果两点颜色不同,GL_SMOOTH会出现过渡效果
                            # GL_FLAT 则只是以指定的某一点的单一色绘制其他所有点

glEnable(GL_CULL_FACE)      # 开启剔除操作效果,里面参数详解看下面
glCullFace(GL_BACK)         # 参数包括GL_FRONT和GL_BACK。表示禁用多边形正面或者背面上的光照、阴影和颜色计算及操作,
                            # GL_FRONT表示显示模式将适用于物体的前向面(也就是物体能看到的面)
                            # GL_BACK表示显示模式将适用于物体的后向面(也就是物体上不能看到的面)
                            # 消除不必要的渲染计算。例如某对象无论如何位置变化,我们都只能看到构成其组成的多边形的某一面时,可使用该函数
                            # 也就是说,如果六面都看的话,要glDisable(GL_CULL_FACE),下面的正方体例子中就有这个问题
                            # 里面还有一个函数,详情见 https://blog.csdn.net/iteye_9368/article/details/82170342
                            

glEnable(GL_POINT_SMOOTH)   # 启用抗锯齿,针对点
glEnable(GL_LINE_SMOOTH)    # 启用抗锯齿,针对线
glEnable(GL_POLYGON_SMOOTH) # 启用抗锯齿,针对多边形
glMatrixMode(GL_PROJECTION) # 原文链接:https://blog.csdn.net/u012861978/article/details/88552546
                            # 在OpenGL中,如果想对模型进行操作,就要对这个模型的状态(当前的矩阵)乘上这个操作对应的一个矩阵.
                            # 如果当前矩阵乘以变换矩阵(平移, 缩放, 旋转), 那相乘之后, 模型的位置被变换;
                            # 如果当前矩阵乘以投影矩阵(将3D物体投影到2D平面), 相乘后, 模型的投影方式被设置;
                            # 如果当前矩阵乘以纹理矩阵(), 模型的纹理方式被设置.
                            # 而用来指定当前矩阵, 就是glMatriMode(GLenum mode);
                            # glMatrixMode有3种模式: GL_PROJECTION 投影, GL_MODELVIEW 模型视图, GL_TEXTURE 纹理.
                            # 所以,在操作投影矩阵以前,需要调用函数:
                            # glMatrixMode(GL_PROJECTION); 将当前矩阵指定为投影矩阵
                            # 然后把矩阵设为单位矩阵:
                            # glLoadIdentity();   重置当前矩阵为单位矩阵
                            
                            # 然后调用glFrustum()或gluPerspective(),它们生成的矩阵会与当前的矩阵相乘,生成透视投影的效果;
# glFrustum(GLdouble left, GLdouble Right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far)
                            # 创建一个透视型的视景体。其操作是创建一个透视投影的矩阵,并且用这个矩阵乘以当前矩阵。
                            # 这个函数的参数只定义近裁剪平面的左下角点和右上角点的三维空间坐标,即(left,bottom,-near)和(right,top,-near);
                            # 最后一个参数far是远裁剪平面的离视点的距离值,其左下角点和右上角点空间坐标由函数根据透视投影原理自动生成。
                            # near和far表示离视点的远近,它们总为正值(near/far 必须>0)。                            
                            
# glOrtho(left, right, bottom, top, near, far)
                            # 和上面那个差不多,遵守左下、右上、近远原则
                            # glOrtho(投影变换函数)创建一个正交平行的视景体,一般用于"物体不会因为离屏幕的远近而产生大小的变换"的情况。
                            # glOrtho将产生一个矩阵,这个矩阵将填到投影矩阵上
                            # glOrtho(0,with,height,0,-100,100)
                            # OpenGL中有两个比较重要的投影变换函数,glViewport和glOrtho。
# glViewPort(x:GLInt;y:GLInt;Width:GLSizei;Height:GLSizei)
                            # 调用glViewPort函数来决定视见区域,告诉OpenGL应把渲染之后的图形绘制在窗体的哪个部位。
                            # 当视见区域是整个窗体时,OpenGL将把渲染结果绘制到整个窗口
                            # 参数X,Y指定了视见区域的左下角在窗口中的位置,一般情况下为(0,0),Width和Height指定了视见区域的宽度和高度
                            
                            
                            
glHint(GL_POINT_SMOOTH_HINT,GL_NICEST)
                            # 原文链接:https://blog.csdn.net/l_andy/article/details/51773490
                            # 这玩意是配合抗锯齿来使用的,参数target说明控制什么行为:
                            # GL_POINT_SMOOTH_HINT、GL_LINE_SMOOTH_HINT和GL_POLYGON_SMOOTH_HINT分别指定点、线和多边形的采样质量;
                            # GL_FOG_HINT指出雾是按像素进行(GL_NICEST)还是按顶点进行(GL_FASTEST);
                            # GL_PERSPECTIVE_CORRECTION_HINT指定了颜色纹理插值的质量并可纠正由单纯线性插值所带来的一些视觉错误。
                            # 参数hint可以是:GL_FASTEST(给出最有效的选择)、GL_NICEST(给出最高质量的选择)、GL_DONT_CARE(没有选择)。
glHint(GL_LINE_SMOOTH_HINT,GL_NICEST)
glHint(GL_POLYGON_SMOOTH_HINT,GL_FASTEST)
glLoadIdentity()
gluPerspective(45.0,w / h, 0.1, 100.0)
glMatrixMode(GL_MODELVIEW)


# glFlush()
# glutSwapBuffers()
                # glFlush(),强制马上输出命令执行的结果,而不是存储在缓冲区中,继续等待其他OpenGL命令
                # 当执行双缓冲交换的时候,使用glutSwapBuffers。
                # 但是在有 glutSwapBuffers 的情况下, 不需要 glFlush 就可以达到同样的效果,因为我们执行双缓冲交换的时候,就隐形的执行了一次刷新操作。
# glutpostredisplay()
                # glutPostRedisplay 标记当前窗口需要重新绘制。通过glutMainLoop下一次循环时,
                # glutPostRedisplay函数会标记当前窗体来重新显示,它会促使主循环尽快的调用完显示函数
                # 窗口显示将被回调以重新显示窗口的正常面板。多次调用glutPostRedisplay,在下一个显示回调只产生单一的重新显示回调
                # 直到所有源代码都使用显示函数作为空闲函数.这意味着当没有任何事件要处理的时候GLUT会调用显示函数,也就是说,它会尽可能频繁的调用显示函数
                # https://blog.csdn.net/hongqiang200/article/details/17113361/

3.  经典小茶壶(带注释版本)

# 经典小茶壶
#           | Y轴
#           |
#           |
#           |____________X轴
#          /
#         /
#        /  Z轴

def drawFunc():
   glClear(GL_COLOR_BUFFER_BIT) # 清理画布,将所有像素点还原未底色,有四个canshu
                                # GL_COLOR_BUFFER_BIT       指定当前被激活为写操作的颜色缓存
                                # GL_DEPTH_BUFFER_BIT       指定深度缓存
                                # GL_ACCUM_BUFFER_BIT       指定累加缓存
                                # GL_STENCIL_BUFFER_BIT     指定模板缓存指定模板缓存
   glRotatef(0.01, 1, 1, 1)     # 指定旋转的方向,glRotatef(GLfloat angle,GLfloat x,GLfloat y,GLfloat z),第一个参数是速度,其他的是角度
                                # 想象:从 坐标(0,0,0)即原点,引出一条线到(1,0,0),然后茶壶绕着这条线逆时针旋转,具体轴线见上图
   glutWireTeapot(0.5)   # 这玩意就是茶壶
   glFlush()    # 用来强制刷新缓冲,保证绘图命令将被执行,而不是存储在缓冲区中等待其他的OpenGL命令

glutInit()
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA)
glutInitWindowSize(400, 400)
glutCreateWindow(b"First")
glutDisplayFunc(drawFunc)
glutIdleFunc(drawFunc)
glClearColor(0.1,0.8,0.05,1)
glutMainLoop()

 4. 开始绘图,第三段时绘图时都要用到的代码,cv就好

glBegin(mode)开始绘图,mode可以为:
      GL_POINTS,很多个点
      GL_LINES,很多条线段
      GL_LINE_STRIP,连续线段
      GL_LINE_LOOP、GL_POLYGON,多边形 (自动封口)
      GL_TRIANGLES,很多个三角形
      GL_TRIANGLE_STRIP,一串连续三角形(见示意图)
      GL_TRIANGLE_FAN,一串连续三角形(见示意图)
      GL_QUADS,很多四边形(注意不一定是矩形)
      GL_QUAD_STRIP,一串连续四边形
glColor3f(r,g,b)设置颜色,均为0~1之间的整数
glVertex2f(x,y)绘制一个点(单独一个点或者一个顶点)
glEnd()结束绘制
glPolygonMode(face,mode)  其中face为GL_FRONT(正面)或GL_BACK(背面,在2d绘图里没啥用), mode 为GL_LINE(只有线)或GL_FILL(不仅有线,还有填充)

 绘制一个2D的图

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

def init():
    glClearColor(0.0, 0.0, 0.0, 1.0)
    gluOrtho2D(-1.0, 1.0, -1.0, 1.0)

def drawFunc():
    glClear(GL_COLOR_BUFFER_BIT)

    glColor3f(0.0, 1.0, 1.0)
    glPolygonMode(GL_FRONT, GL_LINE)
    glPolygonMode(GL_BACK, GL_FILL)
    glBegin(GL_QUADS)
    glVertex2f(-0.8, -0.8)
    glVertex2f(-0.8, 0.8)
    glVertex2f(0.8, 0.8)
    glVertex2f(0.8, -0.8)
    glEnd()
    glFlush()

glutInit()
glutInitDisplayMode(GLUT_RGBA|GLUT_SINGLE)
glutInitWindowSize(400, 400)
glutCreateWindow(b"My OpenGL window")
glutDisplayFunc(drawFunc)
init()
glutMainLoop()
glEnable(GL_DEPTH_TEST)
glClearDepth(1.0)
glDepthFunc(GL_LESS)
glShadeModel(GL_SMOOTH)
glEnable(GL_CULL_FACE)
glCullFace(GL_BACK)
glEnable(GL_POINT_SMOOTH)
glEnable(GL_LINE_SMOOTH)
glEnable(GL_POLYGON_SMOOTH)
glMatrixMode(GL_PROJECTION)
glHint(GL_POINT_SMOOTH_HINT,GL_NICEST)
glHint(GL_LINE_SMOOTH_HINT,GL_NICEST)
glHint(GL_POLYGON_SMOOTH_HINT,GL_FASTEST)
glMatrixMode(GL_MODELVIEW)
# 如果要添加材质,一定要把下面这个也给初始化了
glEnable(GL_TEXTURE_2D)

5. 一个立方体示意,尤其注意被注释的那两行

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *


def drawFunc():
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
    glRotatef(0.01, 1, 1, 0)
    vertex = [[[0, 0, 1], [1, 0, 1], [1, 1, 1], [0, 1, 1]], [[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0]],
              [[0, 1, 0], [0, 1, 1], [1, 1, 1], [1, 1, 0]], [[0, 0, 0], [1, 0, 0], [1, 0, 1], [0, 0, 1]],
              [[1, 0, 0], [1, 1, 0], [1, 1, 1], [1, 0, 1]], [[0, 0, 0], [0, 0, 1], [0, 1, 1], [0, 1, 0]]]
    for i in range(len(vertex)):
        glColor3f(1, 1, 1)
        glBegin(GL_QUADS)
        glVertex3f(*vertex[i][0])       # 绘制顶点
        glVertex3f(*vertex[i][1])
        glVertex3f(*vertex[i][2])
        glVertex3f(*vertex[i][3])
        glEnd()
    glFlush()


glutInit()
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA)
glutInitWindowSize(800, 800)
glutCreateWindow(b"OpenGL")
glClearColor(0, 0, 0, 0)
glEnable(GL_DEPTH_TEST)
glClearDepth(1)
glDepthFunc(GL_LESS)
glShadeModel(GL_SMOOTH)
glEnable(GL_CULL_FACE)
glCullFace(GL_BACK)
glEnable(GL_POINT_SMOOTH)
glEnable(GL_LINE_SMOOTH)
glEnable(GL_POLYGON_SMOOTH)
glMatrixMode(GL_PROJECTION)
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST)
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST)
glHint(GL_POLYGON_SMOOTH_HINT, GL_FASTEST)
glMatrixMode(GL_MODELVIEW)
glutDisplayFunc(drawFunc)
glutIdleFunc(drawFunc)
glTranslatef(0,0,0)
glutMainLoop()

 6. 给立方体加上材质

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from PIL import Image
def drawFunc():
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)

    glRotatef(0.03, 1, 0, 0)
    vertex = [[[0,0,1],[1,0,1],[1,1,1],[0,1,1]],
              [[0,1,0],[0,1,1],[1,1,1],[1,1,0]],
              [[0,0,0],[0,1,0],[1,1,0],[1,0,0]],
              [[0,0,0],[1,0,0],[1,0,1],[0,0,1]],
              [[1,0,0],[1,1,0],[1,1,1],[1,0,1]],
              [[0,0,0],[0,0,1],[0,1,1],[0,1,0]]]
    for i in range(len(vertex)):
        glBindTexture(GL_TEXTURE_2D,1)
        glBegin(GL_QUADS)
        glTexCoord2f(0.0, 0.0)
        glVertex3f(*vertex[i][0])
        glTexCoord2f(1.0, 0.0)
        glVertex3f(*vertex[i][1])
        glTexCoord2f(1.0, 1.0)
        glVertex3f(*vertex[i][2])
        glTexCoord2f(0.0, 1.0)
        glVertex3f(*vertex[i][3])
        glEnd()

    glFlush()

glutInit()
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA | GLUT_DEPTH)
glutInitWindowSize(800, 800)
glutCreateWindow(b"OpenGL")
glClearColor(0,0,0,0)
glEnable(GL_DEPTH_TEST)
glClearDepth(1)
# glDepthFunc(GL_GEQUAL)
# glDepthFunc(GL_LESS)
# glDepthFunc(GL_LEQUAL)
glShadeModel(GL_SMOOTH)

# glDisable(GL_CULL_FACE)
# glEnable(GL_CULL_FACE)
# glFrontFace(GL_CW)
# glCullFace(GL_FRONT)
# glCullFace(GL_BACK)

glPolygonMode(GL_BACK,GL_POINT)
glEnable(GL_POINT_SMOOTH)
glEnable(GL_LINE_SMOOTH)
glEnable(GL_POLYGON_SMOOTH)
glMatrixMode(GL_PROJECTION)
glHint(GL_POINT_SMOOTH_HINT,GL_NICEST)
glHint(GL_LINE_SMOOTH_HINT,GL_NICEST)
glHint(GL_POLYGON_SMOOTH_HINT,GL_FASTEST)
# glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST)
glMatrixMode(GL_MODELVIEW)
glEnable(GL_TEXTURE_2D)

img = Image.open('b.png')
width, height = img.size
img = img.tobytes('raw','RGB',0,-1)
glGenTextures(2)        # 根据纹理参数返回N个纹理索引
glBindTexture(GL_TEXTURE_2D, 1)     # 告诉OpenGL下面代码中对2D纹理的任何设置都是针对索引为1的纹理的
glTexImage2D(GL_TEXTURE_2D, 0, 4, width, height, 0, GL_RGB,GL_UNSIGNED_BYTE,img)
                # 绘制一个2D纹理
                # 0             代表图像的详细程度, 默认为0即可
                # 3             颜色成分R(红色分量)、G(绿色分量)、B(蓝色分量)三部分,若为4则是R(红色分量)、G(绿色分量)、B(蓝色分量)、Alpha
                # width             纹理的宽度
                # height            纹理的高度
                # 0             边框的值
                # GL_RGB          告诉OpenGL图像数据由红、绿、蓝三色数据组成
                # GL_UNSIGNED_BYTE   组成图像的数据是无符号字节类型
                # img               告诉OpenGL纹理数据的来源,此例中指向存放在TextureImage[0]记录中的数据
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP)  
                # 指定贴图方式,并滤波,详情 https://blog.csdn.net/flycatdeng/article/details/82595267
                # 第一个参数,只能取GL_TEXTURE_1D或者GL_TEXTURE_2D
                # 第二个参数,GL_TEXTURE_WRAP_S: S方向上的贴图模式,T就是T方向上贴图
                #           GL_TEXTURE_MAG_FILTER: 放大过滤
                #           GL_TEXTURE_MIN_FILTER: 缩小过滤
                #           
                # 第三个参数,GL_LINEAR: 线性过滤, 使用距离当前渲染像素中心最近的4个纹素加权平均值
                #           GL_LINEAR_MIPMAP_NEAREST: 使用GL_NEAREST对最接近当前多边形的解析度的两个层级贴图进行采样,然后用这两个值进行线性插值
                #           GL_NEAREST: 这种方式没有真正进行滤波。它只占用很小的处理能力,看起来也很差。唯一的好处是在很快和很慢的机器上都可以正常运行
                #           
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_REPEAT)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE, GL_DECAL)
glutDisplayFunc(drawFunc)
glutIdleFunc(drawFunc)
glLoadIdentity()  # 重置矩阵为单位矩阵
glTranslatef(0, -0.5, 0)  # 移动模型位置
glPolygonMode(GL_BACK,GL_FILL)
glutMainLoop()

7. 上面是让正方体自己转起来,这个是让自己的摄像机旋转起来,不知道为什么,这个运行不起来,以后再来看 

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from PIL import Image
import math
theta,phi = (60,30)
pos = (3,60,60)
def target():
    global theta,phi
    x = math.sin(theta) * math.cos(phi)
    y = math.sin(theta) * math.sin(phi)
    z = math.cos(theta)
    return x,y,z
def setLookAt():
    global pos
    tar = target()
    gluLookAt(*pos,*tar,0,1,0)
    glLoadIdentity()
def drawFunc():

    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
    setLookAt()
    # glRotatef(0.01,0,1,0)
    vertex = [[[0,0,1],[1,0,1],[1,1,1],[0,1,1]],[[0,0,0],[0,1,0],[1,1,0],[1,0,0]],[[0,1,0],[0,1,1],[1,1,1],[1,1,0]],[[0,0,0],[1,0,0],[1,0,1],[0,0,1]],[[1,0,0],[1,1,0],[1,1,1],[1,0,1]],[[0,0,0],[0,0,1],[0,1,1],[0,1,0]]]
    for i in range(len(vertex)):
        glBindTexture(GL_TEXTURE_2D,1)
        glBegin(GL_QUADS)
        glTexCoord2f(0.0, 0.0)
        glVertex3f(*vertex[i][0])
        glTexCoord2f(1.0, 0.0)
        glVertex3f(*vertex[i][1])
        glTexCoord2f(1.0, 1.0)
        glVertex3f(*vertex[i][2])
        glTexCoord2f(0.0, 1.0)
        glVertex3f(*vertex[i][3])
        glEnd()
    glFlush()
glutInit()
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA)
glutInitWindowSize(800, 800)
glutCreateWindow(b"OpenGL")
glClearColor(0,0,0,0)
# glEnable(GL_DEPTH_TEST)
# glClearDepth(1)
# glDepthFunc(GL_LESS)
# glShadeModel(GL_SMOOTH)
# glEnable(GL_CULL_FACE)
# glCullFace(GL_BACK)
# glEnable(GL_POINT_SMOOTH)
# glEnable(GL_LINE_SMOOTH)
# glEnable(GL_POLYGON_SMOOTH)
# glMatrixMode(GL_PROJECTION)
# glHint(GL_POINT_SMOOTH_HINT,GL_NICEST)
# glHint(GL_LINE_SMOOTH_HINT,GL_NICEST)
# glHint(GL_POLYGON_SMOOTH_HINT,GL_FASTEST)
# glMatrixMode(GL_MODELVIEW)
glEnable(GL_TEXTURE_2D)
img = Image.open('b.png')
width, height = img.size
img = img.tobytes('raw','RGB',0,-1)
glGenTextures(2)
glBindTexture(GL_TEXTURE_2D, 1)
glTexImage2D(GL_TEXTURE_2D, 0, 4,width,height, 0, GL_RGB,GL_UNSIGNED_BYTE,img)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_REPEAT)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE, GL_DECAL)
glutDisplayFunc(drawFunc)
glutIdleFunc(drawFunc)
glutMainLoop()

8.  OpenGL鼠标与键盘监听事件

OpenGL支持的事件很少,都是由GLUT提供的,有:
键盘按下(glutKeyboardFunc、glutSpecialFunc)
鼠标移动(glutMotionFunc、glutPassiveMotionFunc)
鼠标按下、放开(glutMouseFunc)
注册方式都是register(func),例如 glutKeyboardFunc(keyboard)
原文链接:https://blog.csdn.net/weixin_42954615/article/details/113767921

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

def drawFunc():
    glClear(GL_COLOR_BUFFER_BIT)
    glRotatef(0.01, 0, 1, 0)
    glutWireTeapot(0.5)
    glFlush()

def procKeyboard(key, x, y):    # 函数中key代表按键,x和y代表的是鼠标的位置
    print('KEYBOARD RECALL:')
    print('MOUSE:%s,%s' % (x, y))
    print('KEY:%s' % key.decode())

glutInit()
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA)
glutInitWindowSize(400, 400)
glutCreateWindow(b"First")
glutKeyboardFunc(procKeyboard)  # 监听键盘的动作
glutDisplayFunc(drawFunc)
glutIdleFunc(drawFunc)
glutMainLoop()

 一些其他的监听键位,其与glutKeyboardFunc相似,但只处理特殊键↓ \downarrow↓

GLUT_KEY_BEGIN
GLUT_KEY_DELETE
GLUT_KEY_DOWN
GLUT_KEY_END
GLUT_KEY_F1
GLUT_KEY_F2
GLUT_KEY_F3
GLUT_KEY_F4
GLUT_KEY_F5
GLUT_KEY_F6
GLUT_KEY_F7
GLUT_KEY_F8
GLUT_KEY_F9
GLUT_KEY_F10
GLUT_KEY_F11
GLUT_KEY_F12
GLUT_KEY_HOME
GLUT_KEY_INSERT
GLUT_KEY_LEFT
GLUT_KEY_NUM_LOCK
GLUT_KEY_PAGE_DOWN
GLUT_KEY_PAGE_UP
GLUT_KEY_REPEAT_DEFAULT
GLUT_KEY_REPEAT_OFF
GLUT_KEY_REPEAT_ON
GLUT_KEY_RIGHT
GLUT_KEY_UP

特殊键位监听的例子

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *


def drawFunc():
    glClear(GL_COLOR_BUFFER_BIT)
    glRotatef(1, 0, 1, 0)
    glutWireTeapot(0.5)
    glFlush()


def procKeyboard(key, x, y):
    print('KEYBOARD RECALL:')
    print('MOUSE:%s,%s' % (x, y))
    print('KEY:%s' % key)


glutInit()
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA)
glutInitWindowSize(400, 400)
glutCreateWindow(b"First")
glutSpecialFunc(procKeyboard)    # 一些特殊键位的监听
glutDisplayFunc(drawFunc)
glutIdleFunc(drawFunc)
glutMainLoop()

 将鼠标拖拽的位置返回

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *


def drawFunc():
    glClear(GL_COLOR_BUFFER_BIT)
    glRotatef(1, 0, 1, 0)
    glutWireTeapot(0.5)
    glFlush()


def procMotion(x, y):
    print('MOTION RECALL:')
    print('MOUSE:%s,%s' % (x, y))


glutInit()
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA)
glutInitWindowSize(400, 400)
glutCreateWindow(b"First")
glutMotionFunc(procMotion)  # 鼠标拖拽位置
glutDisplayFunc(drawFunc)
glutIdleFunc(drawFunc)
glutMainLoop()

 鼠标在界面移动位置实时返回

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *


def drawFunc():
    glClear(GL_COLOR_BUFFER_BIT)
    glRotatef(1, 0, 1, 0)
    glutWireTeapot(0.5)
    glFlush()


def procMotion(x, y):
    print('MOTION RECALL:')
    print('MOUSE:%s,%s' % (x, y))


glutInit()
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA)
glutInitWindowSize(400, 400)
glutCreateWindow(b"First")
glutPassiveMotionFunc(procMotion)   # 鼠标移动位置实时返回
glutDisplayFunc(drawFunc)
glutIdleFunc(drawFunc)
glutMainLoop()

鼠标点击位置实时返回

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *


def drawFunc():
    glClear(GL_COLOR_BUFFER_BIT)
    glRotatef(1, 0, 1, 0)
    glutWireTeapot(0.5)
    glFlush()


def procMouse(btn, state, x, y):
    st = 'pressed' if (state == GLUT_DOWN) else 'released'
    print('MOTION RECALL:')
    print('button %s %s' % (btn, st))
    print('MOUSE:%s,%s' % (x, y))


glutInit()
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA)
glutInitWindowSize(400, 400)
glutCreateWindow(b"First")
glutMouseFunc(procMouse)        # 鼠标按键点击位置实时返回
glutDisplayFunc(drawFunc)
glutIdleFunc(drawFunc)
glutMainLoop()

 9. 用来屏幕上像素操作的

glDrawPixels(GLsizei  width,  GLsizei  height,  GLenum  format,  GLenum  type,  const GLvoid *  data)
        width:像素图宽度
        height:像素图高度
        format:GL_RGB, GL_BGR, GL_RGBA, GL_BGRA, GL_RED, GL_GREEN, GL_BLUE中的一个,表示颜色模式,推荐使用GL_RGB
        type:表示数据类型,推荐使用GL_UNSIGNED_BYTE,详情见 https://blog.csdn.net/weixin_42954615/article/details/113832148
        data像素图,c++中使用列表指针,python中使用bytearray

glRasterPos2i(GLint x,GLint y)  # x和y指定绘制的位置

glReadPixels(GLint  x,  GLint  y,  GLsizei  width,  GLsizei  height,  GLenum  format,  GLenum  type,  GLvoid *  data)
        储存指定窗口大小中的像素,也就是截屏
        x和y表示起始位置
        width和height表示长和高
        format表示颜色模式,同glDrawPixels中的format
        type表示数据类型,同glDrawPixels中的type
        data在c++中表示存储像素数据的列表,在python中没有此参数,由返回值代替
        OpenGL中,像素操作的坐标为left bottom坐标,而不是大家所熟悉的left top坐标
        OpenGL中,屏幕坐标原点在正中央,而不是左上角

 用glRasterPos2i,glDrawPixels单独让图片存在

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from PIL import Image
import numpy as np
def convert_img():
    img = Image.open('a.jpg').convert('RGB')
    arr = np.array(img).tolist()
    w,h = img.size
    buf = bytearray(w * h * 3)
    for i in range(h):
        for j in range(w):
            ind = w * i + j
            buf[ind * 3] = arr[i][j][0]
            buf[ind * 3 + 1] = arr[i][j][1]
            buf[ind * 3 + 2] = arr[i][j][2]
    return buf,w,h
img,w,h = convert_img()
def drawFunc():
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
    glRotatef(0.01, 1, 1, 0)
    vertex = [[[0,0,1],[1,0,1],[1,1,1],[0,1,1]],[[0,0,0],[0,1,0],[1,1,0],[1,0,0]],[[0,1,0],[0,1,1],[1,1,1],[1,1,0]],[[0,0,0],[1,0,0],[1,0,1],[0,0,1]],[[1,0,0],[1,1,0],[1,1,1],[1,0,1]],[[0,0,0],[0,0,1],[0,1,1],[0,1,0]]]
    for i in range(len(vertex)):
        glColor3f(65 / 255,105 / 255,1)
        glBegin(GL_QUADS)
        glVertex3f(*vertex[i][0])
        glVertex3f(*vertex[i][1])
        glVertex3f(*vertex[i][2])
        glVertex3f(*vertex[i][3])
        glEnd()
    glRasterPos2i(0,0)
    glDrawPixels(w,h,GL_RGB,GL_UNSIGNED_BYTE,img)
    glFlush()
glutInit()
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA)
glutInitWindowSize(400, 400)
glutCreateWindow(b"OpenGL")
glClearColor(0,0,0,0)
glEnable(GL_DEPTH_TEST)
glClearDepth(1.0)
glDepthFunc(GL_LESS)
glShadeModel(GL_SMOOTH)
glEnable(GL_CULL_FACE)
glCullFace(GL_BACK)
glEnable(GL_POINT_SMOOTH)
glEnable(GL_LINE_SMOOTH)
glEnable(GL_POLYGON_SMOOTH)
glMatrixMode(GL_PROJECTION)
glHint(GL_POINT_SMOOTH_HINT,GL_NICEST)
glHint(GL_LINE_SMOOTH_HINT,GL_NICEST)
glHint(GL_POLYGON_SMOOTH_HINT,GL_FASTEST)
glMatrixMode(GL_MODELVIEW)
glutDisplayFunc(drawFunc)
glutIdleFunc(drawFunc)
glutMainLoop()

窗口截图功能 

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from PIL import Image
import numpy as np
def drawFunc():
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
    glRotatef(0.01, 1, 1, 0)
    vertex = [[[0,0,1],[1,0,1],[1,1,1],[0,1,1]],[[0,0,0],[0,1,0],[1,1,0],[1,0,0]],[[0,1,0],[0,1,1],[1,1,1],[1,1,0]],[[0,0,0],[1,0,0],[1,0,1],[0,0,1]],[[1,0,0],[1,1,0],[1,1,1],[1,0,1]],[[0,0,0],[0,0,1],[0,1,1],[0,1,0]]]
    for i in range(len(vertex)):
        glColor3f(65 / 255,105 / 255,1)
        glBegin(GL_QUADS)
        glVertex3f(*vertex[i][0])
        glVertex3f(*vertex[i][1])
        glVertex3f(*vertex[i][2])
        glVertex3f(*vertex[i][3])
        glEnd()
    glFlush()
def keyboard(key,x,y):
    if(key == b's'):
        img = glReadPixels(0,0,400,400,GL_RGB,GL_UNSIGNED_INT)
        img = Image.fromarray(np.array(img,dtype=np.uint8))
        img.save('screenshot.jpg')
glutInit()
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA)
glutInitWindowSize(400, 400)
glutCreateWindow(b"OpenGL")
glClearColor(0,0,0,0)
glEnable(GL_DEPTH_TEST)
glClearDepth(1.0)
glDepthFunc(GL_LESS)
glShadeModel(GL_SMOOTH)
glEnable(GL_CULL_FACE)
glCullFace(GL_BACK)
glEnable(GL_POINT_SMOOTH)
glEnable(GL_LINE_SMOOTH)
glEnable(GL_POLYGON_SMOOTH)
glMatrixMode(GL_PROJECTION)
glHint(GL_POINT_SMOOTH_HINT,GL_NICEST)
glHint(GL_LINE_SMOOTH_HINT,GL_NICEST)
glHint(GL_POLYGON_SMOOTH_HINT,GL_FASTEST)
glMatrixMode(GL_MODELVIEW)
glutDisplayFunc(drawFunc)
glutIdleFunc(drawFunc)
glutKeyboardFunc(keyboard)
glutMainLoop()

颜色拾取:

https://blog.csdn.net/weixin_42954615/article/details/113899170

灯光:

https://blog.csdn.net/gamesdev/article/details/12112761
https://blog.csdn.net/weixin_42954615/article/details/113901093

半透明:

https://blog.csdn.net/weixin_42954615/article/details/114123071

其他牛逼操作:

https://blog.csdn.net/xufive/article/details/86565130
加载材质时将
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,     GL_DECAL)
替换成
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE, GL_REPLACE)

画之前加入
glClearDepth(1.0)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

画之后加入
glDisable(GL_BLEND)

即可。

10.  VBO概念,原文链接:https://blog.csdn.net/xufive/article/details/86565130

# 六面体数据
# ------------------------------------------------------
#    v4----- v5
#   /|      /|
#  v0------v1|
#  | |     | |
#  | v7----|-v6
#  |/      |/
#  v3------v2


import numpy as np
# 顶点集
vertices = np.array([
    -0.5, 0.5, 0.5,   0.5, 0.5, 0.5,   0.5, -0.5, 0.5,   -0.5, -0.5, 0.5, # v0-v1-v2-v3
    -0.5, 0.5, -0.5,  0.5, 0.5, -0.5,  0.5, -0.5, -0.5,  -0.5, -0.5, -0.5 # v4-v5-v6-v7
], dtype=np.float32)

# 索引集
indices = np.array([
    0, 1, 2, 3, # v0-v1-v2-v3 (front)
    4, 5, 1, 0, # v4-v5-v1-v0 (top)
    3, 2, 6, 7, # v3-v2-v6-v7 (bottom)
    5, 4, 7, 6, # v5-v4-v7-v6 (back)
    1, 5, 6, 2, # v1-v5-v6-v2 (right)
    4, 0, 3, 7  # v4-v0-v3-v7 (left)
], dtype=np.int)


# 那么在GPU上创建的VBO就如下所示
from OpenGL.arrays import vbo

vbo_vertices = vbo.VBO(vertices)
vbo_indices = vbo.VBO(indices, target=GL_ELEMENT_ARRAY_BUFFER)

创建 顶点 VBO 时,默认 target=GL_ARRAY_BUFFER, 而创建索引 VBO 时,target=GL_ELEMENT_ARRAY_BUFFER,
因为顶点的数据类型是 np.float32,索引的数据类型是np.int。

其他的不想写了,去看博客

11. OpenGL和pygame的一次示例

import pygame
from pygame.locals import *

from OpenGL.GL import *
from OpenGL.GLU import *

verticies = (  # 顶点坐标  瞎写的
    (2, 0, 0),
    (0, 1, 0),
    (0, 1, 1),
    (0, 0, 1)
)

edges = (  # 边的顺序
    (0, 1),
    (0, 2),
    (0, 3),
    (1, 2),
    (2, 3),
    (3, 1)
)


def Cube():
    glColor3f(1.0, 0.0, 0.0)  # 设置颜色
    glBegin(GL_LINES)  # glBegin和glEnd()是绘图的必备函数
    for edge in edges:
        for vertex in edge:
            glVertex3fv(verticies[vertex])  # 这个函数就是连点,这个函数执行两次画一条线,两点确定一条直线,参数为三维的坐标
    glEnd()


def main():
    pygame.init()
    display = (800, 600)
    pygame.display.set_mode(display, DOUBLEBUF | OPENGL)
    glClearColor(1.0, 1.0, 1.0, 1.0)  # 设置背景颜色
    gluPerspective(45, (display[0] / display[1]), 0.1, 50.0)
    # Z轴就是我们眼睛到屏幕方向的轴,负是远,正是近,其实就是让物体相对与屏幕在XYZ各方向移动几个距离
    glTranslatef(0, 0, -5)
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:  # 退出事件响应
                pygame.quit()
                quit()
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)  # 用来删除就得画面,清空画布
        Cube()  # 创建模型
        glRotatef(1, 0, 1, 1)  # 旋转矩阵

        pygame.display.flip()  # 显示画面
        pygame.time.wait(10)  # 10ms刷新一次


main()

12. PyQT和OpenGL的一次示例

import sys
# from PyQt6.QtOpenGLWidgets import QOpenGLWidget
# from PyQt6.QtWidgets import QApplication
# from PyQt6.QtGui import QIcon
# from PyQt6.QtCore import Qt, QPoint

from PySide2.QtOpenGL import QGLWidget
# from PySide2.QtOpenGLWidgets import QOpenGLWidget
from PySide2.QtWidgets import QApplication
from PySide2.QtGui import QIcon
from PySide2.QtCore import Qt, QPoint

import numpy as np
from OpenGL.GL import *
from OpenGL.GLU import *


class MyGLWidget(QGLWidget):
    """从QOpenGLWidget类派生的桌面应用程序窗口类"""

    def __init__(self):
        """构造函数"""

        super(MyGLWidget, self).__init__()

        self.setWindowTitle('集成OpenGL')
        self.setWindowIcon(QIcon('res/qt.png'))
        self.resize(800, 600)
        self.show()

    def initializeGL(self):
        """重写GL初始化函数"""

        glClearColor(0.0, 0.0, 0.2, 1.0)  # 设置画布背景色
        glEnable(GL_DEPTH_TEST)  # 开启深度测试,实现遮挡关系
        glDepthFunc(GL_LEQUAL)  # 设置深度测试函数

        self.oecs = [0.0, 0.0, 0.0]  # 视点坐标系原点
        self.cam_pos = [0.0, 0.0, 5.0]  # 相机位置
        self.cam_up = [0.0, 1.0, 0.0]  # 指向上方的向量
        self.dist = 5.0  # 相机距离视点坐标系原点的距离
        self.azim = 0.0  # 初始方位角
        self.elev = 0.0  # 初始高度角

        self.leftdown = False  # 鼠标左键按下
        self.mpos = QPoint(0, 0)  # 鼠标位置

    def paintGL(self):
        """重写绘制函数"""

        # 根据窗口宽高计算视锥体
        k = self.csize[0] / self.csize[1]
        view = (-k, k, -1, 1, 3, 10) if k > 1 else (-1, 1, -1 / k, 1 / k, 3, 10)

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)  # 清除屏幕及深度缓存
        glViewport(0, 0, *self.csize)  # 设置视口

        # 设置投影(透视投影)
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        glFrustum(*view)

        # 设置视点
        gluLookAt(*self.cam_pos, *self.oecs, *self.cam_up)

        # 六面体顶点编号示意图
        #    v4----- v7
        #   /|      /|
        #  v0------v3|
        #  | |     | |
        #  | v5----|-v6
        #  |/      |/
        #  v1------v2

        # 六面体顶点集
        vertices = [
            [-1, 1, 1], [-1, -1, 1], [1, -1, 1], [1, 1, 1],  # v0-v1-v2-v3
            [-1, 1, -1], [-1, -1, -1], [1, -1, -1], [1, 1, -1]  # v4-v5-v6-v7
        ]

        # 顶点集对用的颜色
        colors = [
            [0.8, 0, 0], [0, 0.8, 0], [0, 0, 0.8], [1, 1, 0.2],  # v0-v1-v2-v3
            [1, 0.2, 1], [0.2, 1, 1], [0.5, 1, 0], [0, 1, 0.5]  # v4-v5-v6-v7
        ]

        # 顶点构成四边形的索引集
        indices = [
            0, 1, 2, 3,  # v0-v1-v2-v3 (front)
            4, 0, 3, 7,  # v4-v0-v3-v7 (top)
            1, 5, 6, 2,  # v1-v5-v6-v2 (bottom)
            7, 6, 5, 4,  # v7-v6-v5-v4 (back)
            3, 2, 6, 7,  # v3-v2-v6-v7 (right)
            4, 5, 1, 0  # v4-v5-v1-v0 (left)
        ]

        glBegin(GL_QUADS)  # 开始绘制四角面
        for i in indices:
            glColor3f(*colors[i])
            glVertex3f(*vertices[i])
        glEnd()  # 结束绘制四角面

    def resizeGL(self, width, height):
        """重写改变窗口事件函数"""

        self.csize = (width, height)

    def mousePressEvent(self, evt):
        """重写鼠标按键被按下事件函数"""

        if evt.buttons() == Qt.MouseButton.LeftButton:
            self.leftdown = True
            self.mpos = evt.position()

    def mouseReleaseEvent(self, evt):
        """重写鼠标按键被释放事件函数"""

        if evt.buttons() == Qt.MouseButton.LeftButton:
            self.leftdown = False

    def mouseMoveEvent(self, evt):
        """重写鼠标移动事件函数"""

        if self.leftdown:
            pos = evt.position()
            dx, dy = pos.x() - self.mpos.x(), pos.y() - self.mpos.y()
            self.mpos = pos

            azim = self.azim - self.cam_up[1] * (180 * dx / self.csize[0])
            elev = self.elev + 90 * dy / self.csize[1]
            self.update_cam_and_up(azim=azim, elev=elev)
            self.update()

    def wheelEvent(self, evt):
        """重写鼠标滚轮事件函数"""

        if evt.angleDelta().y() < 0:
            dist = self.dist * 1.02
        else:
            dist = self.dist * 0.98

        self.update_cam_and_up(dist=dist)
        self.update()

    def update_cam_and_up(self, dist=None, azim=None, elev=None):
        """根据相机与ECS原点的距离、方位角、仰角等参数,重新计算相机位置和up向量"""

        if not dist is None:
            self.dist = dist

        if not azim is None:
            self.azim = (azim + 180) % 360 - 180

        if not elev is None:
            self.elev = (elev + 180) % 360 - 180

        azim, elev = np.radians(self.azim), np.radians(self.elev)
        d = self.dist * np.cos(elev)

        self.cam_pos[1] = self.dist * np.sin(elev) + self.oecs[1]
        self.cam_pos[2] = d * np.cos(azim) + self.oecs[2]
        self.cam_pos[0] = d * np.sin(azim) + self.oecs[0]
        self.cam_up[1] = 1.0 if -90 <= self.elev <= 90 else -1.0


if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = MyGLWidget()
    sys.exit(app.exec_())

13. wxPython(类似于QT)和OpenGL的一次示例 

# -*- coding: utf-8 -*-

import wx
from triangle import *

APP_TITLE = u'架起沟通 wxPython 和 pyOpenGL 的桥梁'


class mainFrame(wx.Frame):
    """程序主窗口类,继承自wx.Frame"""

    def __init__(self):
        """构造函数"""

        wx.Frame.__init__(self, None, -1, APP_TITLE, style=wx.DEFAULT_FRAME_STYLE)

        self.SetBackgroundColour(wx.Colour(224, 224, 224))
        self.SetSize((800, 600))
        self.Center()

        self.scene = WxGLScene(self)


class mainApp(wx.App):
    def OnInit(self):
        self.SetAppName(APP_TITLE)
        self.Frame = mainFrame()
        self.Frame.Show()
        return True


if __name__ == "__main__":
    app = mainApp()
    app.MainLoop()
# -*- coding: utf-8 -*-

import wx
from wx import glcanvas
from OpenGL.GL import *
from OpenGL.GLU import *


class WxGLScene(glcanvas.GLCanvas):
    """GL场景类"""

    def __init__(self, parent, eye=[0, 0, 5], aim=[0, 0, 0], up=[0, 1, 0], view=[-1, 1, -1, 1, 3.5, 10]):
        """构造函数

        parent      - 父级窗口对象
        eye         - 观察者的位置(默认z轴的正方向)
        up          - 对观察者而言的上方(默认y轴的正方向)
        view        - 视景体
        """

        glcanvas.GLCanvas.__init__(self, parent, -1,
                                   style=glcanvas.WX_GL_RGBA | glcanvas.WX_GL_DOUBLEBUFFER | glcanvas.WX_GL_DEPTH_SIZE)

        self.parent = parent  # 父级窗口对象
        self.eye = eye  # 观察者的位置
        self.aim = aim  # 观察目标(默认在坐标原点)
        self.up = up  # 对观察者而言的上方
        self.view = view  # 视景体

        self.size = self.GetClientSize()  # OpenGL窗口的大小
        self.context = glcanvas.GLContext(self)  # OpenGL上下文
        self.zoom = 1.0  # 视口缩放因子
        self.mpos = None  # 鼠标位置
        self.initGL()  # 画布初始化

        self.Bind(wx.EVT_SIZE, self.onResize)  # 绑定窗口尺寸改变事件
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.onErase)  # 绑定背景擦除事件
        self.Bind(wx.EVT_PAINT, self.onPaint)  # 绑定重绘事件

        self.Bind(wx.EVT_LEFT_DOWN, self.onLeftDown)  # 绑定鼠标左键按下事件
        self.Bind(wx.EVT_LEFT_UP, self.onLeftUp)  # 绑定鼠标左键弹起事件
        self.Bind(wx.EVT_RIGHT_UP, self.onRightUp)  # 绑定鼠标右键弹起事件
        self.Bind(wx.EVT_MOTION, self.onMouseMotion)  # 绑定鼠标移动事件
        self.Bind(wx.EVT_MOUSEWHEEL, self.onMouseWheel)  # 绑定鼠标滚轮事件

    def onResize(self, evt):
        """响应窗口尺寸改变事件"""

        if self.context:
            self.SetCurrent(self.context)
            self.size = self.GetClientSize()
            self.Refresh(False)

        evt.Skip()

    def onErase(self, evt):
        """响应背景擦除事件"""

        pass

    def onPaint(self, evt):
        """响应重绘事件"""

        self.SetCurrent(self.context)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)  # 清除屏幕及深度缓存
        self.drawGL()  # 绘图
        self.SwapBuffers()  # 切换缓冲区,以显示绘制内容
        evt.Skip()

    def onLeftDown(self, evt):
        """响应鼠标左键按下事件"""

        self.CaptureMouse()
        self.mpos = evt.GetPosition()

    def onLeftUp(self, evt):
        """响应鼠标左键弹起事件"""

        try:
            self.ReleaseMouse()
        except:
            pass

    def onRightUp(self, evt):
        """响应鼠标右键弹起事件"""

        pass

    def onMouseMotion(self, evt):
        """响应鼠标移动事件"""

        if evt.Dragging() and evt.LeftIsDown():
            pos = evt.GetPosition()
            try:
                dx, dy = pos - self.mpos
            except:
                return
            self.mpos = pos

            # 限于篇幅省略改变观察者位置的代码

            self.Refresh(False)

    def onMouseWheel(self, evt):
        """响应鼠标滚轮事件"""

        if evt.WheelRotation < 0:
            self.zoom *= 1.1
            if self.zoom > 100:
                self.zoom = 100
        elif evt.WheelRotation > 0:
            self.zoom *= 0.9
            if self.zoom < 0.01:
                self.zoom = 0.01

        self.Refresh(False)

    def initGL(self):
        """初始化GL"""

        self.SetCurrent(self.context)

        glClearColor(0, 0, 0, 0)  # 设置画布背景色
        glEnable(GL_DEPTH_TEST)  # 开启深度测试,实现遮挡关系
        glDepthFunc(GL_LEQUAL)  # 设置深度测试函数
        glShadeModel(GL_SMOOTH)  # GL_SMOOTH(光滑着色)/GL_FLAT(恒定着色)
        glEnable(GL_BLEND)  # 开启混合
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)  # 设置混合函数
        glEnable(GL_ALPHA_TEST)  # 启用Alpha测试
        glAlphaFunc(GL_GREATER, 0.05)  # 设置Alpha测试条件为大于0.05则通过
        glFrontFace(GL_CW)  # 设置逆时针索引为正面(GL_CCW/GL_CW)
        glEnable(GL_LINE_SMOOTH)  # 开启线段反走样
        glHint(GL_LINE_SMOOTH_HINT, GL_NICEST)

    def drawGL(self):
        """绘制"""

        # 清除屏幕及深度缓存
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

        # 设置视口
        glViewport(0, 0, self.size[0], self.size[1])

        # 设置投影(透视投影)
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()

        k = self.size[0] / self.size[1]
        if k > 1:
            glFrustum(
                self.zoom * self.view[0] * k,
                self.zoom * self.view[1] * k,
                self.zoom * self.view[2],
                self.zoom * self.view[3],
                self.view[4], self.view[5]
            )
        else:
            glFrustum(
                self.zoom * self.view[0],
                self.zoom * self.view[1],
                self.zoom * self.view[2] / k,
                self.zoom * self.view[3] / k,
                self.view[4], self.view[5]
            )

        # 设置视点
        gluLookAt(
            self.eye[0], self.eye[1], self.eye[2],
            self.aim[0], self.aim[1], self.aim[2],
            self.up[0], self.up[1], self.up[2]
        )

        # 设置模型视图
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()

        # ---------------------------------------------------------------
        glBegin(GL_LINES)  # 开始绘制线段(世界坐标系)

        # 以红色绘制x轴
        glColor4f(1.0, 0.0, 0.0, 1.0)  # 设置当前颜色为红色不透明
        glVertex3f(-0.8, 0.0, 0.0)  # 设置x轴顶点(x轴负方向)
        glVertex3f(0.8, 0.0, 0.0)  # 设置x轴顶点(x轴正方向)

        # 以绿色绘制y轴
        glColor4f(0.0, 1.0, 0.0, 1.0)  # 设置当前颜色为绿色不透明
        glVertex3f(0.0, -0.8, 0.0)  # 设置y轴顶点(y轴负方向)
        glVertex3f(0.0, 0.8, 0.0)  # 设置y轴顶点(y轴正方向)

        # 以蓝色绘制z轴
        glColor4f(0.0, 0.0, 1.0, 1.0)  # 设置当前颜色为蓝色不透明
        glVertex3f(0.0, 0.0, -0.8)  # 设置z轴顶点(z轴负方向)
        glVertex3f(0.0, 0.0, 0.8)  # 设置z轴顶点(z轴正方向)

        glEnd()  # 结束绘制线段

        # ---------------------------------------------------------------
        glBegin(GL_TRIANGLES)  # 开始绘制三角形(z轴负半区)

        glColor4f(1.0, 0.0, 0.0, 1.0)  # 设置当前颜色为红色不透明
        glVertex3f(-0.5, -0.366, -0.5)  # 设置三角形顶点
        glColor4f(0.0, 1.0, 0.0, 1.0)  # 设置当前颜色为绿色不透明
        glVertex3f(0.5, -0.366, -0.5)  # 设置三角形顶点
        glColor4f(0.0, 0.0, 1.0, 1.0)  # 设置当前颜色为蓝色不透明
        glVertex3f(0.0, 0.5, -0.5)  # 设置三角形顶点

        glEnd()  # 结束绘制三角形

        # ---------------------------------------------------------------
        glBegin(GL_TRIANGLES)  # 开始绘制三角形(z轴正半区)

        glColor4f(1.0, 0.0, 0.0, 1.0)  # 设置当前颜色为红色不透明
        glVertex3f(-0.5, 0.5, 0.5)  # 设置三角形顶点
        glColor4f(0.0, 1.0, 0.0, 1.0)  # 设置当前颜色为绿色不透明
        glVertex3f(0.5, 0.5, 0.5)  # 设置三角形顶点
        glColor4f(0.0, 0.0, 1.0, 1.0)  # 设置当前颜色为蓝色不透明
        glVertex3f(0.0, -0.366, 0.5)  # 设置三角形顶点

        glEnd()  # 结束绘制三角形

 甜甜圈材质光影示例

import wxgl.glplot as glt

glt.title('快速体验:$x^2+y^2+z^2=1$')
glt.uvsphere((0,0,0), 1, color='cyan')
glt.show()



import wxgl
import wxgl.glplot as glt

glt.subplot(121)
glt.title('经纬度网格生成球体')
glt.uvsphere((0,0,0), 1, texture=wxgl.Texture('e0.jpg'))
glt.grid()

glt.subplot(122)
glt.title('正八面体迭代细分生成球体')
glt.isosphere((0,0,0), 1, color=(0,1,1), fill=False, iterations=5)
glt.grid()

glt.show()



import numpy as np
import wxgl.glplot as glt

vs = np.random.random((300, 3))*2-1
color = np.random.random(300)
size = np.linalg.norm(vs, axis=1)
size = 30 * (size - size.min()) / (size.max() - size.min())

glt.title('随机生成的300个点')
glt.point(vs, color, cm='jet', alpha=0.8, size=size)
glt.colorbar('jet', [0, 100], loc='right', subject='高度')
glt.colorbar('Paired', [-50, 50], loc='bottom', subject='温度', margin_left=5)
glt.colorbar('rainbow', [0, 240], loc='bottom', subject='速度', margin_right=5)
glt.show()






import wxgl
import wxgl.glplot as glt

glt.subplot(221)
glt.title('太阳光')
glt.torus((0,0,0), 1, 3, vec=(0,1,1), light=wxgl.SunLight(roughness=0, metalness=0, shininess=0.5))

glt.subplot(222)
glt.title('灯光')
pos = (1, 0.0, 3)
glt.torus((0,0,0), 1, 3, vec=(0,1,1), light=wxgl.LampLight(position=pos))
glt.point((pos,), color='red', size=30)

glt.subplot(223)
glt.title('户外光')
glt.torus((0,0,0), 1, 3, vec=(0,1,1), light=wxgl.SkyLight(sky=(1.0,1.0,1.0)))

glt.subplot(224)
glt.title('球谐光')
glt.torus((0,0,0), 1, 3, vec=(0,1,1), light=wxgl.SphereLight(5, factor=0.8))

glt.show()





import wxgl
import wxgl.glplot as glt

tf = lambda duration : ((0, 1, 0, (0.02*duration)%360),)
cf = lambda duration : {'azim':(-0.02*duration)%360}

tx = wxgl.Texture('e0.jpg')
light = wxgl.SunLight(direction=(1,0,-1))

glt.subplot(121)
glt.title('模型旋转')
glt.cylinder((0,1,0), (0,-1,0), 1, texture=tx, transform=tf, light=light)

glt.subplot(122)
glt.cruise(cf)
glt.title('相机旋转')
glt.cylinder((0,1,0), (0,-1,0), 1, texture=tx, light=light)

glt.show()






import wxgl
import wxgl.glplot as glt

vshader = """
	#version 330 core
	in vec4 a_Position;
    in vec4 a_Color;
	uniform mat4 u_ProjMatrix;
    uniform mat4 u_ViewMatrix;
    uniform mat4 u_ModelMatrix;
    out vec4 v_Color;
	void main() {
		gl_Position = u_ProjMatrix * u_ViewMatrix * u_ModelMatrix * a_Position;
		v_Color = a_Color;
	}
"""

fshader = """
	#version 330 core
	in vec4 v_Color;
	void main() {
		gl_FragColor = v_Color;
	}
"""

m = wxgl.Model(wxgl.TRIANGLE_STRIP, vshader, fshader) # 实例化模型,设置绘图方法和着色器源码
m.set_vertex('a_Position', [[-1,1,0],[-1,-1,0],[1,1,0],[1,-1,0]]) # 4个顶点坐标
m.set_color('a_Color', [[1,0,0],[0,1,0],[0,0,1],[0,1,1]]) # 4个顶点的颜色
m.set_proj_matrix('u_ProjMatrix') # 设置投影矩阵
m.set_view_matrix('u_ViewMatrix') # 设置视点矩阵
m.set_model_matrix('u_ModelMatrix') # 设置模型矩阵
a = wxgl.glplot.text3d((1,1,1))

glt.model(a)
glt.model(m) # 添加模型到画布
glt.show() # 显示画布

14. VBO详解 

这段代码不能运行

# 六面体数据
# ------------------------------------------------------
#    v4----- v5
#   /|      /|
#  v0------v1|
#  | |     | |
#  | v7----|-v6
#  |/      |/
#  v3------v2

# 顶点集
vertices = np.array([
    -0.5, 0.5, 0.5,   0.5, 0.5, 0.5,   0.5, -0.5, 0.5,   -0.5, -0.5, 0.5, # v0-v1-v2-v3
    -0.5, 0.5, -0.5,  0.5, 0.5, -0.5,  0.5, -0.5, -0.5,  -0.5, -0.5, -0.5 # v4-v5-v6-v7
], dtype=np.float32)

# 索引集
indices = np.array([
    0, 1, 2, 3, # v0-v1-v2-v3 (front)
    4, 5, 1, 0, # v4-v5-v1-v0 (top)
    3, 2, 6, 7, # v3-v2-v6-v7 (bottom)
    5, 4, 7, 6, # v5-v4-v7-v6 (back)
    1, 5, 6, 2, # v1-v5-v6-v2 (right)
    4, 0, 3, 7  # v4-v0-v3-v7 (left)
], dtype=np.int)




from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

# 在CPU上创建VBO
from OpenGL.arrays import vbo
vbo_vertices = vbo.VBO(vertices)
vbo_indices = vbo.VBO(indices, target=GL_ELEMENT_ARRAY_BUFFER)


# 创建 顶点 VBO 时,默认 target=GL_ARRAY_BUFFER, 而创建索引 VBO 时,target=GL_ELEMENT_ARRAY_BUFFER,
# 因为顶点的数据类型是 np.float32,索引的数据类型是np.int。
# 在VBO保存的顶点数据集,除了顶点信息外,还可以包含颜色、法线、纹理等数据,这就是顶点混合数组的概念。
# 假定我们在上面的顶点集中增加每个顶点的颜色,则可以写成这样:
vertices = np.array([
    0.3, 0.6, 0.9, -0.35, 0.35, 0.35,   # c0-v0
    0.6, 0.9, 0.3, 0.35, 0.35, 0.35,    # c1-v1
    0.9, 0.3, 0.6, 0.35, -0.35, 0.35,   # c2-v2
    0.3, 0.9, 0.6, -0.35, -0.35, 0.35,  # c3-v3
    0.6, 0.3, 0.9, -0.35, 0.35, -0.35,  # c4-v4
    0.9, 0.6, 0.3, 0.35, 0.35, -0.35,   # c5-v5
	0.3, 0.9, 0.9, 0.35, -0.35, -0.35,  # c6-v6
	0.9, 0.9, 0.3, -0.35, -0.35, -0.35  # c7-v7
], dtype=np.float32)



# 分离顶点混合数组
# 使用 glInterleavedArrays() 函数可以从顶点混合数组中分离顶点、颜色、法线和纹理,它一共有11个参数
# 比如,对只包含顶点信息的顶点混合数组:
vbo_indices.bind()
glInterleavedArrays(GL_V3F, 0, None)
        # glInterleavedArrays(),它里面的参数如下所示,很好记,例如GL_C3F_V3F,就是3个浮点数作为颜色(C),3个浮点数作为顶点(T)
        #                       其中T表示纹理坐标,C表示颜色,N表示法线向量,V表示顶点坐标
        #       GL_V2F
        #       GL_V3F
        #       GL_C4UB_V2F
        #       GL_C4UB_V3F
        #       GL_C3F_V3F
        #       GL_N3F_V3F
        #       GL_C4F_N3F_V3F
        #       GL_T2F_V3F
        #       GL_T4F_V4F
        #       GL_T2F_C4UB_V3F
        #       GL_T2F_C3F_V3F
        #       GL_T2F_N3F_V3F
        #       GL_T2F_C4F_N3F_V3F
        #       GL_T4F_C4F_N3F_V4F

# 如果顶点混合数组包含了颜色和顶点信息:
vbo_indices.bind()
glInterleavedArrays(GL_C3F_V3F, 0, None)


# 这是一个完整的过程,but运行不了,shit
vbo_indices.bind()
glInterleavedArrays(GL_V3F, 0, None)
vbo_indices.bind()
glDrawElements(GL_QUADS, int(vbo_indices .size/4), GL_UNSIGNED_INT, None)
vbo_indices.unbind()
vbo_indices.unbind()

 一次真正的操作(不能运行)

# 第一步,导入库
from OpenGL.GL import *
from OpenGL.arrays import vbo
from OpenGL.GLU import *
from OpenGL.GLUT import *
import numpy as np

# 第二步,创建顶点缓冲区对象
# 顶点集
vertices = np.array([
    -0.5, 0.5, 0.5,
    0.5, 0.5, 0.5,
    0.5, -0.5, 0.5,
    -0.5, -0.5, 0.5,
    -0.5, 0.5, -0.5,
    0.5, 0.5, -0.5,
    0.5, -0.5, -0.5,
    -0.5, -0.5, -0.5
], 'f')

# 索引集
indices = np.array([
    0, 1, 2, 3,
    4, 5, 1, 0,
    3, 2, 6, 7,
    5, 4, 7, 6,
    1, 5, 6, 2,
    4, 0, 3, 7
], 'H')
# numpy数组类型很重要,否则你用不正确的数组绘图就会一片空白(本人真实经历)
# 在这里我解释一下索引集的作用:正方体有6个面,4个点构成一个面,则有24个点,但有16个点是重复的,索引值的作用类似于字串符的索引,
# 你可以把顶点集的一组数据看做一个字符,索引值的每一个值为索引(我讲的有点乱,但知道字串符索引应该理解),把那些数据按索引传到GPU,减小了顶点集占的空间。
# 接下来我们要把这些数据传到GPU上,方法如下:
# 原文链接:https://blog.csdn.net/Css2432lxp/article/details/125749334


# 第三步
def cube():
    ...
    vbo1.bind()
    glInterleavedArrays(GL_V3F, 0, None)
    vbo2.bind()
    glDrawElements(GL_QUADS, 24, GL_UNSIGNED_SHORT, None)


# 第四步,绘图
def draw():
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    cube()
    glutSwapBuffers()


...

glutDisplayFunc(draw)

 能运行的代码来了

from OpenGL.GL import *
from OpenGL.arrays import vbo
from OpenGL.GLU import *
from OpenGL.GLUT import *
import numpy as np


def cube():
    vertices = np.array([
        -0.5, 0.5, 0.5,
        0.5, 0.5, 0.5,
        0.5, -0.5, 0.5,
        -0.5, -0.5, 0.5,
        -0.5, 0.5, -0.5,
        0.5, 0.5, -0.5,
        0.5, -0.5, -0.5,
        -0.5, -0.5, -0.5
    ], 'f')
    indices = np.array([
        0, 1, 2, 3,
        4, 5, 1, 0,
        3, 2, 6, 7,
        5, 4, 7, 6,
        1, 5, 6, 2,
        4, 0, 3, 7
    ], 'H')

    vbo1 = vbo.VBO(vertices)
    vbo2 = vbo.VBO(indices, target=GL_ELEMENT_ARRAY_BUFFER)
    eboLength = len(indices)

    vbo1.bind()
    glInterleavedArrays(GL_V3F, 0, None)
    vbo2.bind()
    glDrawElements(GL_QUADS, eboLength, GL_UNSIGNED_SHORT, None)
    vbo2.unbind()
    vbo1.unbind()


def Draw():

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    cube()
    glutSwapBuffers()


glutInit()
glutInitDisplayMode(GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)

glutInitWindowSize(600, 600)
glutInitWindowPosition(300, 200)
glutCreateWindow('cube')

glClearColor(0.0, 0.0, 0.0, 1.0)
glEnable(GL_DEPTH_TEST)
glDepthFunc(GL_LEQUAL)

glutDisplayFunc(Draw)

glutMainLoop()

如果要让顶点有颜色,得这么写
vertices = np.array([Red,Green,Blue,X,Z,Y])#前三个是颜色,后三个是顶点坐标,栗子如下

def cube():       
    vertices = np.array([
        0.3, 0.6, 0.9, -0.5, 0.5, 0.5,   
        0.6, 0.9, 0.3, 0.5, 0.5, 0.5,   
        0.9, 0.3, 0.6, 0.5, -0.5, 0.5,   
        0.3, 0.9, 0.6, -0.5, -0.5, 0.5,  
        0.6, 0.3, 0.9, -0.5, 0.5, -0.5,  
        0.9, 0.6, 0.3, 0.5, 0.5, -0.5,   
        0.3, 0.9, 0.9, 0.5, -0.5, -0.5,  
        0.9, 0.9, 0.3, -0.5, -0.5, -0.5  
        ],'f')
    indices = np.array([
        0, 1, 2, 3, 
        4, 5, 1, 0,
        3, 2, 6, 7,
        5, 4, 7, 6,
        1, 5, 6, 2, 
        4, 0, 3, 7  
        ],'H')

# 写完顶点,还要分离数组,so,如下
def cube_split():
    #传入顶点数据还是一样
    vbo1.bind()
    glInterleavedArrays(GL_C3F_V3F,0,None)
    vbo2.bind()
    glDrawElements(GL_QUADS,eboLength,GL_UNSIGNED_SHORT,None)

 下面这个栗子不能实现转动效果

from OpenGL.GL import *
from OpenGL.arrays import vbo
from OpenGL.GLU import *
from OpenGL.GLUT import *
import numpy as np


def cube():
    vertices = np.array([
        0.3, 0.6, 0.9, -0.5, 0.5, 0.5,
        0.6, 0.9, 0.3, 0.5, 0.5, 0.5,
        0.9, 0.3, 0.6, 0.5, -0.5, 0.5,
        0.3, 0.9, 0.6, -0.5, -0.5, 0.5,
        0.6, 0.3, 0.9, -0.5, 0.5, -0.5,
        0.9, 0.6, 0.3, 0.5, 0.5, -0.5,
        0.3, 0.9, 0.9, 0.5, -0.5, -0.5,
        0.9, 0.9, 0.3, -0.5, -0.5, -0.5
    ], 'f')
    indices = np.array([
        0, 1, 2, 3,
        4, 5, 1, 0,
        3, 2, 6, 7,
        5, 4, 7, 6,
        1, 5, 6, 2,
        4, 0, 3, 7
    ], 'H')

    vbo1 = vbo.VBO(vertices)
    vbo2 = vbo.VBO(indices, target=GL_ELEMENT_ARRAY_BUFFER)
    eboLength = len(indices)

    vbo1.bind()
    glInterleavedArrays(GL_C3F_V3F, 0, None)
    vbo2.bind()
    glDrawElements(GL_QUADS, eboLength, GL_UNSIGNED_SHORT, None)
    vbo2.unbind()
    vbo1.unbind()


def Draw():
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    cube()
    glutSwapBuffers()


glutInit()
glutInitDisplayMode(GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)

glutInitWindowSize(600, 600)
glutInitWindowPosition(300, 200)
glutCreateWindow('cube')

glClearColor(0.0, 0.0, 0.0, 1.0)
glEnable(GL_DEPTH_TEST)
glDepthFunc(GL_LEQUAL)

glutDisplayFunc(Draw)

glutMainLoop()

这个例子可以实现转动效果

# -*- coding: utf-8 -*-

# -------------------------------------------
# quidam_02.py 旋转、缩放、改变视点和参考点
# -------------------------------------------

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
import numpy as np
from OpenGL.arrays import vbo

IS_PERSPECTIVE = True  # 透视投影
VIEW = np.array([-0.8, 0.8, -0.8, 0.8, 1.0, 200.0])  # 视景体的left/right/bottom/top/near/far六个面
SCALE_K = np.array([1.0, 1.0, 1.0])  # 模型缩放比例
EYE = np.array([0.0, 2.0, 0.0])  # 眼睛的位置(默认z轴的正方向)
LOOK_AT = np.array([0.0, 0.0, 0.0])  # 瞄准方向的参考点(默认在坐标原点)
EYE_UP = np.array([0.0, 1.0, 0.0])  # 定义对观察者而言的上方(默认y轴的正方向)
WIN_W, WIN_H = 640, 480  # 保存窗口宽度和高度的变量
LEFT_IS_DOWNED = False  # 鼠标左键被按下
MOUSE_X, MOUSE_Y = 0, 0  # 考察鼠标位移量时保存的起始位置

# 计算窗体的中绘图的位置
def getposture():
    global EYE, LOOK_AT

    dist = np.sqrt(np.power((EYE - LOOK_AT), 2).sum())  # 计算平方,然后再开平方根
    print(dist)
    if dist > 0:
        phi = np.arcsin((EYE[1] - LOOK_AT[1]) / dist)   # 用dist的反正弦值除以它
        theta = np.arcsin((EYE[0] - LOOK_AT[0]) / (dist * np.cos(phi)))
    else:
        phi = 0.0
        theta = 0.0

    return dist, phi, theta

DIST, PHI, THETA = getposture()  # 眼睛与观察目标之间的距离、仰角、方位角

# 初始化函数
def init():
    glClearColor(0.0, 0.0, 0.0, 1.0)  # 设置画布背景色,RGBA,最大值为1
    glEnable(GL_DEPTH_TEST)  # 开启深度测试,实现遮挡关系
    glDepthFunc(GL_LEQUAL)  # 设置深度测试函数(GL_LEQUAL只是选项之一)


# 先设置cube函数,在设置绘图draw()函数之前
def cube():
    vertices = np.array([
        0.3, 0.6, 0.9, -0.5, 0.5, 0.5,  # c0-v0
        0.6, 0.9, 0.3, 0.5, 0.5, 0.5,  # c1-v1
        0.9, 0.3, 0.6, 0.5, -0.5, 0.5,  # c2-v2
        0.3, 0.9, 0.6, -0.5, -0.5, 0.5,  # c3-v3
        0.6, 0.3, 0.9, -0.5, 0.5, -0.5,  # c4-v4
        0.9, 0.6, 0.3, 0.5, 0.5, -0.5,  # c5-v5
        0.3, 0.9, 0.9, 0.5, -0.5, -0.5,  # c6-v6
        0.9, 0.9, 0.3, -0.5, -0.5, -0.5  # c7-v7
    ], 'f')
    indices = np.array([
        0, 1, 2, 3,  # v0-v1-v2-v3 (front)
        4, 5, 1, 0,  # v4-v5-v1-v0 (top)
        3, 2, 6, 7,  # v3-v2-v6-v7 (bottom)
        5, 4, 7, 6,  # v5-v4-v7-v6 (back)
        1, 5, 6, 2,  # v1-v5-v6-v2 (right)
        4, 0, 3, 7  # v4-v0-v3-v7 (left)
    ], 'H')

    vbo_vertices = vbo.VBO(vertices)
    vbo_indices = vbo.VBO(indices, target=GL_ELEMENT_ARRAY_BUFFER)
    eboLength = len(indices)

    vbo_vertices.bind()
    glInterleavedArrays(GL_C3F_V3F, 0, None)
    vbo_indices.bind()
    glDrawElements(GL_QUADS, eboLength, GL_UNSIGNED_SHORT, None)
    vbo_indices.unbind()
    vbo_vertices.unbind()

# 绘图函数
def Draw():
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()

    if WIN_W > WIN_H:
        if IS_PERSPECTIVE:
            glFrustum(VIEW[0] * WIN_W / WIN_H, VIEW[1] * WIN_W / WIN_H, VIEW[2], VIEW[3], VIEW[4], VIEW[5])
        else:
            glOrtho(VIEW[0] * WIN_W / WIN_H, VIEW[1] * WIN_W / WIN_H, VIEW[2], VIEW[3], VIEW[4], VIEW[5])
    else:
        if IS_PERSPECTIVE:
            glFrustum(VIEW[0], VIEW[1], VIEW[2] * WIN_H / WIN_W, VIEW[3] * WIN_H / WIN_W, VIEW[4], VIEW[5])
        else:
            glOrtho(VIEW[0], VIEW[1], VIEW[2] * WIN_H / WIN_W, VIEW[3] * WIN_H / WIN_W, VIEW[4], VIEW[5])

    # 设置模型视图
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

    # 几何变换
    glScale(SCALE_K[0], SCALE_K[1], SCALE_K[2])

    # 设置视点
    gluLookAt(
        EYE[0], EYE[1], EYE[2],
        LOOK_AT[0], LOOK_AT[1], LOOK_AT[2],
        EYE_UP[0], EYE_UP[1], EYE_UP[2]
    )

    # 设置视口
    glViewport(0, 0, WIN_W, WIN_H)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    cube()
    glutSwapBuffers()

def reshape(width, height):
    global WIN_W, WIN_H

    WIN_W, WIN_H = width, height
    glutPostRedisplay()     # 回调函数,当当前窗体没有变动时,释放CPU资源,有变动就让CPU去再渲染一边

def get_data():
    THz_data = np.load('青源峰达已降噪.npy')   # 获取青源峰达数据
    x,y,z = THz_data.shape  # 获取三个维度的值 73,62,9000

    xss = []    # 创建空列表,用来在循环中获取每一列的数据,也就是x=0时,所有的y
    xy_list = []    # 用来把每一列数据进行储存,方便绘图
    for i in range(x):      # 通过计算矩阵大小,来计算出每个点对应的矩阵位置
        for j in range(y):
            a = (i, j, 0)
            xss.append(a)
        xy_list.append(xss)
        xss = []

    # print(len(xy_list))
    # print(xy_list[1])
    return xy_list



# 检测鼠标左键状态和滚轮状态
def mouseclick(button, state, x, y):
    global SCALE_K  # 声明改变压缩比例
    global LEFT_IS_DOWNED       # 声明要改变鼠标状态
    global MOUSE_X, MOUSE_Y     # 声明改变鼠标位置

    MOUSE_X, MOUSE_Y = x, y
    if button == GLUT_LEFT_BUTTON:  # 如果鼠标左键被按下
        LEFT_IS_DOWNED = state == GLUT_DOWN     # 改变鼠标左键状态
    elif button == 3:       # 滚轮往上滑的时候
        SCALE_K *= 1.05
        glutPostRedisplay()     # 通知OpenGL开工渲染一次
    elif button == 4:       # 滚轮往下滑的时候,这个地方有点问题
        SCALE_K *= 0.95
        glutPostRedisplay()

# 如果鼠标在界面上移动了,
def mousemotion(x, y):
    global LEFT_IS_DOWNED
    global EYE, EYE_UP
    global MOUSE_X, MOUSE_Y
    global DIST, PHI, THETA     # 眼睛与目标之间的距离,仰角,方位角
    global WIN_W, WIN_H

    if LEFT_IS_DOWNED:      # 如果鼠标左键被按下为真,也就是说,按下了这段代码才会被执行
        dx = MOUSE_X - x    # MOUSE_X是鼠标此时在屏幕上的位置,x是鼠标按下之后在屏幕上的位置
        dy = y - MOUSE_Y
        MOUSE_X, MOUSE_Y = x, y     # 重新给x,y赋值,赋值为当前鼠标在界面上的悬浮位置

        # 通过计算来确定在界面中y轴的方向
        PHI += 2 * np.pi * dy / WIN_H
        PHI %= 2 * np.pi
        THETA += 2 * np.pi * dx / WIN_W
        THETA %= 2 * np.pi
        r = DIST * np.cos(PHI)

        EYE[1] = DIST * np.sin(PHI)
        EYE[0] = r * np.sin(THETA)
        EYE[2] = r * np.cos(THETA)

        if 0.5 * np.pi < PHI < 1.5 * np.pi:
            EYE_UP[1] = -1.0
        else:
            EYE_UP[1] = 1.0

        glutPostRedisplay()     # 通知刷新

# 用来检测键盘的动态
def keydown(key, x, y):
    global DIST, PHI, THETA
    global EYE, LOOK_AT, EYE_UP
    global IS_PERSPECTIVE, VIEW

    if key in [b'x', b'X', b'y', b'Y', b'z', b'Z']:
        if key == b'x':  # 瞄准参考点 x 减小
            LOOK_AT[0] -= 0.01
        elif key == b'X':  # 瞄准参考 x 增大
            LOOK_AT[0] += 0.01
        elif key == b'y':  # 瞄准参考点 y 减小
            LOOK_AT[1] -= 0.01
        elif key == b'Y':  # 瞄准参考点 y 增大
            LOOK_AT[1] += 0.01
        elif key == b'z':  # 瞄准参考点 z 减小
            LOOK_AT[2] -= 0.01
        elif key == b'Z':  # 瞄准参考点 z 增大
            LOOK_AT[2] += 0.01

        DIST, PHI, THETA = getposture()
        glutPostRedisplay()
    elif key == b'\r':  # 回车键,视点前进
        EYE = LOOK_AT + (EYE - LOOK_AT) * 0.9
        DIST, PHI, THETA = getposture()
        glutPostRedisplay()
    elif key == b'\x08':  # 退格键,视点后退
        EYE = LOOK_AT + (EYE - LOOK_AT) * 1.1
        DIST, PHI, THETA = getposture()
        glutPostRedisplay()
    elif key == b' ':  # 空格键,切换投影模式
        IS_PERSPECTIVE = not IS_PERSPECTIVE
        glutPostRedisplay()


if __name__ == "__main__":
    glutInit()
    displayMode = GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH
    glutInitDisplayMode(displayMode)

    glutInitWindowSize(WIN_W, WIN_H)
    glutInitWindowPosition(300, 200)
    glutCreateWindow('Quidam Of OpenGL')

    init()  # 初始化画布
    glutDisplayFunc(Draw)  # 注册回调函数draw()
    glutReshapeFunc(reshape)  # 注册响应窗口改变的函数reshape(),监听窗口动态
    glutMouseFunc(mouseclick)  # 注册响应鼠标点击的函数mouseclick()
    glutMotionFunc(mousemotion)  # 注册响应鼠标拖拽的函数mousemotion()
    glutKeyboardFunc(keydown)  # 注册键盘输入的函数keydown()

    glutMainLoop()  # 进入glut主循环

 

三、一些具体实操

1. 基本的语法

from OpenGL.GL import *
from OpenGL.GLUT import *
def draw():
    # ---------------------------------------------------------------
    glBegin(GL_LINES)                    # 开始绘制线段(世界坐标系)
    # 以红色绘制x轴
    glColor4f(1.0, 0.0, 0.0, 1.0)        # 设置当前颜色为红色不透明
    glVertex3f(-0.8, 0.0, 0.0)           # 设置x轴顶点(x轴负方向)
    glVertex3f(0.8, 0.0, 0.0)            # 设置x轴顶点(x轴正方向)

    # 以绿色绘制y轴
    glColor4f(0.0, 1.0, 0.0, 1.0)        # 设置当前颜色为绿色不透明
    glVertex3f(0.0, -0.8, 0.0)           # 设置y轴顶点(y轴负方向)
    glVertex3f(0.0, 0.8, 0.0)            # 设置y轴顶点(y轴正方向)

    # 以蓝色绘制z轴
    glColor4f(0.0, 0.0, 1.0, 1.0)        # 设置当前颜色为蓝色不透明
    glVertex3f(0.0, 0.0, -0.8)           # 设置z轴顶点(z轴负方向)
    glVertex3f(0.0, 0.0, 0.8)            # 设置z轴顶点(z轴正方向)
    glEnd()                              # 结束绘制线段

    # ---------------------------------------------------------------
    glBegin(GL_TRIANGLES)                # 开始绘制三角形(z轴负半区)
    glColor4f(1.0, 0.0, 0.0, 1.0)        # 设置当前颜色为红色不透明
    glVertex3f(-0.5, -0.366, -0.5)       # 设置三角形顶点
    glColor4f(0.0, 1.0, 0.0, 1.0)        # 设置当前颜色为绿色不透明
    glVertex3f(0.5, -0.366, -0.5)        # 设置三角形顶点
    glColor4f(0.0, 0.0, 1.0, 1.0)        # 设置当前颜色为蓝色不透明
    glVertex3f(0.0, 0.5, -0.5)           # 设置三角形顶点
    glEnd()
                         # 结束绘制三角形
    # ---------------------------------------------------------------
    glFlush()                            # 清空缓冲区,将指令送往硬件立即执行
if __name__ == "__main__":
    glutInit()                           # 1. 初始化glut库
    glutCreateWindow('Quidam Of OpenGL') # 2. 创建glut窗口
    glutDisplayFunc(draw)                # 3. 注册回调函数draw()
    glutMainLoop()                       # 4. 进入glut主循环

2. 旋转的线条小茶壶示例,也是官方示例:

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *


def drawFunc():
    # 清楚之前画面
    glClear(GL_COLOR_BUFFER_BIT)
    # 让模型转起来
    glRotatef(0.05, 20, 5, 0)  # 旋转速度,剩下三个是它的x,y,z的倾斜角度
    # 模型的大小
    glutWireTeapot(0.5)
    # 刷新显示
    glFlush()


# 使用glut初始化OpenGL
glutInit()
# 显示模式:GLUT_SINGLE无缓冲直接显示|GLUT_RGBA采用RGB(A非alpha)
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA)
# 窗口位置及大小-生成
# 窗口的弹出位置
glutInitWindowPosition(0, 0)
# 窗口的尺寸
glutInitWindowSize(400, 400)
# 窗口的标题名称
glutCreateWindow(b"test")
# 调用函数绘制图像

glutDisplayFunc(drawFunc)
glutIdleFunc(drawFunc)
# 主循环
glutMainLoop()

3. 小狗绘制

 图片在此

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from PIL import Image
import math
theta,phi = 0,0
pos = (0,0,0)
def target():
    global theta,phi
    x = math.sin(theta) * math.cos(phi)
    y = math.sin(theta) * math.sin(phi)
    z = math.cos(theta)
    return x,y,z
def setLookAt():
    global pos
    tar = target()
    gluLookAt(*pos,*tar,0,1,0)
    glLoadIdentity()
def drawFunc():
    setLookAt()
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
    vertex = [[[0,0,1],[1,0,1],[1,1,1],[0,1,1]],[[0,0,0],[0,1,0],[1,1,0],[1,0,0]],[[0,1,0],[0,1,1],[1,1,1],[1,1,0]],[[0,0,0],[1,0,0],[1,0,1],[0,0,1]],[[1,0,0],[1,1,0],[1,1,1],[1,0,1]],[[0,0,0],[0,0,1],[0,1,1],[0,1,0]]]
    for i in range(len(vertex)):
        glBindTexture(GL_TEXTURE_2D,1)
        glBegin(GL_QUADS)
        glTexCoord2f(0.0, 0.0)
        glVertex3f(*vertex[i][0])
        glTexCoord2f(1.0, 0.0)
        glVertex3f(*vertex[i][1])
        glTexCoord2f(1.0, 1.0)
        glVertex3f(*vertex[i][2])
        glTexCoord2f(0.0, 1.0)
        glVertex3f(*vertex[i][3])
        glEnd()
    glFlush()
glutInit()
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA)
glutInitWindowSize(400, 400)
glutCreateWindow(b"OpenGL")
glClearColor(0,0,0,0)
glEnable(GL_DEPTH_TEST)
glClearDepth(1.0)
glDepthFunc(GL_LESS)
glShadeModel(GL_SMOOTH)
# glEnable(GL_CULL_FACE)
# glCullFace(GL_BACK)
glEnable(GL_POINT_SMOOTH)
glEnable(GL_LINE_SMOOTH)
glEnable(GL_POLYGON_SMOOTH)
glMatrixMode(GL_PROJECTION)
glHint(GL_POINT_SMOOTH_HINT,GL_NICEST)
glHint(GL_LINE_SMOOTH_HINT,GL_NICEST)
glHint(GL_POLYGON_SMOOTH_HINT,GL_FASTEST)
glMatrixMode(GL_MODELVIEW)
glEnable(GL_TEXTURE_2D)
img = Image.open('a.jpg')
width, height = img.size
img = img.tobytes('raw','RGB',0,-1)
glGenTextures(2)
glBindTexture(GL_TEXTURE_2D, 1)
glTexImage2D(GL_TEXTURE_2D, 0, 4,width,height, 0, GL_RGB,GL_UNSIGNED_BYTE,img)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_REPEAT)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE, GL_DECAL)
glutDisplayFunc(drawFunc)
glutIdleFunc(drawFunc)
glutMainLoop()

4. 一个三维视景体

# -*- coding: utf-8 -*-

# -------------------------------------------
# quidam_02.py 旋转、缩放、改变视点和参考点
# -------------------------------------------

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
import numpy as np

IS_PERSPECTIVE = True  # 透视投影
VIEW = np.array([-0.8, 0.8, -0.8, 0.8, 1.0, 200.0])  # 视景体的left/right/bottom/top/near/far六个面
SCALE_K = np.array([1.0, 1.0, 1.0])  # 模型缩放比例
EYE = np.array([0.0, 0.0, 2.0])  # 眼睛的位置(默认z轴的正方向)
LOOK_AT = np.array([0.0, 0.0, 0.0])  # 瞄准方向的参考点(默认在坐标原点)
EYE_UP = np.array([0.0, 1.0, 0.0])  # 定义对观察者而言的上方(默认y轴的正方向)
WIN_W, WIN_H = 640, 480  # 保存窗口宽度和高度的变量
LEFT_IS_DOWNED = False  # 鼠标左键被按下
MOUSE_X, MOUSE_Y = 0, 0  # 考察鼠标位移量时保存的起始位置

# 计算窗体的中绘图的位置
def getposture():
    global EYE, LOOK_AT

    dist = np.sqrt(np.power((EYE - LOOK_AT), 2).sum())  # 计算平方,然后再开平方根
    print(dist)
    if dist > 0:
        phi = np.arcsin((EYE[1] - LOOK_AT[1]) / dist)   # 用dist的反正弦值除以它
        theta = np.arcsin((EYE[0] - LOOK_AT[0]) / (dist * np.cos(phi)))
    else:
        phi = 0.0
        theta = 0.0

    return dist, phi, theta

DIST, PHI, THETA = getposture()  # 眼睛与观察目标之间的距离、仰角、方位角

# 初始化函数
def init():
    glClearColor(0.0, 0.0, 0.0, 1.0)  # 设置画布背景色,RGBA,最大值为1
    glEnable(GL_DEPTH_TEST)  # 开启深度测试,实现遮挡关系
    glDepthFunc(GL_LEQUAL)  # 设置深度测试函数(GL_LEQUAL只是选项之一)

# 绘图函数
def draw():
    global IS_PERSPECTIVE, VIEW  # 声明变量,要在局部中改变全局变量的值(此处是赋值)
    global EYE, LOOK_AT, EYE_UP
    global SCALE_K
    global WIN_W, WIN_H


    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)      # 清除屏幕及深度缓存


    glMatrixMode(GL_PROJECTION)     # 设置投影(透视投影)
    glLoadIdentity()    # 重置当前矩阵为单位矩阵

    if WIN_W > WIN_H:   # 比较窗口的长宽大小
        if IS_PERSPECTIVE:      # 判断如果投射投影为真
            # 设计视景体的左、右、下、上、近、远的点,一共三个坐标(左下、右上、近远,一共三个坐标点)
            glFrustum(VIEW[0] * WIN_W / WIN_H, VIEW[1] * WIN_W / WIN_H, VIEW[2], VIEW[3], VIEW[4], VIEW[5])
        else:
            # 同上
            glOrtho(VIEW[0] * WIN_W / WIN_H, VIEW[1] * WIN_W / WIN_H, VIEW[2], VIEW[3], VIEW[4], VIEW[5])
    else:
        if IS_PERSPECTIVE:
            glFrustum(VIEW[0], VIEW[1], VIEW[2] * WIN_H / WIN_W, VIEW[3] * WIN_H / WIN_W, VIEW[4], VIEW[5])
        else:
            glOrtho(VIEW[0], VIEW[1], VIEW[2] * WIN_H / WIN_W, VIEW[3] * WIN_H / WIN_W, VIEW[4], VIEW[5])

    # 这两玩意好像有没有都没啥区别,先dont care
    glMatrixMode(GL_MODELVIEW)  # 设置模型视图
    glLoadIdentity()    # 重置当前矩阵为单位矩阵


    glScale(SCALE_K[0], SCALE_K[1], SCALE_K[2])     # 几何变换,设置压缩比例!!

    # 设置视点,定义Z的方向和Y的方向
    gluLookAt(
        EYE[0], EYE[1], EYE[2],
        LOOK_AT[0], LOOK_AT[1], LOOK_AT[2],
        EYE_UP[0], EYE_UP[1], EYE_UP[2]
    )

    # 设置视口,告诉OpenGL把图绘制在哪里
    glViewport(0, 0, WIN_W, WIN_H)

    # ---------------------------------------------------------------
    glBegin(GL_LINES)  # 开始绘制线段(世界坐标系)

    # 以红色绘制x轴
    glColor4f(1.0, 0.0, 0.0, 1.0)  # 设置当前颜色为红色不透明
    glVertex3f(-0.8, 0.0, 0.0)  # 设置x轴顶点(x轴负方向)
    glVertex3f(0.8, 0.0, 0.0)  # 设置x轴顶点(x轴正方向)

    # 以绿色绘制y轴
    glColor4f(0.0, 1.0, 0.0, 1.0)  # 设置当前颜色为绿色不透明
    glVertex3f(0.0, -0.8, 0.0)  # 设置y轴顶点(y轴负方向)
    glVertex3f(0.0, 0.8, 0.0)  # 设置y轴顶点(y轴正方向)

    # 以蓝色绘制z轴
    glColor4f(0.0, 0.0, 1.0, 1.0)  # 设置当前颜色为蓝色不透明
    glVertex3f(0.0, 0.0, -0.8)  # 设置z轴顶点(z轴负方向)
    glVertex3f(0.0, 0.0, 0.8)  # 设置z轴顶点(z轴正方向)

    glEnd()  # 结束绘制线段

    # ---------------------------------------------------------------
    glBegin(GL_TRIANGLES)  # 开始绘制三角形(z轴负半区)

    glColor4f(1.0, 0.0, 0.0, 1.0)  # 设置当前颜色为红色不透明
    glVertex3f(-0.5, -0.366, -0.5)  # 设置三角形顶点
    glColor4f(0.0, 1.0, 0.0, 1.0)  # 设置当前颜色为绿色不透明
    glVertex3f(0.5, -0.366, -0.5)  # 设置三角形顶点
    glColor4f(0.0, 0.0, 1.0, 1.0)  # 设置当前颜色为蓝色不透明
    glVertex3f(0.0, 0.5, -0.5)  # 设置三角形顶点

    glEnd()  # 结束绘制三角形

    # ---------------------------------------------------------------
    glBegin(GL_TRIANGLES)  # 开始绘制三角形(z轴正半区)

    glColor4f(1.0, 0.0, 0.0, 1.0)  # 设置当前颜色为红色不透明
    glVertex3f(-0.5, 0.5, 0.5)  # 设置三角形顶点
    glColor4f(0.0, 1.0, 0.0, 1.0)  # 设置当前颜色为绿色不透明
    glVertex3f(0.5, 0.5, 0.5)  # 设置三角形顶点
    glColor4f(0.0, 0.0, 1.0, 1.0)  # 设置当前颜色为蓝色不透明
    glVertex3f(0.0, -0.366, 0.5)  # 设置三角形顶点

    glEnd()  # 结束绘制三角形

    # ---------------------------------------------------------------
    glutSwapBuffers()  # 切换缓冲区,以显示绘制内容
    # glFlush()

def reshape(width, height):
    global WIN_W, WIN_H

    WIN_W, WIN_H = width, height
    glutPostRedisplay()     # 回调函数,当当前窗体没有变动时,释放CPU资源,有变动就让CPU去再渲染一边

# 检测鼠标左键状态和滚轮状态
def mouseclick(button, state, x, y):
    global SCALE_K  # 声明改变压缩比例
    global LEFT_IS_DOWNED       # 声明要改变鼠标状态
    global MOUSE_X, MOUSE_Y     # 声明改变鼠标位置

    MOUSE_X, MOUSE_Y = x, y
    if button == GLUT_LEFT_BUTTON:  # 如果鼠标左键被按下
        LEFT_IS_DOWNED = state == GLUT_DOWN     # 改变鼠标左键状态
    elif button == 3:       # 滚轮往上滑的时候
        SCALE_K *= 1.05
        glutPostRedisplay()     # 通知OpenGL开工渲染一次
    elif button == 4:       # 滚轮往下滑的时候,这个地方有点问题
        SCALE_K *= 0.95
        glutPostRedisplay()

# 如果鼠标在界面上移动了,
def mousemotion(x, y):
    global LEFT_IS_DOWNED
    global EYE, EYE_UP
    global MOUSE_X, MOUSE_Y
    global DIST, PHI, THETA     # 眼睛与目标之间的距离,仰角,方位角
    global WIN_W, WIN_H

    if LEFT_IS_DOWNED:      # 如果鼠标左键被按下为真,也就是说,按下了这段代码才会被执行
        dx = MOUSE_X - x    # MOUSE_X是鼠标此时在屏幕上的位置,x是鼠标按下之后在屏幕上的位置
        dy = y - MOUSE_Y
        MOUSE_X, MOUSE_Y = x, y     # 重新给x,y赋值,赋值为当前鼠标在界面上的悬浮位置

        # 通过计算来确定在界面中y轴的方向
        PHI += 2 * np.pi * dy / WIN_H
        PHI %= 2 * np.pi
        THETA += 2 * np.pi * dx / WIN_W
        THETA %= 2 * np.pi
        r = DIST * np.cos(PHI)

        EYE[1] = DIST * np.sin(PHI)
        EYE[0] = r * np.sin(THETA)
        EYE[2] = r * np.cos(THETA)

        if 0.5 * np.pi < PHI < 1.5 * np.pi:
            EYE_UP[1] = -1.0
        else:
            EYE_UP[1] = 1.0

        glutPostRedisplay()     # 通知刷新

# 用来检测键盘的动态
def keydown(key, x, y):
    global DIST, PHI, THETA
    global EYE, LOOK_AT, EYE_UP
    global IS_PERSPECTIVE, VIEW

    if key in [b'x', b'X', b'y', b'Y', b'z', b'Z']:
        if key == b'x':  # 瞄准参考点 x 减小
            LOOK_AT[0] -= 0.01
        elif key == b'X':  # 瞄准参考 x 增大
            LOOK_AT[0] += 0.01
        elif key == b'y':  # 瞄准参考点 y 减小
            LOOK_AT[1] -= 0.01
        elif key == b'Y':  # 瞄准参考点 y 增大
            LOOK_AT[1] += 0.01
        elif key == b'z':  # 瞄准参考点 z 减小
            LOOK_AT[2] -= 0.01
        elif key == b'Z':  # 瞄准参考点 z 增大
            LOOK_AT[2] += 0.01

        DIST, PHI, THETA = getposture()
        glutPostRedisplay()
    elif key == b'\r':  # 回车键,视点前进
        EYE = LOOK_AT + (EYE - LOOK_AT) * 0.9
        DIST, PHI, THETA = getposture()
        glutPostRedisplay()
    elif key == b'\x08':  # 退格键,视点后退
        EYE = LOOK_AT + (EYE - LOOK_AT) * 1.1
        DIST, PHI, THETA = getposture()
        glutPostRedisplay()
    elif key == b' ':  # 空格键,切换投影模式
        IS_PERSPECTIVE = not IS_PERSPECTIVE
        glutPostRedisplay()


if __name__ == "__main__":
    glutInit()
    displayMode = GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH
    glutInitDisplayMode(displayMode)

    glutInitWindowSize(WIN_W, WIN_H)
    glutInitWindowPosition(300, 200)
    glutCreateWindow('Quidam Of OpenGL')

    init()  # 初始化画布
    glutDisplayFunc(draw)  # 注册回调函数draw()
    glutReshapeFunc(reshape)  # 注册响应窗口改变的函数reshape(),监听窗口动态
    glutMouseFunc(mouseclick)  # 注册响应鼠标点击的函数mouseclick()
    glutMotionFunc(mousemotion)  # 注册响应鼠标拖拽的函数mousemotion()
    glutKeyboardFunc(keydown)  # 注册键盘输入的函数keydown()

    glutMainLoop()  # 进入glut主循环

5. 修改后的旋转视景体

from OpenGL.GL import *
from OpenGL.GLUT import *


def draw():
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
    glRotatef(0.01, 0, 1, 0)
    # ---------------------------------------------------------------
    glBegin(GL_LINES)  # 开始绘制线段(世界坐标系)

    # 以红色绘制x轴
    glColor4f(1.0, 0.0, 0.0, 1.0)  # 设置当前颜色为红色不透明
    glVertex3f(-0.8, 0.0, 0.0)  # 设置x轴顶点(x轴负方向)
    glVertex3f(0.8, 0.0, 0.0)  # 设置x轴顶点(x轴正方向)

    # 以绿色绘制y轴
    glColor4f(0.0, 1.0, 0.0, 1.0)  # 设置当前颜色为绿色不透明
    glVertex3f(0.0, -0.8, 0.0)  # 设置y轴顶点(y轴负方向)
    glVertex3f(0.0, 0.8, 0.0)  # 设置y轴顶点(y轴正方向)

    # 以蓝色绘制z轴
    glColor4f(0.0, 0.0, 1.0, 1.0)  # 设置当前颜色为蓝色不透明
    glVertex3f(0.0, 0.0, -0.8)  # 设置z轴顶点(z轴负方向)
    glVertex3f(0.0, 0.0, 0.8)  # 设置z轴顶点(z轴正方向)

    glEnd()  # 结束绘制线段

    # ---------------------------------------------------------------
    glBegin(GL_TRIANGLES)  # 开始绘制三角形(z轴负半区)

    glColor4f(1.0, 0.0, 0.0, 1.0)  # 设置当前颜色为红色不透明
    glVertex3f(-0.5, -0.366, -0.5)  # 设置三角形顶点
    glColor4f(0.0, 1.0, 0.0, 1.0)  # 设置当前颜色为绿色不透明
    glVertex3f(0.5, -0.366, -0.5)  # 设置三角形顶点
    glColor4f(0.0, 0.0, 1.0, 1.0)  # 设置当前颜色为蓝色不透明
    glVertex3f(0.0, 0.5, -0.5)  # 设置三角形顶点

    glEnd()  # 结束绘制三角形

    # ---------------------------------------------------------------
    # ---------------------------------------------------------------
    glBegin(GL_TRIANGLES)  # 开始绘制三角形(z轴正半区)

    glColor4f(1.0, 0.0, 0.0, 1.0)  # 设置当前颜色为红色不透明
    glVertex3f(0.5, 0.366, 0.5)  # 设置三角形顶点
    glColor4f(0.0, 1.0, 0.0, 1.0)  # 设置当前颜色为绿色不透明
    glVertex3f(-0.5, 0.366, -0.5)  # 设置三角形顶点
    glColor4f(0.0, 0.0, 1.0, 1.0)  # 设置当前颜色为蓝色不透明
    glVertex3f(-0.0, -0.5, 0.5)  # 设置三角形顶点

    glEnd()  # 结束绘制三角形

    # ---------------------------------------------------------------
    glFlush()  # 清空缓冲区,将指令送往硬件立即执行


if __name__ == "__main__":
    glutInit()  # 1. 初始化glut库
    glutCreateWindow('Quidam Of OpenGL')  # 2. 创建glut窗口
    glutDisplayFunc(draw)  # 3. 注册回调函数draw()
    glutIdleFunc(draw)
    glutMainLoop()  # 4. 进入glut主循环

你可能感兴趣的:(python基础,python,开发语言)