在三维显示领域,OpenGL 是神一样的存在,其地位就像编程语言里面的 C 一样。基于 OpenGL 衍生出来的分支、派系,林林总总。Python 旗下,影响较大的三维库有 pyOpenGl / VTK / Mayavi / Vispy 等,它们各自拥有庞大的用户体。VTK 在医学领域应用广泛,Vispy 在科研领域粉丝众多。VTK 和 Vispy 都是基于 OpenGL 的扩展,Mayavi 则是基于VTK 的,因此很多的医学影像应用都是采用 Python + VTK + ITK + Mayavi 的组合(ITK 是图像处理库,类似于 OpenCV 或 PIL)。
上述三维渲染库,包括 PyOpenGl,都有一个共同的特点,那就是只专注于三维功能的实现,而疏于对 UI 的支持。比 Vispy,虽然支持以 wx 或者 Qt 作为后端,但绑定后端以后,在窗口管理、交互操作等方面还是存在不少问题。PyOpenGl 做得更简单,提供一个 GLUT 库就算是对 UI 的支持了。事实上,在复杂的三维展示系统中,UI 的重要性并不亚于 OpenGL。如果能为 OpenGL 找到一位 UI 搭档,必将提高程序的可靠性和可操作性,增强用户感受。wxPython 和 PyOpenGL 就是这样的一对黄金搭档。
pyOpenGL 的入门教程有很多,我也有一篇博文《写给 Python 程序员的 OpenGL 教程》。特别提醒一下,这篇博文最后提到顶点缓冲区对象 VBO,并有演示代码。VBO 的概念很重要很重要很重要,只有学会使用 VBO,才能真正进入 OpenGL 的精彩世界。
早期的 OpenGL 使用立即渲染模式(Immediate mode,也就是固定渲染管线),概念清晰易于理解,绘制图形也很方便,但效率太低。从 OpenGL3.2 开始,规范文档开始废弃立即渲染模式,并鼓励开发者在 OpenGL 的核心模式(Core-profile)下进行开发,这个分支的规范完全移除了旧的特性。
VBO 是 OpenGL 核心模式的基础。VBO 将顶点数据集存储在 GPU 中,这意味着渲染 VBO 数据会很快。不过,数据从 RAM 传送到 GPU 是有代价的。VBO 虽然在 GPU 上,但并没有使用 GPU 的运算功能。在 VBO 之上,还有 VAO 的概念,即Vertex Array Object,顶点数组对象。这个概念很复杂,我们可以简单把 VAO 理解为 VBO 管理者。由于 VAO 依赖于显卡,通用性较差,我选择绕过它。
官网:https://www.opengl.org/resources/libraries/glut/
预备知识
OpenGL 是 Open Graphics Library 的简写,意为“开放式图形库”,是用于渲染 2D、3D 矢量图形的跨语言、跨平台的应用程序编程接口(API)。OpenGL 不是一个独立的平台,因此,它需要借助于一种编程语言才能被使用。C / C++ / python / java 都可以很好支持 OpengGL,我当然习惯性选择 python 语言。
如果读者是 python 程序员,并且了解 numpy,接下来的阅读应该不会有任何障碍;否则,我建议先花半小时学习一下 python 语言。关于 numpy,可以参考我的另一篇博文《数学建模三剑客MSN》。事实上,我觉得 python 语言近乎于自然语言,只要读者是程序员,即便不熟悉 python,读起来也不会有多大问题。
另外,读者也不必担心数学问题。使用 OpenGL 不需要具备多么高深的数学水平,只要能辅导初中学生的数学作业,就足够用了。
坐标系
在 OpenGL 的世界里,有各式各样的坐标系。随着对 OpenGL 概念的理解,我们至少会接触到六种坐标系,而初始只需要了解其中的三个就足够用了(第一次阅读这段话的时候,只需要了解世界坐标系就可以了)。
世界坐标系(World Coordinates)
世界坐标系是右手坐标系,以屏幕中心为原点(0, 0, 0),且是始终不变的。
视点坐标系(Eye or Camera Coordinates)
视点坐标是以视点为原点,以视线的方向为Z+轴正方向的坐标系。OpenGL 管道会将世界坐标先变换到视点坐标,然后进行裁剪,只有在视线范围(视景体)之内的场景才会进入下一阶段的计算。
屏幕坐标系(Window or Screen Coordinates)
OpenGL 的重要功能之一就是将三维的世界坐标经过变换、投影等计算,最终算出它在显示设备上对应的位置,这个位置就称为设备坐标。在屏幕、打印机等设备上的坐标是二维坐标。值得一提的是,OpenGL 可以只使用设备的一部分进行绘制,这个部分称为视区或视口(viewport)。投影得到的是视区内的坐标(投影坐标),从投影坐标到设备坐标的计算过程就是设备变换了。
投影
三维场景中的物体最终都会显示在类似屏幕这样的二维观察平面上。将三维物体变为二维图形的变换成为投影变换。最常用的投影有两种:平行投影和透视投影。如下图所示,F 是投影面,p1p2 为三维空间中的一条直线,p’1 和 p’2 分别是 p1 和 p2 在 F 上的投影,虚线表示投影线,O 为投影中心。
平行投影
这里所说的平行投影,特指正交平行投影——投影线垂直于投影面。将一个三维点 (x,y,z) 正交平行投影到 xoy 平面上,则投影点坐标为 (x,y,0)。由于平行投影丢弃了深度信息,所以无法产生真实感,但可以保持物体之间相对大小关系不变。
透视投影
透视投影将投影面置于观察点和投影对象之间,距离观察者越远的物体,投影尺寸越小,投影效果具有真实感,常用于游戏和仿真领域。
视景体
无论是平行投影还是透视投影,投影成像都是在投影面上——我们可以把投影面理解成显示屏幕。世界坐标系描述的三维空间是无限的,投影平面是无限的,但(我们能够看到的)屏幕面积总是有限的,因此在投影变换时,通常只处理能够显示在屏幕上的那一部分三维空间。从无限三维空间中裁切出来的可以显示在屏幕上的部分三维空间,我们称之为视景体。视景体有六个面,分别是左右上下和前后面。
对于平行投影而言,视景体是一个矩形平行六面体;对于透视投影来说,视景体是一个棱台。理解这一点并不难:因为越远处的物体在投影窗口的透视投影越小,也就意味着填满投影窗口需要更大的体量,视景体自然就变成了棱台。
视口
对于平行投影而言,视口就是由视景体的左右上下四个面围成的矩形,对于透视投影来说,视口就是视景体的前截面在投影窗口上的透视投影。
视口是 OpenGL 中比较重要的概念,现阶段可以简单理解成屏幕(或其他输出设备)。事实上,视口和屏幕是相关但又不相同的,屏幕有固定的宽高比,而视口大小可以由用户自行定义。通常,为了适应不同宽高比的屏幕,在设置视口时,会根据屏幕宽高比调整视景体(增加宽度或高度)。
视点
现实生活中,人们看到的三维空间物体的样子取决于观察者站在什么角度去看。这里面包含着三个概念:
- 观察者的位置:眼睛在哪儿?
- 观察者的姿势:站立还是倒立?左侧卧还是右侧卧?
- 观察对象:眼睛盯着哪里?
对应在 OpenGL 中,也有同样的概念,即视点的位置、瞄准方向的参考点,以及(向上的)方向。
OpenGL 变换
下图是三维图形的显示流程。世界坐标系中的三维物体经过视点变换和一系列几何变换(平移、旋转、缩放)之后,坐标系变换为视点坐标系;经过投影和裁剪之后,坐标系变换为归一化设备坐标系;最后经过视口变换显示在屏幕上,相应地,坐标系变成了窗口坐标系。
- 视点变换:相当于设置视点的位置和方向
- 模型变换:包括平移、旋转、缩放等三种类型
- 裁剪变换:根据视景体定义的六个面(和附加裁剪面)对三维空间裁剪
- 视口变换:将视景体内投影的物体显示在二维的视口平面上
安装 pyopengl
下载编译好的PyOpenGL,也就是一个whl文件(这里不建议直接用命令“pip install PyOpenGL”进行安装,不然会出现“OpenGL.error.NullFunctionError”错误),地址:https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyopengl,如下,选择适合自己的版本:
下载完这两个文件后:
pip install whl文件
注意:如果安装失败,可能是系统里没有c++环境导致的,则需要先安装C++,提供C环境。
OpenGL 库及函数简介
我第一次接触 OpenGL 的 GL / GLU / GLUT 的时候,一下就被这些长得像孪生兄弟的库名字给整懵圈了,要不是内心强大,也许就跟 OpenGL 说再见了。时间久了才发现,OpenGL 的库及函数命名规则非常合理,便于查找、记忆。
OpenGL函数的命名格式如下:
<库前缀><根命令><可选的参数个数><可选的参数类型>
常见的库前缀有 gl、glu、glut、aux、wgl、glx、agl 等。库前缀表示该函数属于 OpenGL 哪一个开发库。从函数名后面中还可以看出需要多少个参数以及参数的类型。I 代表 int 型,f 代表 float 型,d 代表 double 型,u 代表无符号整型。例如 glColor3f() 表示了该函数属于gl库,参数是三个浮点数。
OpenGL 函数库相关的 API 有核心库(gl)、实用库(glu)、实用工具库(glut)、辅助库(aux)、窗口库(glx、agl、wgl)和扩展函数库等。gl是核心,glu是对gl的部分封装。glut是为跨平台的OpenGL程序的工具包,比aux功能强大。glx、agl、wgl 是针对不同窗口系统的函数。扩展函数库是硬件厂商为实现硬件更新利用OpenGL的扩展机制开发的函数。本文仅对常用的四个库做简单介绍。
OpenGL 核心库 GL
核心库包含有115个函数,函数名的前缀为gl。这部分函数用于常规的、核心的图形处理。此函数由gl.dll来负责解释执行。由于许多函数可以接收不同数以下几类。据类型的参数,因此派生出来的函数原形多达300多个。核心库中的函数主要可以分为以下几类函数:
绘制基本几何图元的函数:
- glBegain()、glEnd()、glNormal*()、glVertex*()
矩阵操作、几何变换和投影变换的函数:
- 如矩阵入栈函数glPushMatrix(),矩阵出栈函数glPopMatrix(),装载矩阵函数glLoadMatrix(),矩阵相乘函数glMultMatrix(),当前矩阵函数glMatrixMode()和矩阵标准化函数glLoadIdentity(),几何变换函数glTranslate*()、glRotate*()和glScale*(),投影变换函数glOrtho()、glFrustum()和视口变换函数glViewport()
颜色、光照和材质的函数:
- 如设置颜色模式函数glColor*()、glIndex*(),设置光照效果的函数glLight*() 、glLightModel*()和设置材质效果函数glMaterial()
显示列表函数:
- 主要有创建、结束、生成、删除和调用显示列表的函数glNewList()、glEndList()、glGenLists()、glCallList()和glDeleteLists()
纹理映射函数:
- 主要有一维纹理函数glTexImage1D()、二维纹理函数glTexImage2D()、设置纹理参数、纹理环境和纹理坐标的函数glTexParameter*()、glTexEnv*()和glTetCoord*()
特殊效果函数:
- 融合函数glBlendFunc()、反走样函数glHint()和雾化效果glFog*()
光栅化、象素操作函数:
- 如象素位置glRasterPos*()、线型宽度glLineWidth()、多边形绘制模式glPolygonMode(),读取象素glReadPixel()、复制象素glCopyPixel()
选择与反馈函数:
- 主要有渲染模式glRenderMode()、选择缓冲区glSelectBuffer()和反馈缓冲区glFeedbackBuffer()
曲线与曲面的绘制函数:
- 生成曲线或曲面的函数glMap*()、glMapGrid*(),求值器的函数glEvalCoord*() glEvalMesh*()
状态设置与查询函数:
- glGet*()、glEnable()、glGetError()
OpenGL 实用库 GLU
包含有43个函数,函数名的前缀为glu。OpenGL提供了强大的但是为数不多的绘图命令,所有较复杂的绘图都必须从点、线、面开始。Glu 为了减轻繁重的编程工作,封装了OpenGL函数,Glu函数通过调用核心库的函数,为开发者提供相对简单的用法,实现一些较为复杂的操作。此函数由glu.dll来负责解释执行。OpenGL中的核心库和实用库可以在所有的OpenGL平台上运行。主要包括了以下几种:
辅助纹理贴图函数:
- gluScaleImage() 、gluBuild1Dmipmaps()、gluBuild2Dmipmaps()
坐标转换和投影变换函数:
- 定义投影方式函数gluPerspective()、gluOrtho2D() 、gluLookAt(),拾取投影视景体函数gluPickMatrix(),投影矩阵计算gluProject()和gluUnProject()
多边形镶嵌工具:
- gluNewTess()、gluDeleteTess()、gluTessCallback()、gluBeginPolygon()、gluTessVertex()、gluNextContour()、gluEndPolygon()
二次曲面绘制工具:
- 主要有绘制球面、锥面、柱面、圆环面gluNewQuadric()、gluSphere()、gluCylinder()、gluDisk()、gluPartialDisk()、gluDeleteQuadric()
非均匀有理B样条绘制工具:
- 主要用来定义和绘制Nurbs曲线和曲面,包括gluNewNurbsRenderer()、gluNurbsCurve()、gluBeginSurface()、gluEndSurface()、gluBeginCurve()、gluNurbsProperty()
错误反馈工具:
- 获取出错信息的字符串gluErrorString()
OpenGL 工具库 GLUT
包含大约30多个函数,函数名前缀为glut。glut是不依赖于窗口平台的OpenGL工具包,由Mark KLilgrad在SGI编写(现在在Nvidia),目的是隐藏不同窗口平台API的复杂度。函数以glut开头,它们作为aux库功能更强的替代品,提供更为复杂的绘制功能,此函数由glut.dll来负责解释执行。由于glut中的窗口管理函数是不依赖于运行环境的,因此OpenGL中的工具库可以在X-Window, Windows NT, OS/2等系统下运行,特别适合于开发不需要复杂界面的OpenGL示例程序。对于有经验的程序员来说,一般先用glut理顺3D图形代码,然后再集成为完整的应用程序。这部分函数主要包括:
窗口操作函数:
- 窗口初始化、窗口大小、窗口位置函数等 glutInit()、glutInitDisplayMode()、glutInitWindowSize()、glutInitWindowPosition()
回调函数:
- 响应刷新消息、键盘消息、鼠标消息、定时器函数 GlutDisplayFunc()、glutPostRedisplay()、glutReshapeFunc()、glutTimerFunc()、glutKeyboardFunc()、glutMouseFunc()
创建复杂的三维物体:
- 这些和aux库的函数功能相同
菜单函数:
- 创建添加菜单的函数 GlutCreateMenu()、glutSetMenu()、glutAddMenuEntry()、glutAddSubMenu() 和 glutAttachMenu()
程序运行函数:
- glutMainLoop()
Windows 专用库 WGL
针对windows平台的扩展,包含有16个函数,函数名前缀为wgl。这部分函数主要用于连接OpenGL和Windows ,以弥补OpenGL在文本方面的不足。 Windows专用库只能用于Windows环境中。这类函数主要包括以下几类:
绘图上下文相关函数:
- wglCreateContext()、wglDeleteContext()、wglGetCurrentContent()、wglGetCurrentDC()、wglDeleteContent()
文字和文本处理函数:
- wglUseFontBitmaps()、wglUseFontOutlines()
覆盖层、地层和主平面层处理函数:
- wglCopyContext()、wglCreateLayerPlane()、wglDescribeLayerPlane()、wglReakizeLayerPlatte()
其他函数:
- wglShareLists()、wglGetProcAddress()
OpenGL 基本图形的绘制
设置颜色
设置颜色的函数有几十个,都是以 glColor 开头,后面跟着参数个数和参数类型。参数可以是 0 到 255 之间的无符号整数,也可以是 0 到 1 之间的浮点数。三个参数分别表示 RGB 分量,第四个参数表示透明度(其实叫不透明度更恰当)。以下最常用的两个设置颜色的方法:
glColor3f(1.0,0.0,0.0) # 设置当前颜色为红色 glColor4f(0.0,1.0,1.0,1.0) # 设置当前颜色为青色,不透明度 glColor3ub(0, 0, 255) # 设置当前颜色为蓝色
glColor 也支持将三个或四个参数以向量方式传递,例如:
glColor3fv([0.0,1.0,0.0]) # 设置当前颜色为绿色
特别提示:OpenGL 是使用状态机模式,颜色是一个状态变量,设置颜色就是改变这个状态变量并一直生效,直到再次调用设置颜色的函数。除了颜色,OpenGL 还有很多的状态变量或模式。在任何时间,都可以查询每个状态变量的当前值,还可以用 glPushAttrib() 或 glPushClientAttrib() 把状态变量的集合保存起来,必要的时候,再用 glPopAttrib() 或 glPopClientAttrib() 恢复状态变量。
设置顶点
顶点(vertex)是 OpengGL 中非常重要的概念,描述线段、多边形都离不开顶点。和设置颜色类似,设置顶点的函数也有几十个,都是以 glVertex 开头,后面跟着参数个数和参数类型,同样也支持将多个以向量方式传递。 两个参数的话,分别表示 xy 坐标,三个参数则分别表示 xyz 坐标。如有第四个参数,则表示该点的齐次坐标 w;否则,默认 w=1。至于什么是齐次坐标,显然超出了初中数学的范畴,在此不做探讨。
glVertex2f(1.0,0.5) # xoy平面上的点,z=0 glVertex3f(0.5,1.0,0.0) # 三维空间中的点
绘制基本图形
仅仅设置颜色和顶点,并不能画出来什么。我们可以在任何时候改变颜色,但所有的顶点设置,都必须包含在 glBegin() 和 glEnd() 之间,而 glBegin() 的参数则指定了将这些顶点画成什么。以下是 glBegin() 可能的参数选项:
参数说明:
- 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 绘制连续四边形
第一个 OpenGL 程序
通常,我们使用工具库(GLUT)创建 OpenGL 应用程序。为啥不用 GL 或者 GLU 库呢?画画之前总得先有一块画布吧,不能直接拿起画笔就开画。前文说过,工具库主要提供窗口相关的函数,有了窗口,就相当于有了画布,而核心库和实用库,就好比各式各样的画笔、颜料。使用工具库(GLUT)创建 OpenGL 应用程序只需要四步(当然,前提是你需要先准备好绘图函数,并给它取一个合适的名字):
- 初始化glut库
- 创建glut窗口
- 注册绘图的回调函数
- 进入glut主循环
OK,铺垫了这么多之后,我们终于开始第一个 OpenGL 应用程序了:绘制三维空间的世界坐标系,在坐标原点的后方(z轴的负半区)画一个三角形。代码如下:
# -*- coding: utf-8 -*- # ------------------------------------------- # quidam_01.py 三维空间的世界坐标系和三角形 # ------------------------------------------- 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主循环
from OpenGL.GL import * from OpenGL.GLU import * from OpenGL.GLUT import * def Draw(): glClear(GL_COLOR_BUFFER_BIT) glRotatef(0.5, 0, 1, 0) glutWireTeapot(0.5) glFlush() glutInit() glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA) glutInitWindowSize(400, 400) glutCreateWindow("test") glutDisplayFunc(Draw) glutIdleFunc(Draw) glutMainLoop()
运行代码,我这里显示结果如下面左图所示。如果尝试运行这段代码出错的话,我猜应该是 pyopengl 安装出现了问题,首先登录https://www.opengl.org/resources/libraries/glut/,下载下图箭头所指的文件:
解压缩,如下图所示:
将:glut.dll和glut32.dll放在C:\WINDOWS中的SysWOW64(win10 64位操作系统),然后重新运行代码。
短暂的激动之后,你可能会尝试画一些其他的线段,变换颜色或者透明度,甚至绘制多边形。很快你会发现,我们的第一个程序有很多问题,比如:
- 窗口的标题不能使用中文,否则会显示乱码
- 窗口的初始大小和位置无法改变
- 改变窗口的宽高比,三角形宽高比也会改变(如上面右图所示)
- 三角形不应该遮挡坐标轴
- 改变颜色的透明度无效
- 不能缩放旋转
没关系,除了第1个问题我不知道怎么解决(貌似无解),其他问题都不是事儿。和我们的代码相比,一个真正实用的 OpenGL 程序,还有许多工作要做:
- 设置初始显示模式
- 初始化画布
- 绘图函数里面需要增加:
- 清除屏幕及深度缓存
- 投影设置
- 模型试图设置
- 绑定鼠标键盘的事件函数
设置初始显示模式
初始化 glut 库的时候,我们一般都要用 glutInitDisplayMode() 来设置初始的显示模式,它的参数可以是下表中参数的组合。
参数说明:
- GLUT_RGB 指定RGB颜色模式的窗口
- GLUT_RGBA 指定RGBA 颜色模式的窗口
- GLUT_INDEX 指定颜色索引模式的窗口
- GLUT_SINGLE 指定单缓存窗口
- GLUT_DOUBLE 指定双缓存窗口
- GLUT_ACCUM 窗口使用累加缓存
- GLUT_ALPHA 窗口的颜色分量包含 alpha 值
- GLUT_DEPTH 窗口使用深度缓存
- GLUT_STENCIL 窗口使用模板缓存
- GLUT_MULTISAMPLE 指定支持多样本功能的窗口
- GLUT_STEREO 指定立体窗口
- GLUT_LUMINANCE 窗口使用亮度颜色模型
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
使用双缓存窗口,可以避免重绘时产生抖动的感觉。我一般选择 GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH 作为参数来设置初始的显示模式。
初始化画布
开始绘图之前,需要对画布做一些初始化工作,这些工作只需要做一次。比如:
- glClearColor(0.0, 0.0, 0.0, 1.0) # 设置画布背景色。注意:这里必须是4个参数
- glEnable(GL_DEPTH_TEST) # 开启深度测试,实现遮挡关系
- glDepthFunc(GL_LEQUAL) # 设置深度测试函数(GL_LEQUAL只是选项之一)
如有必要,还可以开启失真校正(反走样)、开启表面剔除等。
清除屏幕及深度缓存
每次重绘之前,需要先清除屏幕及深度缓存。这项操作一般放在绘图函数的开头。
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
设置投影
投影设置也是每次重绘都需要的步骤之一。glOrtho() 用来设置平行投影,glFrustum() 用来设置透视投影。这两个函数的参数相同,都是视景体的 left / right / bottom / top / near / far 六个面。
视景体的 left / right / bottom / top 四个面围成的矩形,就是视口。near 就是投影面,其值是投影面距离视点的距离,far 是视景体的后截面,其值是后截面距离视点的距离。far 和 near 的差值,就是视景体的深度。视点和视景体的相对位置关系是固定的,视点移动时,视景体也随之移动。
我个人认为,视景体是 OpengGL 最重要、最核心的概念,它和视口、视点、投影面、缩放、漫游等概念密切关联。只有正确理解了视景体,才能正确设置它的六个参数,才能呈现出我们期望的效果。
为了在窗口宽高比改变时,绘制的对象仍然保持固定的宽高比,一般在做投影变换时,需要根据窗口的宽高比适当调整视景体的 left / right 或者 bottom / top 参数。
假设 view 是视景体,width 和 height 是窗口的宽度和高度,在投影变换之前,需要先声明是对投影矩阵的操作,并将投影矩阵单位化:
glMatrixMode(GL_PROJECTION) glLoadIdentity() if width > height: k = width / height glFrustum(view [0]*k, view [1]*k, view [2], view [3], view [4], view [5]) else: k = height / width glFrustum(view [0], view [1], view [2]*k, view [3]*k, view [4], view [5])
设置视点
视点是和视景体关联的概念。设置视点需要考虑眼睛在哪儿、看哪儿、头顶朝哪儿,分别对应着eye, lookat 和 eye_up 三个向量。
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, width, height)
设置模型视图
模型平移、旋转、缩放等几何变换,需要切换到模型矩阵:
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glScale(1.0, 1.0, 1.0)
捕捉鼠标事件、键盘事件和窗口事件
GLUT 库提供了几个函数帮我们捕捉鼠标事件、键盘事件和窗口事件:
glutMouseFunc()
该函数捕捉鼠标点击和滚轮操作,返回4个参数给被绑定的事件函数:键(左键/右键/中键/滚轮上/滚轮下)、状态(1/0)、x坐标、y坐标
- glutMotionFunc()该函数捕捉有一个鼠标键被按下时的鼠标移动给被绑定的事件函数,返回2个参数:x坐标、y坐标
- glutPassiveMotionFunc()该函数捕捉鼠标移动,返回2个参数给被绑定的事件函数:x坐标、y坐标
- glutEntryFunc()该函数捕捉鼠标离开或进入窗口区域,返回1个参数给被绑定的事件函数:GLUT_LEFT 或者 GLUT_ENTERED
- glutKeyboardFunc(keydown)该函数捕捉键盘按键被按下,返回3个参数给被绑定的事件函数:被按下的键,x坐标、y坐标
- glutReshapeFunc()该函数捕捉窗口被改变大小,返回2个参数给被绑定的事件函数:窗口宽度、窗口高度
如果我们需要捕捉这些事件,只需要定义事件函数,注册相应的函数就行:
def reshape(width, height): pass def mouseclick(button, state, x, y): pass def mousemotion(x, y): pass def keydown(key, x, y): pass glutReshapeFunc(reshape) # 注册响应窗口改变的函数reshape() glutMouseFunc(mouseclick) # 注册响应鼠标点击的函数mouseclick() glutMotionFunc(mousemotion) # 注册响应鼠标拖拽的函数mousemotion() glutKeyboardFunc(keydown) # 注册键盘输入的函数keydown()
综合应用
是时候把我们上面讲的这些东西完整的演示一下了。下面的代码还是画了世界坐标系,并在原点前后各画了一个三角形。鼠标可以拖拽视点绕参考点旋转(二者距离保持不变),滚轮可以缩放模型。敲击退格键或回车键可以让视点远离或接近参考点。敲击 x/y/z 可以减小参考点对应的坐标值,敲击 X/Y/Z 可以增大参考点对应的坐标值。敲击空格键可以切换投影模式。
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, 20.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()) if dist > 0: phi = np.arcsin((EYE[1]-LOOK_AT[1])/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) # 设置画布背景色。注意:这里必须是4个参数 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]) # 设置模型视图 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) # --------------------------------------------------------------- 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() # 切换缓冲区,以显示绘制内容 def reshape(width, height): global WIN_W, WIN_H WIN_W, WIN_H = width, height glutPostRedisplay() 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() 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 dy = y - MOUSE_Y MOUSE_X, MOUSE_Y = x, 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主循环
加速渲染
实际应用 OpenGL 绘制三维图像时,往往需要处理数以万计的顶点,有时甚至是百万级、千万级。我们通常不会在绘制函数里面传送这些数据,而是在绘制之前,将这些数据提前传送到GPU。绘制函数每次绘制时,只需要从GPU的缓存中取出数据即可,极大地提高了效率。这个机制地实现,依赖于顶点缓冲区对象(Vertex Buffer Object),简称VBO。
尽管 VBO 是显卡的扩展,其实没有用到GPU运算,也就是说 VBO 不用写着色语言,直接用opengl函数就可以调用,主要目的是用于加快渲染的速。
VBO 将顶点信息放到 GPU 中,GPU 在渲染时去缓存中取数据,二者中间的桥梁是 GL-Context。GL-Context 整个程序一般只有一个,所以如果一个渲染流程里有两份不同的绘制代码,GL-context 就负责在他们之间进行切换。这也是为什么要在渲染过程中,在每份绘制代码之中会有 glBindbuffer、glEnableVertexAttribArray、glVertexAttribPointer。如果把这些都放到初始化时候完成,使用一种结构记录该次绘制所需要的所有 VBO 所需信息,把它保存到 VBO特定位置,绘制的时候直接在这个位置取信息绘制,会简化渲染流程、提升渲染速度。这就是 VAO 概念产生的初衷。
VAO 的全名是 Vertex Array Object,首先,它不是 Buffer-Object,所以不用作存储数据;其次,它针对“顶点”而言,也就是说它跟“顶点的绘制”息息相关。VAO 记录的是一次绘制中所需要的信息,这包括“数据在哪里 glBindBuffer”、“数据的格式是怎么样的 glVertexAttribPointer”、shader-attribute 的 location 的启用 glEnableVertexAttribArray。
根据我查到的资料,几乎所有的显卡都支持 VBO,但不是所有的显卡都支持 VAO,而 VAO 仅仅是优化了 VBO 的使用方法,对于加速并没有实质性的影响,因此本文只讨论 VBO 技术。
创建顶点缓冲区对象(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)
在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。
在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() 函数可以从顶点混合数组中分离顶点、颜色、法线和纹理。比如,对只包含顶点信息的顶点混合数组:
vbo_indices.bind()
glInterleavedArrays(GL_V3F, 0, None)
如果顶点混合数组包含了颜色和顶点信息:
vbo_indices.bind()
glInterleavedArrays(GL_C3F_V3F, 0, None)
glInterleavedArrays() 函数第一个参数总共有14个选项,分别是:
- 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)
使用glDrawElements() 等函数绘制前,需要先绑定顶点数据集和索引数据集,然后使用glInterleavedArrays() 分理出顶点、颜色、法线等数据。
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()
函数库
核心函数库主要可以分为以下几类函数
(1) 绘制基本的几何图元函数。如:glBegain().
(2) 矩阵操作、几何变换和投影变换的函数。如:矩阵入栈glPushMatrix(),还有矩阵的出栈、转载、相乘,此外还有几何变换函数glTranslate*(),投影变换函数glOrtho()和视口变换函数glViewport()等等。
(3) 颜色、光照和材质函数。
(4) 显示列表函数,主要有创建、结束、生成、删除和调用显示列表的函数glNewList()、glEndList()、glGenLists()、glDeleteLists()和glCallList()。
(5) 纹理映射函数,主要有一维和二维纹理函数,设置纹理参数、纹理环境和纹理坐标的函数glTexParameter*()、glTexEnv*()和glTetCoord*()等。
(6) 特殊效果函数。
(7) 选着和反馈函数。
(8) 曲线与曲面的绘制函数。
(9) 状态设置与查询函数。
(10) 光栅化、像素函数。
OpenGL实用库(The OpenGL Utility Library)(GLU)
包含有43个函数,函数名的前缀名为glu.
(1) 辅助纹理贴图函数。
(2) 坐标转换和投影变换函数。
(3) 多边形镶嵌工具。
(4) 二次曲面绘制工具。
(5) 非均匀有理B样条绘制工具。
(6) 错误反馈工具,获取出错信息的字符串gluErrorString()
OpenGL辅助库
包含有31个函数,函数名前缀名为aux
这部分函数提供窗口管理、输入输出处理以及绘制一些简单的三维物体。
OpenGL工具库(OpenGL Utility Toolkit)
包含大约30多个函数,函数前缀名为glut,此函数由glut.dll来负责解释执行。
(1) 窗口操作函数。窗口初始化、窗口大小、窗口位置等函数glutInit() glutInitDisplayMode()、glutInitWindowSize() glutInitWindowPosition()等。
(2) 回调函数。响应刷新消息、键盘消息、鼠标消息、定时器函数等,GlutDisplayFunc()、glutPostRedisplay()、 glutReshapeFunc()、glutTimerFunc()、glutKeyboardFunc()、 glutMouseFunc()。
(3) 创建复杂的三维物体。这些和aux库函数功能相同。如创建球体glutWireSphere().
(4) 函数菜单
(5) 程序运行函数 glutAttachMenu()
16个WGL函数,专门用于OpenGL和Windows窗口系统的联接,其前缀名为wgl
(1) 绘制上下文函数。 wglCreateContext()、wglDeleteContext()、wglGetCurrentContent()、wglGetCurrentDC() wglDeleteContent()等。
(2) 文字和文本处理函数。wglUseFontBitmaps()、wglUseFontOutlines()。
(3) 覆盖层、地层和主平面处理函数。wglCopyContext()、wglCreateLayerPlane()、 wglDescribeLayerPlane()、wglReakizeLayerPlatte()等。
(4) 其他函数。wglShareLists()、wglGetProcAddress()等。
OpenGL应用函数库
gluBeginCurve,gluEndCurve 定义一条不一至的有理的NURBS曲线
gluBeginPolygon,gluEndPolygon 定义一个非凸多边形
gluBeginSurface,gluEndSurface 定义一个NURBS曲线
gluBeginTrim,gluEndTrim 定义一个NURBS整理循环
gluBuild1Dmipmaps 建立一维多重映射
gluBuild2Dmipmaps 建立二维多重映射
gluCylinder 绘制一个圆柱
gluDeleteNurbsRenderer 删除一个NURBS对象
gluDeleQuadric 删除一个二次曲面对象
gluDeleteTess 删除一个镶嵌对象
gluDisk 绘制一个盘子
gluErrorString 根据OpenGL或GLU错误代码产生错误字符串
gluGetNutbsProperty 得到一个NURBS属性
gluGetString 得到一个描述GLU版本号或支持GLU扩展调用的字符串
gluGetTessProperty 得到一个镶嵌对象
gluLoadSamplingMatrices 加载NUMRBS例子和精选矩阵
gluLookAt 设定一个变换视点
gluNewNurbsRenderer 创建一个NURBS对象
gluNewQuadric 建立一个二次曲面对象
gluNewTess 建立一个镶嵌对象
gluNextContour 为其他轮廓的开始做标记
gluNurbsCallback 为NURBS对象设定一个回调
gluNnrbsCurve 设定一个NuRBS曲线的形状
gluNurbsProperty 设定一个NURBS属性
gluNurbsSurface 定义一个NURBS表面的形状
gluOrtho2D 定义一个二位正交投影矩阵
gluPartialDisk 绘制一个盘子的弧
gluPerspective 设置一个透视投影矩阵
gluPickMatrix 定义一个拾取区间
gluProject 将对象坐标映射为窗口坐标
gluPwlCurve 描述一个分段线性NURBS修剪曲线
gluQuadricCallback 为二次曲面对象定义一个回调
gluQuadricDrawStyle 为二次曲面设定合适的绘制风格
gluQuadricNormals 定义二次曲面所用的法向的种类
gluQuadricOrientation 定义二次曲面内部或外部方向
gluQuadricTexture 定义是否带二次其面做纹理帖图
gluScaleImage 将图象变换为任意尺寸
gluSphere 绘制一个球体
gluTessBeginContour,gluTessEndContour 划定一个边界描述
gluTessBeginPolygon,gluTessEndPolygon 划定一个多边形描述
gluTessCallback 为镶嵌对象定义一个回调
gluTessNormal 为一个多边行形定义法向
gluTessProperty 设置镶嵌对象的属性
gluTessVertex 定义在一个多边形上的顶点
gluUnProject 将窗口坐标映射为对象坐标
OpenGL核心函数库
glAccum 操作累加缓冲区
glAddSwapHintRectWIN 定义一组被 SwapBuffers 拷贝的三角形 glAlphaFunc 允许设置 alpha 检测功能
glAreTexturesResident 决定特定的纹理对象是否常驻在纹理内存中
glArrayElement 定义一个被用于顶点渲染的数组成分 glBegin,glEnd 定义一个或一组原始的顶点
glBindTexture 允许建立一个绑定到目标纹理的有名称的纹理 glBitmap 绘制一个位图
glBlendFunc 特殊的像素算法
glCallList 执行一个显示列表
glCallLists 执行一列显示列表
glClear 用当前值清除缓冲区
GlClearAccum 为累加缓冲区指定用于清除的值 glClearColor 为色彩缓冲区指定用于清除的值 glClearDepth 为深度缓冲区指定用于清除的值 glClearStencil 为模板缓冲区指定用于清除的值 glClipPlane 定义被裁剪的一个平面几何体 glColor 设置当前色彩
glColorMask 允许或不允许写色彩组件帧缓冲区
glColorMaterial 使一个材质色彩指向当前的色彩
glColorPointer 定义一列色彩
glColorTableEXT 定义目的一个调色板纹理的调色板的格式和尺寸 glColorSubTableEXT 定义目的纹理的调色板的一部分被替换 glCopyPixels 拷贝帧缓冲区里的像素
glCopyTexImage1D 将像素从帧缓冲区拷贝到一个单空间纹理图象中 glCopyTexImage2D 将像素从帧缓冲区拷贝到一个双空间纹理图象中 glCopyTexSubImage1D 从帧缓冲区拷贝一个单空间纹理的子图象 glCopyTexSubImage2D 从帧缓冲区拷贝一个双空间纹理的子图象 glCullFace 定义前面或后面是否能被精选
glDeleteLists 删除相邻一组显示列表
glDeleteTextures 删除命名的纹理
glDepthFunc 定义用于深度缓冲区对照的数据
glDepthMask 允许或不允许写入深度缓冲区
glDepthRange 定义 z 值从标准的设备坐标映射到窗口坐标 glDrawArrays 定义渲染多个图元glDrawBuffer 定义选择哪个色彩缓冲区被绘制 glDrawElements 渲染数组数据中的图元
glDrawPixels 将一组像素写入帧缓冲区
glEdgeFlag 定义一个边缘标志数组
glEdgeFlagPointer 定义一个边缘标志数组
glEnable, glDisable 打开或关闭 OpenGL 的特殊功能 glEnableClientState,glDisableClientState 分别打开或关闭数组 glEvalCoord 求解一维和二维贴图
glEvalMesh1,glEvalMesh2 求解一维和二维点或线的网格 glEvalPoint1,glEvalPoint2 生成及求解一个网格中的单点 glFeedbackBuffer 控制反馈模式
glFinish 等待直到 OpenGL 执行结束
glFlush 在有限的时间里强制 OpenGL 的执行
glFogf,glFogi,glFogfv,glFogiv 定义雾参数
glFrontFace 定义多边形的前面和背面
glFrustum 当前矩阵乘上透视矩阵
glGenLists 生成一组空的连续的显示列表
glGenTextures 生成纹理名称
glGetBooleanv,glGetDoublev,glGetFloatv,glGetIntegerv 返回值或所选参数值 glGetClipPlane 返回特定裁减面的系数
glGetColorTableEXT 从当前目标纹理调色板得到颜色表数据 glGetColorTableParameterfvEXT,glGetColorTableParameterivEXT 从颜色表中 得到调色板参数
glGetError 返回错误消息
glGetLightfv,glGetLightiv 返回光源参数值 glGetMapdv,glGetMapfv,glGetMapiv 返回求值程序参数
glGetMaterialfv,glGetMaterialiv 返回材质参数 glGetPixelMapfv,glGetpixelMapuiv,glGetpixelMapusv 返回特定的像素图 glGetPointerv 返回顶点数据数组的地址
glGetPolygonStipple 返回多边形的点图案
glGetString 返回描述当前 OpenGl 连接的字符串
glGetTexEnvfv 返回纹理环境参数
glGetTexGendv,glGetTexGenfv,glGetTexGeniv 返回纹理坐标生成参数 glGetTexImage 返回一个纹理图象 glGetTexLevelParameterfv,glGetTexLevelParameteriv 返回特定的纹理参数的 细节级别
glGetTexParameterfv,glGetTexParameteriv 返回纹理参数值
glHint 定义实现特殊的线索
glIndex 建立当前的色彩索引
glIndexMask 控制写色彩索引缓冲区里的单独位
GlIndexPointer 定义一个颜色索引数组
glInitName 初始化名字堆栈
glInterleavedArrays 同时定义和允许几个在一个大的数组集合里的交替数组 glIsEnabled 定义性能是否被允许
glIsList 检测显示列表的存在
glIsTexture 确定一个名字对应一个纹理
glLightf,glLighti,glLightfv,glLightiv 设置光源参数 glLightModelf,glLightModeli,glLightModelfv,glLightModeliv 设置光线模型参数 glLineStipple 设定线点绘图案
glLineWidth 设定光栅线段的宽
glListBase 为 glcallList 设定显示列表的基础
glLoadIdentity 用恒等矩阵替换当前矩阵
glLoadMatrixd,glLoadMatrif 用一个任意矩阵替换当前矩阵
glLoadName 将一个名字调入名字堆栈
glLogicOp 为色彩索引渲染定义一个逻辑像素操作
glMap1d,glMap1f 定义一个一维求值程序
glMap2d,glMap2f 定义一个二维求值程序glMapGrid1d,glMapGrid1f,glMapgrid2d,glMapGrid2f 定义一个一维或二维网 格
glMaterialf,glMateriali,glMateriafv,glMaterialiv 为光照模型定义材质参数 glMatrixMode 定义哪一个矩阵是当前矩阵
glMultMatrixd,glMultMatrixf 用当前矩阵与任意矩阵相乘
glNewList,glEndList 创建或替换一个显示列表
glNormal 设定当前顶点法向
glNormalPointer 设定一个法向数组
glOrtho 用垂直矩阵与当前矩阵相乘
glPassThrough 在反馈缓冲区做记号 glPixelMapfv,glPixelMapuiv,glPixelMapusv 设定像素交换图 glPixelStoref,glpixelStorei 设定像素存储模式
glPixelTransferf,glPixelTransferi 设定像素存储模式
glPixelZoom 设定像素缩放因数
glPointSize 设定光栅点的直径
glPolygonMode 选择一个多边形的光栅模式
glPolygonOffset 设定 OpenGL 用于计算深度值的比例和单元
glPolygonStipple 设定多边形填充图案
glPrioritizeTextures 设定纹理固定的优先级
glPushAttrib,glPopAttrib 属性堆栈的压入和弹出操作 glPushClientAttrib,glPopClientAttrib 在客户属性堆栈存储和恢复客户状态值 glPushmatrix,glPopMatrix 矩阵堆栈的压入和弹出操作 glPushName,glPopName 名字堆栈的压入和弹出操作
glRasterPos 定义像素操作的光栅位置
glreadBuffer 为像素选择一个源色彩缓冲区
glReadPixels 从帧缓冲区读取一组数据 glRectd,glRectf,glRecti,glRects,glRectdv,glRectfv,glRectiv,glRectsv 绘制一个三 角形
glRenderMode 定义光栅模式
glRotated,glRotatef 将旋转矩阵与当前矩阵相乘
glScaled,glScalef 将一般的比例矩阵与当前矩阵相乘
glScissor 定义裁减框
glSelectBuffer 为选择模式值建立一个缓冲区
glShadeModel 选择平直或平滑着色
glStencilFunc 为模板测试设置功能和参照值
glStencilMask 控制在模板面写单独的位
glStencilOp 设置激活模式测试
glTexCoord 设置当前纹理坐标
glTexCoordPointer 定义一个纹理坐标数组 glTexEnvf,glTexEnvi,glTexEnvfv,glTexEnviv 设定纹理坐标环境参数 glTexGend,glTexgenf,glTexGendv,glTexGenfv,glTexGeniv 控制纹理坐标的生成 glTexImage1D 定义一个一维的纹理图象
glTexImage2D 定义一个二维的纹理图 glTexParameterf,glTexParameteri,glTexParameterfv,glTexParameteriv 设置纹理参数
glTexSubImage1D 定义一个存在的一维纹理图像的一部分,但不能定义新的纹理glTexSubImage2D 定义一个存在的二维纹理图像的一部分,但不能定义新的纹理glTranslated,glTranslatef 将变换矩阵与当前矩阵相乘 glVertex 定义一个顶点
glVertexPointer 设定一个顶点数据数组
glViewport 设置视窗
参考:
https://blog.csdn.net/xufive/article/details/86565130