之前一段时间在研究Kinect的深度摄像头,发现了深度摄像头存在一个“点云”模式。
也就是可以将摄像头识别到的深度信息在3D空间以一个一个点的形式给表现出来。
于是想要自己试着渲染一个点云出来。
既然是在3D空间,那么显然需要使用3D相关的技术了。
在Qt中……那就用Qt提供的OpenGL吧。
恰好之前(挺早之前了)我也算是接触过OpenGL的,然而当我查找了一下相关的文档才发现……新的OpenGL已经和我之前了解的OpenGL是两个东西了……
详见LearnOpenGL CN:https://learnopengl-cn.github.io/
新的OpenGL竟然在最简单的例子中要求写shader?
好家伙,直接给我整不会了。
不过没办法,既然需要写shader那就写吧……
于是最终的成品就是如下所示:
距离摄像机越近,点的颜色越红,越远越蓝,适中就是绿色。
要怎么说呢,Qt中对OpenGL的使用是进行了很多层的包装的,我尽可能的挑选了Qt原生的对OpenGL的使用方法,也就是继承QOpenGLWidget
和QOpenGLExtraFunctions
的方案:
大致上,你的用于显示OpenGL渲染结果的窗口,应该是这么一个类,即拥有以下特性:
QOpenGLWidget
和QOpenGLExtraFunctions
void initializeGL()
,void paintGL()
还有void resizeGL(int w, int h)
三个函数注意:当你需要显示复杂的内容时,就需要额外的编写逻辑,例如模型管理,贴图管理等等。本例子中因为只显示一个点云数据,所以并没有什么复杂的资源管理逻辑,只需要一个顶点缓存用来保存点云数据,然后在有一些矩阵数据保存各种变化矩阵即可。
在void initializeGL()
函数中,我们需要对OpenGL进行一定的初始化。
具体来说,就是:
initializeOpenGLFunctions()
只要调用就可以了。如果不调用这个函数,则我们将无法使用OpenGL提供的函数。glEnable(GL_DEPTH_TEST)
启用深度测试等。深度测试可以让我们在绘制3D图形的时候,总是看到“近处的物体可以挡住远处的物体”这个效果。QOpenGLShaderProgram
,我们用它就可以了。addShaderFromSourceCode
或addShaderFromSourceFile
加载你的shader,最后用link
函数连接即可。在void paintGL()
函数中,我们就可以进行主要的3D绘制逻辑了。
我们主要需要进行以下行为:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
m_program->bind();
// 设置视图矩阵
m_view.setToIdentity();
QVector3D _lookDir{ 0, 0, 1 };
QVector3D _up{ 0, -1, 0 };
_lookDir = _lookDir * m_cameraRotate;
_up = _up * m_cameraRotate;
m_view.lookAt(m_cameraPos, m_cameraPos + _lookDir, _up);
m_program->setUniformValue("view", m_view);
// 设置投影矩阵
m_program->setUniformValue("projection", m_projection);
// 针对本模型, 设置模型矩阵
m_model.setToIdentity();
m_model *= m_scale;
m_model *= m_rotate;
m_model *= m_translate;
m_program->setUniformValue("model", m_model);
m_vao.bind();
// 绑定本模型需要的顶点缓冲
m_vbo.bind();
m_vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
m_vbo.allocate(m_vertices, m_vertecesCount * 3 * sizeof(float));
// 针对本模型, 设置如何渲染
m_program->setAttributeBuffer(0, GL_FLOAT, 0, 3, 0);
m_program->enableAttributeArray(0);
// 绘制模型
glDrawArrays(GL_POINTS, 0, m_vertecesCount);
m_vao.release();
m_program->release();
其中,第5步和第6步就是动态加载数据的步骤。如果你要显示的内容是静态的(例如一个静态模型),你实际上应该在初始化的时候进行这个操作。注意,初始化的时候也要先启用shader和VAO后在操作,并在最后释放VAO和shader。
说了这么多,实际上我们只是对整个OpenGL的使用流程做了一个最初步的介绍,如果你是第一次接触肯定会依旧感到莫名其妙。
但是不要紧,因为我们现在其实并不需要真的理解,面对复杂的内容,我个人的习惯是首先了解整个流程,然后再找到一个最小样的例来体验这个流程。
目前我们已经了解了整个流程,即初始化然后绘制。
简单来说,就是:
初始阶段:
绘制阶段:
根据我们的需求(数据变化与否),我们会在初始化阶段绑定缓存(数据不会变化)或者在绘制阶段绑定缓存(数据会变化)。
注意:可以存在多个缓存。每个缓存可以保存不同的内容(例如不同的模型),根据需要,我们可以在绘制阶段重复4,5,6,7,8步骤以达到绘制不同模型的目的。
下一篇:
在Qt中使用OpenGL(二)