本文共享自华为云社区《[Python图画处理] 二十七.OpenGL入门及制作根本图形(一) 》,作者:eastmount。

一.OpenGL入门常识

1.什么是OpenGL

OpenGL(Open Graphics Library,译为“开放式图形库”) 是用于烘托2D、3D矢量图形的跨言语、跨渠道的运用程序编程接口(API)。这个接口由近350个不同的函数调用组成,用来制作从简略的图形元件到杂乱的三维景象。OpenGL常用于CAD、虚拟现实、科学可视化程序和电子游戏开发。

OpenGL可用于设置所需的目标、图画和操作,以便开发交互式的3维核算机图形运用程序。OpenGL被设计为一个现代化的、硬件无关的接口,因此咱们能够在不考虑核算机操作体系或窗口体系的前提下,在多种不同的图形硬件体系上,或许彻底经过软件的方法完成OpenGL的接口。OpenGL的高效完成(利用了图形加速硬件)存在于Windows,部分UNIX渠道和Mac OS。这些完成一般由显现设备厂商供给,而且十分依赖于该厂商供给的硬件。

跟我学Python图像处理丨带你入门OpenGL

OpenGL规范由1992年成立的OpenGL架构评审委员会(ARB)保护。ARB由一些对创立一个一致的、遍及可用的API特别感兴趣的公司组成。到了今天已经发布了十分多的OpenGL版别,以及大量构建于OpenGL之上以简化运用程序开发进程的软件库。这些软件库大量用于视频游戏、科学可视化和医学软件的开发,或许只是用来显现图画。

一个用来烘托图画的OpenGL程序需求履行的主要操作如下:

  • 从OpenGL的几许图元中设置数据,用于构建形状
  • 运用不同的着色器(shader)对输入的图元数据履行核算操作,判断它们的方位、色彩,以及其他烘托特点
  • 将输入图元的数学描绘转换为与屏幕方位对应的像素片元(fragment),这一步也称作光栅化(rasterization)
  • 最终,针对光栅化进程发生的每个片元,履行片元着色器(fragment shader),然后决议这个片元的最终色彩和方位
  • 假如有必要,还需求对每个片元履行一些额定的操作,例如判断片元对应的目标是否可见,或许将片元的色彩与当时屏幕方位的色彩进行融合

2.OpenGL装置

作者的电脑环境为Win10+Python3.7,打开CMD调用pip东西进行装置,如下图所示。

cd C:\Software\Program Software\Python37\Scripts
pip install pyopengl

跟我学Python图像处理丨带你入门OpenGL

但一般装置成功之后,运转代码会报错“OpenGL.error.NullFunctionError: Attempt to call an undefined function glutInit, check for bool(glutInit) before calling”。

跟我学Python图像处理丨带你入门OpenGL

据说是pip默许装置的是32位版别的pyopengl,而作者的操作体系是64位。网上许多大牛会去 “www.lfd.uci.edu/~gohlke/pyt… 网站下载适合自己的版别。比方Python3.7且64位操作体系。

跟我学Python图像处理丨带你入门OpenGL

装置流程如下所示:

pip install D:\PyOpenGL-3.1.5-cp37-cp37m-win_amd64.whl
pip install D:\PyOpenGL-3.1.5-cp37-cp37m-win32.whl

跟我学Python图像处理丨带你入门OpenGL

写到这里,咱们Python的OpenGL库就装置成功了!

二.OpenGL入门程序

咱们首先介绍两个入门代码,然后再进行深化的解说。

1.OpenGL制作正方形

完整代码如下:

# -*- coding: utf-8 -*-
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
# 制作图画函数
def display():
    # 铲除屏幕及深度缓存
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
    # 设置赤色
    glColor3f(1.0, 0.0, 0.0)
    # 开端制作四边形
    glBegin(GL_QUADS)
    # 制作四个极点
    glVertex3f(-0.5, -0.5, 0.0)
    glVertex3f(0.5, -0.5, 0.0)
    glVertex3f(0.5, 0.5, 0.0)
    glVertex3f(-0.5, 0.5, 0.0)
    # 完毕制作四边形
    glEnd()
    # 清空缓冲区并将指令送往硬件履行
    glFlush()
# 主函数
if __name__ == "__main__":
    # 运用glut库初始化OpenGL
    glutInit()
    # 显现方法 GLUT_SINGLE无缓冲直接显现|GLUT_RGBA选用RGB(A非alpha)
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA)
    # 设置窗口方位巨细
    glutInitWindowSize(400, 400)
    # 创立窗口
    glutCreateWindow("eastmount")
    # 调用display()函数制作图画
    glutDisplayFunc(display)
    # 进入glut主循环
    glutMainLoop()

运转成果如下图所示:

跟我学Python图像处理丨带你入门OpenGL

中心过程如下:

  • 主函数运用glut库初始化OpenGL
    glutInit()

  • 设置显现方法并初始化glut窗口(画布)
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA)
    glutInitWindowSize(400, 400)
    glutCreateWindow(“eastmount”)

  • 注册制作图画的回调函数,如display()
    glutDisplayFunc(display)

  • 制作图画display函数,包含铲除画布、设置色彩、制作图元、设置定点、完毕制作、改写履行
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
    glColor3f(1.0, 0.0, 0.0)
    glBegin(GL_QUADS)
    glVertex3f(-0.5, -0.5, 0.0)
    glVertex3f(0.5, -0.5, 0.0)
    glVertex3f(0.5, 0.5, 0.0)
    glVertex3f(-0.5, 0.5, 0.0)
    glEnd()
    glFlush()

  • 进入glut主循环
    glutMainLoop()

2.OpenGL制作水壶

接着补充一段经典的水壶代码,一切核算机试卷、核算机图形学、3D图画范畴都会制作它。

# -*- coding: utf-8 -*-
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
# 制作图画函数
def drawFunc():
    # 铲除屏幕及深度缓存
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
    # 设置绕轴旋转(角度,x,y,z)
    glRotatef(0.1, 5, 5, 0)
    # 制作实心茶壶
    # glutSolidTeapot(0.5)
    # 制作线框茶壶
    glutWireTeapot(0.5)
    # 改写显现图画
    glFlush()
# 主函数
if __name__ == "__main__":
    # 运用glut库初始化OpenGL
    glutInit()
    # 显现方法 GLUT_SINGLE无缓冲直接显现|GLUT_RGBA选用RGB(A非alpha)
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA)
    # 设置窗口方位及巨细
    glutInitWindowPosition(0, 0)
    glutInitWindowSize(400, 400)
    # 创立窗口
    glutCreateWindow("CSDN Eastmount")
    # 调用display()函数制作图画
    glutDisplayFunc(drawFunc)
    # 设置全局的回调函数
    # 当没有窗口事情到达时,GLUT程序功能能够履行后台处理任务或接连动画
    glutIdleFunc(drawFunc)
    # 进入glut主循环
    glutMainLoop()

运转成果如下图所示,它主要调用glutSolidTeapot(0.5)函数制作完成实心茶壶,glutWireTeapot(0.5)函数制作线框茶壶。

跟我学Python图像处理丨带你入门OpenGL

跟我学Python图像处理丨带你入门OpenGL

留意,glut供给了一些现成的制作立体的API,如glutWireSphere制作球、glutWireCone制作椎体、glutWireCube制作立体、glutWireTorus制作甜圈、glutWireTeapot制作茶壶、glutWireOctahedron制作八面体,请读者自行提高。

3.OpenGL制作多个图形

接下来制作一个坐标系,并别离制作四个图形,设置不同色彩,代码如下所示。

# -*- coding: utf-8 -*-
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
# 制作图画函数
def display():
    # 铲除屏幕及深度缓存
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
    # 制作线段
    glBegin(GL_LINES)
    glVertex2f(-1.0, 0.0)       # 左下角极点
    glVertex2f(1.0, 0.0)        # 右下角极点
    glVertex2f(0.0, 1.0)        # 右上角极点
    glVertex2f(0.0, -1.0)       # 左上角极点
    glEnd()
    # 制作极点
    glPointSize(10.0)
    glBegin(GL_POINTS)
    glColor3f(1.0, 0.0, 0.0)    # 赤色
    glVertex2f(0.3, 0.3)
    glColor3f(0.0, 1.0, 0.0)    # 绿色
    glVertex2f(0.5, 0.6)
    glColor3f(0.0, 0.0, 1.0)    # 蓝色
    glVertex2f(0.9, 0.9)
    glEnd()
    # 制作四边形
    glColor3f(1.0, 1.0, 0)
    glBegin(GL_QUADS)
    glVertex2f(-0.2, 0.2)
    glVertex2f(-0.2, 0.5)
    glVertex2f(-0.5, 0.5)
    glVertex2f(-0.5, 0.2)
    glEnd()
    # 制作多边形
    glColor3f(0.0, 1.0, 1.0)
    glPolygonMode(GL_FRONT, GL_LINE)
    glPolygonMode(GL_BACK, GL_FILL)
    glBegin(GL_POLYGON)
    glVertex2f(-0.5, -0.1)
    glVertex2f(-0.8, -0.3)
    glVertex2f(-0.8, -0.6)
    glVertex2f(-0.5, -0.8)
    glVertex2f(-0.2, -0.6)
    glVertex2f(-0.2, -0.3)
    glEnd()
    # 制作三角形
    glColor3f(1.0, 1.0, 1.0)
    glPolygonMode(GL_FRONT, GL_FILL)
    glPolygonMode(GL_BACK, GL_LINE)
    glBegin(GL_TRIANGLES)
    glVertex2f(0.5, -0.5)
    glVertex2f(0.3, -0.3)
    glVertex2f(0.2, -0.6)
    # 完毕制作四边形
    glEnd()
    # 清空缓冲区并将指令送往硬件履行
    glFlush()
# 主函数
if __name__ == "__main__":
    # 运用glut库初始化OpenGL
    glutInit()
    # 显现方法 GLUT_SINGLE无缓冲直接显现|GLUT_RGBA选用RGB(A非alpha)
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA)
    # 设置窗口方位及巨细
    glutInitWindowSize(400, 400)
    glutInitWindowPosition(500, 300)
    # 创立窗口
    glutCreateWindow("CSDN Eastmount")
    # 调用display()函数制作图画
    glutDisplayFunc(display)
    # 进入glut主循环
    glutMainLoop()

输出成果如下图所示:

跟我学Python图像处理丨带你入门OpenGL

4.OpenGL绘图代码及原理详解

该部分将具体解说上面三段代码的中心常识,协助咱们稳固根底。作者让咱们先看代码及其运转作用,然后提高OpenGL编程兴趣,再深化分析其原理,这种倒叙的方法期望您们喜欢。

(1) 中心函数
上述代码中,以glut最初的函数都是GLUT东西包所供给的函数。

  • glutInit():对GLUT进行初始化,该函数有必要在其它的GLUT运用之前调用一次。其格局比较死板,一般glutInit()直接调用即可。
  • glutInitDisplayMode():设置显现方法,其间GLUT_RGB表明运用RGB色彩,与之对应的是GLUT_INDEX(表明运用索引色彩);GLUT_SINGLE表明运用单缓冲,与之对应的是GLUT_DOUBLE(表明运用双缓冲)。更多参数请读者阅读官方网站或Google。
  • glutInitWindowPosition():设置窗口在屏幕中的方位。
  • glutInitWindowSize():设置窗口的巨细,两个参数表明长度和宽度。
  • glutCreateWindow():依据当时设置的信息创立窗口,参数将作为窗口的标题。需求留意的是,当窗口被创立后,并不是当即显现到屏幕上,需求调用glutMainLoop()才干看到窗口。
  • glutDisplayFunc():设置一个函数,当需求进行画图时,这个函数就会被调用,一般用来调用制作图形函数。
  • glutMainLoop():进行一个音讯循环,咱们需求知道这个函数能够显现窗口,而且等待窗口封闭后才会返回。

以gl最初的函数都是OpenGL的规范函数。

  • glClear():铲除,其间参数GL_COLOR_BUFFER_BIT表明铲除色彩,GL_DEPTH_BUFFER_BIT表明铲除深度。

  • glRectf():画一个矩形,四个参数别离表明位于对角线上的两个点的横、纵坐标。

  • glFlush():改写显现图画,保证前面的OpenGL指令当即履行,而不是让它们在缓冲区中等待。

  • OpenGL要求指定极点的指令(glVertex2f)有必要包含在glBegin()函数和glEnd()函数之间履行。

(2) 制作极点
极点(vertex)是 OpengGL 中十分重要的概念,描绘线段、多边形都离不开极点。它们都是以glVertex最初,后边跟一个数字和1~2个字母,比方:

  • glVertex2d
  • glVertex2f
  • glVertex3f
  • glVertex3fv

数字表明参数的个数,2表明有2个参数(xy坐标),3表明三个(xyz坐标),4表明四个(齐次坐标 w)。字母表明参数的类型,s表明16位整数(OpenGL中将这个类型界说为GLshort),i表明32位整数(OpenGL中将这个类型界说为GLint和GLsizei),f表明32为浮点数(OpenGL中将这个类型界说为GLfloat和GLclampf),d表明64位浮点数(OpenGL中将这个类型界说为GLdouble和GLclampd)。例如:

  • glVertex2i(1, 3)
  • glVertex2f(1.0, 3.0)
  • glVertex3f(1.0, 3.0, 1.0)
  • glVertex4f(1.0, 3.0, 0.0, 1.0)

留意,OpenGL中许多函数都选用这种方法命名。

(3) 设置色彩
在OpenGL中,设置色彩函数以glColor最初,后边跟着参数个数和参数类型。参数能够是0到255之间的无符号整数,也能够是0到1之间的浮点数。三个参数别离表明RGB分量,第四个参数表明透明度(其实叫不透明度更恰当)。以下最常用的两个设置色彩的方法:

  • glColor3f(1.0,0.0,0.0) #赤色
  • glColor3f(0.0,1.0,0.0) #绿色
  • glColor3f(0.0,0.0,1.0) #蓝色
  • glColor3f(1.0,1.0,1.0) #白色
  • glColor4f(0.0,1.0,0.0,0.0) #赤色且不透明度
  • glColor3ub(255, 0, 0) #赤色

留意,OpenGL是运用状况机方法,色彩是一个状况变量,设置色彩就是改变这个状况变量并一直生效,直到再次调用设置色彩的函数。除了色彩,OpenGL 还有许多的状况变量或方法。

(4) 制作根本图形
前面咱们介绍了各种图画,下表展示了常见的图画元件。

  • 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:制作接连四边形

详见下图所示。

跟我学Python图像处理丨带你入门OpenGL

三.OpenGL根底常识

在深化学习OpenGL之前,咱们有必要了解一些最常用的图形学名词、OpenGL原理和语法。

1.OpenGL语法

OpenGL程序的根本结构一般包含——初始化物体烘托所对应的状况、设置需求烘托的物体。烘托(render)表明核算机从模型创立最终图画的进程,OpenGL只是其间一种烘托体系。模型(model)或许场景目标是经过几许图元,比方点、线和三角形来构建的,而图元与模型的极点(vertex)也存在着各种对应的关系。

OpenGL另一个最实质的概念叫着色器,它是图形硬件设备所履行的一类特征函数。能够将着色器理解为专为图形处理单元(GPU)编译的一种小型程序。在OpenGL中,会用到一直不同的着色阶段(shader stage),最常用的包含极点着色器(vertex shader)以及片元着色器,前者用于处理极点数据,后者用于处理光栅化后的片元数据。一切的OpenGL程序都需求用到这两类着色器。最终生成的图画包含了屏幕上制作的一切像素点。像素(pixel)是显现器上最小的可见单元。核算机体系将一切的像素保存到帧缓存(framebuffer)傍边,后者是由图形硬件设备办理的一块独立内存区域,能够直接映射到最终的显现设备上。

跟我学Python图像处理丨带你入门OpenGL

正如前面您看到的,OpenGL库中一切的函数都会以字符“gl”作为前缀,然后是一个或许多个大写字母最初的词组,以此来指令一个完整的函数(例如glBindVertexArray())。OpenGL的一切函数都是这种格局,上面看到的“glut”最初的函数,它们来自第三方库OpenGL Utility Toolkit(GLUT),能够用来显现窗口、办理用户输入以及履行其他一些操作。

与函数命名约定类似,OpenGL库中界说的常量也是GL_COLOR_BUFFER_BIT的方法,常量以GL_作为前缀,而且运用下划线来切割单词。这些常量的界说是经过#define来完成的,它们根本能够在OpenGL的头文件glcorearb.h和glext.h中找到。

为了能够方便地在不同的操作体系之间移植OpenGL程序,它还为函数界说了不同的数据类型,例如GLfloat是浮点数类型。此外,比方glVertex*()的函数,它有多种改变方法,如glVertex2d、glVertex2f。在函数称号的“中心”部分之后,经过后缀的改变来提示函数应当传入的参数,一般由一个数字和1~2个字母组成。glVertex2f()中的“2”表明需求传入2个参数,f表明浮点数。

跟我学Python图像处理丨带你入门OpenGL

2.旧式OpenGL vs 现代OpenGL

(1) 旧式OpenGL
在大多数核算机图形体系中,绘图的方法是将一些极点发送给处理管线,管线由一系列功能模块互相衔接而成。最近,OpenGL运用编程接口(API)从固定功能的图形管线转换为可编程的图形管线。

如下图制作正方形的代码,它运用的是旧式OpenGL,要为三维图元(在这个代码中,是一个GL_QUADS即矩形)指定各个极点,但随后每个极点需求被别离发送到GPU,这是低效的方法。 这种旧式编程方法伸缩性不好,假如几许图形变得杂乱,程序就会很慢。对于屏幕上的极点和像素如何改换,它只供给了有限的操控。

后续咱们将专注于现代的OpenGL,可是网络上也会有旧式OpenGL的比如。

跟我学Python图像处理丨带你入门OpenGL

(2) 现代OpenGL
现代OpenGL利用一系列的操作,即经过“三维图形管线”制作图形,其根本流程如下图所示。

跟我学Python图像处理丨带你入门OpenGL

简化三维图形管线分为6步:

  • 三维几许图形界说(VBO等)。 在第一步,经过界说在三维空间中的三角形的极点,并指定每个极点相关联的色彩,咱们界说了三维几许图形。
  • 极点着色器。 接下来,改换这些极点:第一次改换将这些极点放在三维空间中,第二次改换将三维坐标投影到二维空间。依据照明等因素,对应极点的色彩值也在这一步中核算,这在代码中一般称为“极点着色器”。
  • 光栅化。 将几许图形“光栅化”(从几许物体转换为像素)。
  • 片段着色器。 针对每个像素,履行另一个名为“片段着色器”的代码块。正如极点着色器作用于三维极点,片段着色器作用于光栅化后的二维像素。
  • 帧缓冲区操作(深度测试、混合等)。 最终,像素经过一系列帧缓冲区操作,其间,它经过“深度缓冲区查验”(查看一个片段是否遮挡另一个)、“混合”(用透明度混合两个片段)以及其他操作,其当时的色彩与帧缓冲区中该方位已有的色彩结合。
  • 帧缓冲区。 这些改变最终体现在最终的帧缓冲区上,一般显现在屏幕上。

PS:该部分参阅Mahesh Venkitachalam大神编写的《Python极客项目编程》,代码能够查看:github.com/electronut/…

3.OpenGL制作时钟

最终补充“xiaoge2016教师”的一段兴趣代码,经过OpenGL制作时钟,留意它是跳动的。

# -*- coding: utf-8 -*-
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
import math
import time
h = 0
m = 0
s = 0
# 制作图画函数
def Draw():
    PI = 3.1415926
    R = 0.5
    TR = R - 0.05
    glClear(GL_COLOR_BUFFER_BIT)
    glLineWidth(5)
    glBegin(GL_LINE_LOOP)
    for i in range(100):
        glVertex2f(R * math.cos(2 * PI / 100 * i), R * math.sin(2 * PI / 100 * i))
    glEnd()
    glLineWidth(2)
    for i in range(100):
        glBegin(GL_LINES)
        glVertex2f(TR * math.sin(2 * PI / 12 * i), TR * math.cos(2 * PI / 12 * i))
        glVertex2f(R * math.sin(2 * PI / 12 * i), R * math.cos(2 * PI / 12 * i))
        glEnd()
    glLineWidth(1)
    h_Length = 0.2
    m_Length = 0.3
    s_Length = 0.4
    count = 60.0
    s_Angle = s / count
    count *= 60
    m_Angle = (m * 60 + s) / count
    count *= 12
    h_Angle = (h * 60 * 60 + m * 60 + s) / count
    glLineWidth(1)
    glBegin(GL_LINES)
    glVertex2f(0.0, 0.0)
    glVertex2f(s_Length * math.sin(2 * PI * s_Angle), s_Length * math.cos(2 * PI * s_Angle))
    glEnd()
    glLineWidth(5)
    glBegin(GL_LINES)
    glVertex2f(0.0, 0.0)
    glVertex2f(h_Length * math.sin(2 * PI * h_Angle), h_Length * math.cos(2 * PI * h_Angle))
    glEnd()
    glLineWidth(3)
    glBegin(GL_LINES)
    glVertex2f(0.0, 0.0)
    glVertex2f(m_Length * math.sin(2 * PI * m_Angle), m_Length * math.cos(2 * PI * m_Angle))
    glEnd()
    glLineWidth(1)
    glBegin(GL_POLYGON)
    for i in range(100):
        glVertex2f(0.03 * math.cos(2 * PI / 100 * i), 0.03 * math.sin(2 * PI / 100 * i));
    glEnd()
    glFlush()
# 更新时间函数
def Update():
    global h, m, s
    t = time.localtime(time.time())
    h = int(time.strftime('%H', t))
    m = int(time.strftime('%M', t))
    s = int(time.strftime('%S', t))
    glutPostRedisplay()
# 主函数
if __name__ == "__main__":
    glutInit()
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA)
    glutInitWindowSize(400, 400)
    glutCreateWindow("My clock")
    glutDisplayFunc(Draw)
    glutIdleFunc(Update)
    glutMainLoop() 

其运转成果如下图所示:

跟我学Python图像处理丨带你入门OpenGL

四.总结

本篇文章主要解说Python和OpenGL根底常识,包含装置、根底语法、制作图形等。期望对读者有一定协助,也期望这些常识点为读者从事Python图画处理相关项目实践或科学研究供给一定根底。

参阅文献:

[1] 《OpenGL编程指南(第8版)》 作者:Dave Shreiner Granham Sellers等,王锐 译
[2] 《Python极客项目编程》 作者:Mahesh Venkitachalam,王海鹏 译
[3] 《OpenGL编程精粹》杨柏林 陈根浪 徐静 编著
[4] 写给 python 程序员的 OpenGL 教程 – 许教师(天元浪子)
[5] Python之OpenGL笔记(2):现代OpenGL编程常用的几个通用函数 – 大龙教师
[6] python3+OpenGL环境配置 – GraceSkyer教师
[7] VS2012下基于Glut OpenGL显现一些立体图形示例程序 – yearafteryear教师
[8] Python——OpenGL – 白季飞龙教师
[9] python+opengl显现三维模型小程序 – xiaoge2016
[10] github.com/electronut/…

点击关注,第一时间了解华为云新鲜技能~