Qt中使用OpenGL(二)
前言
本篇从最简单的实现来把在 Qt 中使用 OpenGL 的流程走一遍
简单流程
新建类
首先创建一个自己的窗口
1 | |
protected 中的三个函数继承自 QOpenGLWidget,必须要进行重载。
在 initializeGL() 中做一些基本的初始化
首先要做的就是初始化 OpenGL 函数,OpenGL 本身的 API 只提供了函数定义,所有的实现实际上是操作系统或者其他库的工作,初始化 OpenGL 函数的目的,就是加载这些 OpenGL 的实现。这个操作可以通过 initializeOpenGLFunctions() 这个函数实现。
其次要做的就是设置一些 OpenGL 的特性,比如深度测试。深度测试是指 “近处的物体会遮挡远处的物体” 这种在现实中最为基础的法则。可以通过 glEnable(GL_DEPTH_TEST) 这个函数实现。
在最后,还可以设置一下刷新时的背景颜色,可以通过 glClearColor(0.2f, 0.3f, 0.3f, 1.0f) 来实现,四个参数分别是 R,G,B,A 的值,取值范围 [0, 1]。
1 | |
在 OpenGL 中,颜色值的范围,RGBA 每个通道都是从 0 开始,1 结束,如果希望按照 [0, 255] 的范围来设置,可以用 (X / 255.0) 。
创建缓存
在绘制三维图像时,需要的输入是顶点,即用(x, y, z)表示的三维的点,三个点就能确定一个三角形。在 OpenGL 中,如果想传入顶点,就需要将顶点中的 x, y, z 每个值,一个一个的放到缓存中。
OpenGL 中存在两个概念:VAO 和 VBO
VAO 是 Vertex Array Object,顶点数组对象,VBO 是 Vertex Buffer Object,顶点缓存对象。
很显然,我们需要把顶点放到缓存中,也就是 VBO 中。 VAO 可以帮助我们在绘制多个三维对象时,将各自的绘制状态进行隔离,即每个绘制对象都可以有自己的顶点缓存,shader,以及它的各种状态,VAO 可以把这些状态保存起来,下一次执行的时候,就不需要重复设置了,也就是:一次设置,到处使用。
VAO 对应的 Qt 中的 QOpenGLVertexArrayObject 类, VBO 对应 Qt 中的 QOpenGLBuffer 类。
操作方法:创建两个对象,调用各自的创建函数。
1 | |
1 | |
VAO 和 VBO 是相互对应的,如果希望创建第二个 VBO 用于绘制第二个物体,那也要再创建一个 VAO。
shader
如果把顶点数据通过缓存给了 OpenGL,OpenGL 需要通过 shader 来使用这些顶点数据,包括对坐标进行缩放、平移、旋转,给顶点染色等等。
在 Qt 中提供了 QOpenGLShaderProgram 类帮助用户使用 shader。
shader 目前有两个类型,一个类型是 vertex,用于处理顶点; 一个类型是 fragment,用于处理颜色。
1 | |
处理顶点的时候需要定义输入的顶点,处理颜色的时候可以什么都不用定义
1 | |
这是vertex shader,其中 in 表示 vPos 这个变量是输入的变量, 类型为 vec3 即三维向量。 gl_Position 表示最终输出的位置,是内置的,不需要我们定义,它是一个四维向量,即 X,Y,Z,W。
这个 shader 的含义就是将输入的三维向量最后加一个 1.0 的值变成四维向量,然后作为最终的输出位置。
1 | |
gl_FragColor 表示最终输出的颜色,是内置的,不需要我们定义,它是一个四维向量,即R,G,B,A。
这个 shader 的含义就是最终输出的颜色是 (1.0, 1.0, 1.0, 1.0),即白色.
最终,链接一下这个 shader
1 | |
使用缓存与 shader
准备好了缓存与 shader,下一步就是使用它们了。
OpenGL 想要画出一个三维对象,就必须要有顶点的输入。OpenGL 通过顶点缓存这个概念让用户可以输入顶点信息。所谓的顶点缓存,简单来说就是一个一维数组。
1 | |
这个数组存储了 9 个 float 值,可以将其看作三个点,按照 x, y, z, x, y, z, x, y, z 这个顺序排列。使用 VBO 告诉 OpenGL 如何使用这 9 个值,3 个点。
首先使用 VAO 和 VBO 的 bind()函数告诉 OpenGL 我们要使用缓存了。
1 | |
然后将自己定义的 9 个值,3 个点绑定到 VBO 上,通过 allocate() 函数
1 | |
这里和我们需要内存保存数据一样, VBO 也会创建一块和顶点信息相同大小的内存,顶点信息是 9 个 float,这里也就创建 9 个 float 大小的区域。为了方便也可以使用注释掉的方法。
为了让 OpenGL 能够知道缓存中的数据是怎么排列的,我们需要为 shader 绑定一些信息。此时使用 vPos 这个输入变量的名字来告诉 OpenGL,在顶点缓存中,是按照 vPos 这个变量的类型,即 vec3 的标准来保存顶点信息的。
1 | |
一般的,当顶点缓存只有顶点信息的时候,setAttributeBuffer() 这个函数我们只需要关心前四个参数即可。即 shader 中的输入参数名称,顶点缓存中的数据类型, 顶点缓存中开始的位置,顶点缓存中一个点需要用几个数据表示,第五个参数一般取 0 。
至此,所有的准备工作都完成了。
然后做做一个清理工作,释放 VAO 和 shader。
1 | |
开始绘制吧
绘制部分非常简单,几行代码就可以。
1 | |
代码分别是启用 VAO,启用 shader,用三个点话一个三角形,释放 shader,释放 VAO。