Qt中使用OpenGL(一)
Qt 中的 OpenGL
Qt 中对 OpenGL 进行了封装,一般来说,是需要继承 QOpenGLWidget 和 QOpenGLFunctions。大致上,用于显示 OpenGL 的窗口应该是一个类,这个类包括以下特性:
- 继承
QOpenGLWidget和QOpenGLFunctions - 重载
void initializeGL(),void paintGL()和void resizeGL(int w, int h)这三个函数 - 提供函数用于设置相机的位置与选择
- 提供函数用于设置需要显示内容的具体数据
- 提供函数用于设置显示内容的缩放、旋转、位移等
当显示复杂的内容时,则需要编写额外的逻辑,如模型管理等。
initializeGL
initializeGL 函数负责对 OpenGL 进行一定的初始化:
初始化函数指针
initializeOpenGLFunctions()启用一些 flag
例如
glEnable(GL_DEPTH_TEST),开启深度测试。深度测试可以在绘制三维图形的时候,产生近处物体遮挡远处物体的效果创建缓存
OpenGL 使用的时候需要一些缓存对象,如顶点数组对象(VAO),顶点缓存对象(VBO),在进行绘制的时候,数据来源于这些缓存对象
初始化 shader 程序并链接
Qt 提供了一个类
QOpenGLShaderProgram,通过这个类的addShaderFromSourceCode或者addShaderFromSourceFile方法加载 shader,然后使用 link 函数链接加载数据
如果要显示的数据是静态数据,如静态模型,就可以在初始化的时候加载数据。如果是动态数据,就要实时加载
paintGL
在 paintGL 函数中进行主要的三维绘制逻辑,主要进行如下操作:
清除之前绘制的内容,包括颜色与深度信息
1
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);启用 shader
1
m_shaderProgram->bind();设置 shader 中的全局数据,例如模型矩阵、视图矩阵、投影矩阵等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 设置视图矩阵
m_viewMatrix.setToIdentity();
QVector3D _lookDir{0, 0, 1};
QVector3D _up{0, -1, 0};
_lookDir = _lookDir * m_cameraRotate;
_up = _up * m_cameraRotate;
m_viewMatrix.lookAt(m_cameraPos, m_cameraPos + _lookDIr, _up);
m_shaderProgram->setUniformValue("view", m_viewMatrix);
// 设置投影矩阵
m_shaderProgram->setUniformValue("projection", m_projectionMatrix);
// 设置模型矩阵
m_modelMatrix.setToIdentity();
m_modelMatrix *= m_scale;
m_modelMatrix *= m_rotate;
m_modelMatrix *= m_translate;
m_shaderProgram->setUniformValu("model", m_modelMatrix);启用顶点数组对象(VAO)
1
m_VAO.bind();启用并设置顶点缓存对象(VBO)
1
2
3
4// 绑定 VBO
m_VBO.bind();
m_VBO.setUsagePattern(QOpenGLBuffer::StaticDraw);
m_VBO.allocate(m_vertices, m_verticesCount * 3 * sizeof(float));将 shader 与顶点缓存进行对应,告诉 OpenGL 如何将顶点缓存中的数据转化为 shader 中的顶点数据
1
2m_shaderProgram->setAttributeBuffer(0, GL_FLOAT, 0, 3, 0);
m_shaderProgram->enableAttributeArray(0);调用绘制函数,绘制顶点缓存中的内容
1
2// 绘制
glDrawArrays(GL_POINTS, 0, m_verticesCount);释放顶点数组对象
1
m_VAO.release();释放 shader
1
m_shaderProgram.release();
其中,第五、六步是动态加载数据的步骤,如果数据是静态的,则应该在初始化执行操作。初始化的时候也应该先启用 shader 和 VAO ,并在最后释放 shader 和 VAO。
总结
初始阶段
- 初始化 OpenGL 函数
- 初始化各种 flag
- 创建各种缓存对象
- 创建并链接 shader
- 启用 shader 和 缓存
- 绑定缓存 (静态数据)
- 绑定 shader 的缓存数据 (告诉 OpenGL 如何从缓存中读取到 shader 中)
- 释放缓存
- 释放shader
绘制阶段
- 清空屏幕
- 启用 shader
- 绑定 shader 的全局数据(各种矩阵)
- 启用缓存
- 绑定缓存(加载动态数据)
- 绑定 shader 的缓存数据(告诉 OpenGL 如何从缓存中读取到 shader)
- 绘制(使用 shader 读取缓存中的数据,转换为顶点数据,按照命令基于顶点绘制单位数据)
- 释放缓存
- 释放 shader
可以存在多个缓存,每个缓存可以保存不同的内容,根据需要,可以在绘制阶段重复 启用缓存到释放缓存 之间的步骤以达到绘制不同模型的目的。