一、实现功能
1、网格显示
2、坐标轴显示
3、鼠标操作旋转、平移、缩放
4、点云显示
之前写了一篇基于QGLWidget实现,此版本的功能接口基本保持一致,方便对比
QGLWidget与QOpenGLWidget的区别:
1、QGLWidget中是直接调用opengl指令,个人觉得比较方便,逻辑清晰,可以了解opengl底层的接口;
2、QOpenGLWidget是调用QOpenGLFunctions中封装接口,从Qt5.4开始,Qt推荐使用QOpenGLWidget和QOpenGL类。
二、实现效果
三、实现方法
细节待补充
完整代码
PointCloudOpenGLWidget.h
#ifndef BASEOPENGLWIDGET_H
#define BASEOPENGLWIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QMouseEvent>
#include "opengllib_global.h"
class OPENGLLIBSHARED_EXPORT PointCloudOpenGLWidget: public QOpenGLWidget, public QOpenGLFunctions_3_3_Core
{
Q_OBJECT
public:
PointCloudOpenGLWidget(QWidget *parent = 0);
~PointCloudOpenGLWidget();
void updatePoints(const QVector<QVector3D> &points);
void loadCsvFile(const QString &path);
protected:
void initializeGL() override;
void paintGL() override;
void resizeGL(int width, int height) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void wheelEvent(QWheelEvent *event);
virtual unsigned int drawMeshline(float size, int count);
virtual void drawCooraxis(float length);
virtual unsigned int drawPointdata(std::vector<float> &pointVertexs);
protected:
QOpenGLShaderProgram m_shaderProgramMesh;
QOpenGLShaderProgram m_shaderProgramAxis;
QOpenGLShaderProgram m_shaderProgramPoint;
unsigned int m_VBO_MeshLine;
unsigned int m_VAO_MeshLine;
unsigned int m_VBO_Axis;
unsigned int m_VAO_Axis;
unsigned int m_VBO_Point;
unsigned int m_VAO_Point;
std::vector<float> m_pointData;
unsigned int m_pointCount;
unsigned int m_vertexCount;
float m_xRotate;
float m_zRotate;
float m_xTrans;
float m_yTrans;
float m_zoom;
QPoint lastPos;
};
PointCloudOpenGLWidget.cpp
#include "PointCloudOpenGLWidget.h"
#include <QDebug>
PointCloudOpenGLWidget::PointCloudOpenGLWidget(QWidget *parent)
: QOpenGLWidget(parent)
{
m_xRotate = -30.0;
m_zRotate = 100.0;
m_xTrans = 0.0;
m_yTrans = 0.0;
m_zoom = 45.0;
}
PointCloudOpenGLWidget::~PointCloudOpenGLWidget()
{
makeCurrent();
glDeleteBuffers(1, &m_VBO_MeshLine);
glDeleteVertexArrays(1, &m_VAO_MeshLine);
glDeleteBuffers(1, &m_VBO_Axis);
glDeleteVertexArrays(1, &m_VAO_Axis);
glDeleteBuffers(1, &m_VBO_Point);
glDeleteVertexArrays(1, &m_VAO_Point);
m_shaderProgramMesh.release();
m_shaderProgramAxis.release();
m_shaderProgramPoint.release();
doneCurrent();
qDebug() << __FUNCTION__;
}
void PointCloudOpenGLWidget::updatePoints(const QVector<QVector3D> &points)
{
m_pointData.clear();
for(auto vector3D : points)
{
m_pointData.push_back(vector3D.x());
m_pointData.push_back(vector3D.y());
m_pointData.push_back(vector3D.z());
m_pointData.push_back(1);
}
}
void PointCloudOpenGLWidget::loadCsvFile(const QString &path)
{
m_pointData.clear();
QFile inFile(path);
if (inFile.open(QIODevice::ReadOnly))
{
QTextStream stream_text(&inFile);
while (!stream_text.atEnd())
{
QString line = stream_text.readLine();
QStringList strSplit = line.split(",");
double x = strSplit.value(0).toDouble();
double y = strSplit.value(1).toDouble();
double z = strSplit.value(2).toDouble();
m_pointData.push_back(x);
m_pointData.push_back(y);
m_pointData.push_back(z);
m_pointData.push_back(1);
}
inFile.close();
}
}
void PointCloudOpenGLWidget::initializeGL()
{
initializeOpenGLFunctions();
// enable depth_test
glEnable(GL_DEPTH_TEST);
// link meshline shaders vs文件为顶点着色器 fs为片段着色器
m_shaderProgramMesh.addShaderFromSourceFile(QOpenGLShader::Vertex,
":/opengl/shader/shader_mesh.vs");
m_shaderProgramMesh.addShaderFromSourceFile(QOpenGLShader::Fragment,
":/opengl/shader/shader_mesh.fs");
m_shaderProgramMesh.link();
// link coordinate axis shaders
m_shaderProgramAxis.addShaderFromSourceFile(QOpenGLShader::Vertex,
":/opengl/shader/shader_axis.vs");
m_shaderProgramAxis.addShaderFromSourceFile(QOpenGLShader::Fragment,
":/opengl/shader/shader_axis.fs");
m_shaderProgramAxis.link();
// link pointcloud shaders
m_shaderProgramPoint.addShaderFromSourceFile(QOpenGLShader::Vertex,
":/opengl/shader/shader_point.vs");
m_shaderProgramPoint.addShaderFromSourceFile(QOpenGLShader::Fragment,
":/opengl/shader/shader_point.fs");
m_shaderProgramPoint.link();
m_vertexCount = drawMeshline(2.0, 16);
m_pointCount = drawPointdata(m_pointData);
qDebug() << "point_count" << m_pointCount;
drawCooraxis(4.0);
}
void PointCloudOpenGLWidget::paintGL()
{
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/*
为了将坐标从一个坐标系转换到另一个坐标系,需要用到几个转换矩阵,
分别是模型(Model)、视图(View)、投影(Projection)三个矩阵。
*/
QMatrix4x4 projection, view, model;
//透视矩阵变换
projection.perspective(m_zoom, (float)width() / (float)height(), 1.0f, 100.0f);
// eye:摄像机位置 center:摄像机看的点位 up:摄像机上方的朝向
view.lookAt(QVector3D(0.0, 0.0, 50.0), QVector3D(0.0, 0.0, 1.0), QVector3D(0.0, 1.0, 0.0));
model.translate(m_xTrans, m_yTrans, 0.0);
model.rotate(m_xRotate, 1.0, 0.0, 0.0);
model.rotate(m_zRotate, 0.0, 0.0, 1.0);
m_shaderProgramMesh.bind();
m_shaderProgramMesh.setUniformValue("projection", projection);
m_shaderProgramMesh.setUniformValue("view", view);
m_shaderProgramMesh.setUniformValue("model", model);
m_shaderProgramAxis.bind();
m_shaderProgramAxis.setUniformValue("projection", projection);
m_shaderProgramAxis.setUniformValue("view", view);
m_shaderProgramAxis.setUniformValue("model", model);
m_shaderProgramPoint.bind();
m_shaderProgramPoint.setUniformValue("projection", projection);
m_shaderProgramPoint.setUniformValue("view", view);
m_shaderProgramPoint.setUniformValue("model", model);
//画网格
m_shaderProgramMesh.bind();
glBindVertexArray(m_VAO_MeshLine);
glLineWidth(1.0f);
glDrawArrays(GL_LINES, 0, m_vertexCount);
//画坐标轴
m_shaderProgramAxis.bind();
glBindVertexArray(m_VAO_Axis);
glLineWidth(5.0f);
glDrawArrays(GL_LINES, 0, 6);
//画点云
m_shaderProgramPoint.bind();
glBindVertexArray(m_VAO_Point);
glPointSize(1.0f);
glDrawArrays(GL_POINTS, 0, m_pointCount);
}
void PointCloudOpenGLWidget::resizeGL(int width, int height)
{
glViewport(0, 0, width, height);
}
void PointCloudOpenGLWidget::mousePressEvent(QMouseEvent *event)
{
lastPos = event->pos();
}
void PointCloudOpenGLWidget::mouseMoveEvent(QMouseEvent *event)
{
int dx = event->pos().x() - lastPos.x();
int dy = event->pos().y() - lastPos.y();
if (event->buttons() & Qt::LeftButton)
{
m_xRotate = m_xRotate + 0.3 * dy;
m_zRotate = m_zRotate + 0.3 * dx;
if (m_xRotate > 30.0f)
{
m_xRotate = 30.0f;
}
if (m_xRotate < -120.0f)
{
m_xRotate = -120.0f;
}
update();
}
else if (event->buttons() & Qt::MidButton)
{
m_xTrans = m_xTrans + 0.1 * dx;
m_yTrans = m_yTrans - 0.1 * dy;
update();
}
lastPos = event->pos();
}
void PointCloudOpenGLWidget::wheelEvent(QWheelEvent *event)
{
auto scroll_offest = event->angleDelta().y() / 120;
m_zoom = m_zoom - (float)scroll_offest;
if (m_zoom < 1.0f) /* 放大限制 */
{
m_zoom = 1.0f;
}
if (m_zoom > 80.0f)
{
m_zoom = 80.0f;
}
update();
}
unsigned int PointCloudOpenGLWidget::drawMeshline(float size, int count)
{
std::vector<float> mesh_vertexs;
unsigned int vertex_count = 0;
float start = count * (size / 2);
float posX = start, posZ = start;
for (int i = 0; i <= count; ++i)
{
mesh_vertexs.push_back(posX);
mesh_vertexs.push_back(start);
mesh_vertexs.push_back(0);
mesh_vertexs.push_back(posX);
mesh_vertexs.push_back(-start);
mesh_vertexs.push_back(0);
mesh_vertexs.push_back(start);
mesh_vertexs.push_back(posZ);
mesh_vertexs.push_back(0);
mesh_vertexs.push_back(-start);
mesh_vertexs.push_back(posZ);
mesh_vertexs.push_back(0);
posX = posX - size;
posZ = posZ - size;
}
glGenVertexArrays(1, &m_VAO_MeshLine);
glGenBuffers(1, &m_VBO_MeshLine);
glBindVertexArray(m_VAO_MeshLine);
glBindBuffer(GL_ARRAY_BUFFER, m_VBO_MeshLine);
glBufferData(GL_ARRAY_BUFFER, mesh_vertexs.size() * sizeof(float), &mesh_vertexs[0], GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
vertex_count = (int)mesh_vertexs.size() / 3;
return vertex_count;
}
void PointCloudOpenGLWidget::drawCooraxis(float length)
{
std::vector<float> axis_vertexs =
{
//x,y ,z ,r, g, b
0.0, 0.0, 0.0, 1.0, 0.0, 0.0,
length, 0.0, 0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 1.0, 0.0,
0.0, length, 0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 1.0,
0.0, 0.0, length, 0.0, 0.0, 1.0,
};
glGenVertexArrays(1, &m_VAO_Axis);
glGenBuffers(1, &m_VBO_Axis);
glBindVertexArray(m_VAO_Axis);
glBindBuffer(GL_ARRAY_BUFFER, m_VBO_Axis);
glBufferData(GL_ARRAY_BUFFER, axis_vertexs.size() * sizeof(float), &axis_vertexs[0], GL_STATIC_DRAW);
// 位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0);
// 颜色属性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void *)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
unsigned int PointCloudOpenGLWidget::drawPointdata(std::vector<float> &pointVertexs)
{
unsigned int point_count = 0;
glGenVertexArrays(1, &m_VAO_Point);
glGenBuffers(1, &m_VBO_Point);
glBindVertexArray(m_VAO_Point);
glBindBuffer(GL_ARRAY_BUFFER, m_VBO_Point);
glBufferData(GL_ARRAY_BUFFER, pointVertexs.size() * sizeof(float), &pointVertexs[0], GL_STATIC_DRAW);
// 位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0);
// 颜色属性
glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
point_count = (unsigned int)pointVertexs.size() / 4;
return point_count;
}