QOpenGLWidget显示点云

纯Qt实现使用QOpenGLWidget显示点云,无需借助PCL、VTK、Open3d等第三方库。

myqopenglwidget.h

#ifndef MYQOPENGLWIDGET_H
#define MYQOPENGLWIDGET_H

#include <QtGui/QOpenGLFunctions>
#include <QtWidgets/QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
#include <QPainter>
#include <QOpenGLContext>
#include <QOpenGLPaintDevice>
#include <QOpenGLShaderProgram>
#include <QOpenGLShader>
#include <QTime>
#include <QTimer>
#include <QVector3D>
#include <QMouseEvent>
#include <QKeyEvent>

QT_BEGIN_NAMESPACE
class QPainter;
class QOpenGLContext;
class QOpenGLPaintDevice;
class QOpenGLShaderProgram;
class QOpenGLShader;
QT_END_NAMESPACE

struct PointXYZRGB
{
    float x;
    float y;
    float z;
    int r;
    int g;
    int b;
};

struct BoundingBox
{
    float width;
    float height;
    float depth;
    QVector3D maxPoint;
    QVector3D minPoint;
    QVector3D centerPoint;
};

class MyQOpenglWidget: public QOpenGLWidget, protected QOpenGLFunctions
{
    Q_OBJECT
public:
    explicit MyQOpenglWidget(QWidget *parent = 0);
    ~MyQOpenglWidget();
    void showPointCloud(const std::vector<PointXYZRGB>& cloud);
    virtual void resizeGL(int w, int h);
    void setBackgroundColor(QVector3D color);

protected:
    virtual void initializeGL(void);
    virtual void paintGL(void);

    struct VertexInfo
    {
        float pos[3];
        float normal[3];
        float color[4];
    };
private slots:
    void onTimerOut(void);
    virtual void mousePressEvent(QMouseEvent * e);
    virtual void mouseMoveEvent(QMouseEvent * e);
    virtual void mouseReleaseEvent(QMouseEvent * e);
    virtual void wheelEvent(QWheelEvent * e);
    virtual void keyPressEvent(QKeyEvent *e);
    virtual void leaveEvent(QEvent *);
    virtual void enterEvent(QEvent *);

private:
    QTimer *m_Timer;
    QOpenGLContext *m_context;					
    QOpenGLShaderProgram *m_Program;			
    QOpenGLFunctions_3_3_Core *OpenGLCore;
    QOpenGLShader *m_VertexShader;
    QOpenGLShader *m_FragmentShader;

	// shader spara
    GLuint m_posAttr;                           
    GLuint m_colAttr;                          
    GLuint m_norAttr;                           
    GLuint m_matrixUniform;						
	GLuint m_VBO;
    GLuint m_VAO;
    QVector4D m_backgroundColor;

	//store points
	QVector<VertexInfo> m_PointsVertex;				
    BoundingBox m_box;
   
    QVector3D m_lineMove;
    QQuaternion m_rotate;
	QVector3D m_rotationAxis;
    float m_scale;

    QVector2D m_lastPoint;

    GLuint createGPUProgram(QString nVertexShaderFile, QString nFragmentShaderFile);
    void GetShaderUniformPara();
    bool InitShader();                    

    void LineMove(QVector2D posOrgin, QVector2D posEnd);
    void Rotate(QVector2D posOrgin, QVector2D posEnd);
    void modelZoomInOrOut(bool ZoomInOrOut);
    QVector3D pixelPosToViewPos(const QVector2D& p);
    void calRotation(QVector2D posOrgin, QVector2D posEnd);
    void calculateBoundingBox(const std::vector<PointXYZRGB>& cloud);
    void initPointCloud(const std::vector<PointXYZRGB>& cloud);
    void gray2Pseudocolor(const PointXYZRGB pos, float color[4]);
    void changePointCloud();
    void setMatrixUniform();
    void ResetView();
    void initCloud();
};

#endif // MYQOPENGLWIDGET_H

myqopenglwidget.cpp

#include "myqopenglwidget.h"
#include <QtMath>

static const char *vertexShaderSource =
        "attribute highp vec3 posAttr;\n"
        "attribute lowp vec4 colAttr;\n"
        "varying lowp vec4 col;\n"
        "uniform highp mat4 matrix;\n"
        "void main() {\n"
        "  col=colAttr;\n"
        "  gl_Position=matrix * vec4(posAttr,1.0f);\n"
        "}\n";

static const char *fragmentShaderSource =   
        "varying lowp vec4 col;\n"
        "void main() {\n"
        "   gl_FragColor = col;\n"
        "}\n";

MyQOpenglWidget::MyQOpenglWidget(QWidget *parent)
    : QOpenGLWidget(parent)
    ,m_context(NULL)
    ,m_Program(NULL)
    ,OpenGLCore(NULL)
    ,m_posAttr(0)    
    ,m_colAttr(0)
    ,m_norAttr(0)
    ,m_matrixUniform(0)
    ,m_VBO(0)
    ,m_VAO(0)
    ,m_VertexShader(NULL)
    ,m_FragmentShader(NULL)
    ,m_scale(1.0f)
{
    m_Timer = new QTimer;
    m_PointsVertex = QVector<VertexInfo>();
    ResetView();
    this->grabKeyboard();
}

MyQOpenglWidget::~MyQOpenglWidget()
{
    if(m_context)
    {
        delete m_context;
        m_context = NULL;
    }
    if(m_Program)
    {
        delete m_Program;
        m_Program = NULL;
    }
    m_PointsVertex.clear();
}

void MyQOpenglWidget::showPointCloud(const std::vector<PointXYZRGB> &cloud)
{
    QTime startTime = QTime::currentTime();
    initPointCloud(cloud);
    startTime = QTime::currentTime();
    changePointCloud();
    ResetView();
    repaint();
}

void MyQOpenglWidget::calculateBoundingBox(const std::vector<PointXYZRGB> &cloud)
{
    float x_max=INT_MIN, y_max=INT_MIN, z_max=INT_MIN;
    float x_min=INT_MAX, y_min=INT_MAX, z_min=INT_MAX;
    for(int i = 0;i<cloud.size();i++)
    {
        if(cloud[i].x>x_max)    x_max=cloud[i].x;
        if(cloud[i].x<x_min)    x_min=cloud[i].x;
        if(cloud[i].y>y_max)    y_max=cloud[i].y;
        if(cloud[i].y<y_min)    y_min=cloud[i].y;
        if(cloud[i].z>z_max)    z_max=cloud[i].z;
        if(cloud[i].z<z_max)    z_min=cloud[i].z;
    }
    float width = x_max - x_min;
    float height = y_max - y_min;
    float depth = z_max - z_min;
    QVector3D maxPoint(x_max, y_max, z_max);
    QVector3D minPoint(x_min, y_min, z_min);
    QVector3D centerPoint((x_max+x_min)/2, (y_max+y_min)/2, (z_max+z_min)/2);
    m_box.width = width;
    m_box.height = height;
    m_box.depth = depth;
    m_box.maxPoint = maxPoint;
    m_box.minPoint = minPoint;
    m_box.centerPoint = centerPoint;
}

void MyQOpenglWidget::initPointCloud(const std::vector<PointXYZRGB> &cloud)
{
    m_PointsVertex.clear();
    m_PointsVertex.resize((int)cloud.size());
    
    calculateBoundingBox(cloud);
    
    for(int i = 0;i<cloud.size();i++)
    {
        //move cloud center to origin
        m_PointsVertex[i].pos[0] = cloud[i].x;
        m_PointsVertex[i].pos[1] = cloud[i].y;
        m_PointsVertex[i].pos[2] = cloud[i].z;
        gray2Pseudocolor(cloud[i], m_PointsVertex[i].color);
        m_PointsVertex[i].normal[0] = 0.0f;
        m_PointsVertex[i].normal[1] = 1.0f;
        m_PointsVertex[i].normal[2] = 0.0f;
    }
}

void MyQOpenglWidget::gray2Pseudocolor(const PointXYZRGB pos,float color[4])
{
    color[0] = pos.r *1.0f/255;
    color[1] = pos.g *1.0f/255;
    color[2] = pos.b *1.0f/255;
    color[3] = 1.0f;
}

void MyQOpenglWidget::changePointCloud()
{
    if(m_PointsVertex.size()<=0)
    {
        return ;
    }
    OpenGLCore->glBindVertexArray(m_VAO);
    glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(VertexInfo)*(int)m_PointsVertex.size(), m_PointsVertex.data(), GL_DYNAMIC_DRAW);
    
    glEnableVertexAttribArray(m_posAttr);
    glVertexAttribPointer(m_posAttr, 3, GL_FLOAT, GL_FALSE, sizeof(VertexInfo), (void*)(offsetof(VertexInfo, pos)));
    glEnableVertexAttribArray(m_colAttr);
    glVertexAttribPointer(m_colAttr, 4, GL_FLOAT, GL_FALSE, sizeof(VertexInfo), (void*)(offsetof(VertexInfo,color)));
    
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    OpenGLCore->glBindVertexArray(0);
}

void MyQOpenglWidget::ResetView()
{
    m_lineMove = QVector3D();
    m_rotate = QQuaternion(); 
    m_rotate *= QQuaternion::fromAxisAndAngle(QVector3D(0,0,1),180);
    m_rotationAxis = QVector3D();
    m_scale = 1.0f;
}

void MyQOpenglWidget::setBackgroundColor(QVector3D color)
{
    m_backgroundColor= QVector4D(color, 1.0f);
}

void MyQOpenglWidget::resizeGL(int w, int h)
{
    const qreal retinaScale = devicePixelRatio();
    glViewport(0, 0, w * retinaScale, h * retinaScale);
    repaint();
}

void MyQOpenglWidget::initializeGL()
{
    bool binit = true;
    binit &= InitShader();
    if (!binit)
    {
        return;
    }
    OpenGLCore = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_3_Core>();
    initializeOpenGLFunctions();
    glEnable(GL_DEPTH_TEST);
    OpenGLCore->glGenVertexArrays(1, &m_VAO);
    glGenBuffers(1, &m_VBO);
    
    //init need import a point data
    initCloud();
    changePointCloud();
    
    QObject::connect(m_Timer, SIGNAL(timeout()), this, SLOT(onTimerOut()));
    m_Timer->start(50);
}

bool MyQOpenglWidget::InitShader()
{
    m_Program = new QOpenGLShaderProgram(this);
    bool success = true;
    success &= m_Program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
    success &= m_Program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
    success &= m_Program->link();
    GetShaderUniformPara();
    return success;
}

void MyQOpenglWidget::GetShaderUniformPara()
{
    m_posAttr = m_Program->attributeLocation("posAttr");
    m_colAttr = m_Program->attributeLocation("colAttr");  
    m_matrixUniform = m_Program->uniformLocation("matrix");
}

void MyQOpenglWidget::paintGL()
{
    m_Program->bind();
    glClearColor(m_backgroundColor.x(), m_backgroundColor.y(), m_backgroundColor.z(), m_backgroundColor.w());
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BITS);
    
    OpenGLCore->glBindVertexArray(m_VAO);
    setMatrixUniform();
    glDrawArrays(GL_POINTS, 0,(GLsizei ) m_PointsVertex.size());    
    OpenGLCore->glPointSize(2.0f);
    
    OpenGLCore->glBindVertexArray(0);
    m_Program->release();
}

void MyQOpenglWidget::setMatrixUniform()
{
    QMatrix4x4 matrix = QMatrix4x4();
    QMatrix4x4 matrixPerspect= QMatrix4x4();;
    QMatrix4x4 matrixView= QMatrix4x4();;
    QMatrix4x4 matrixModel= QMatrix4x4();;
    
    QVector3D minPos = (m_box.minPoint - m_box.centerPoint);
    QVector3D maxPos = (m_box.maxPoint - m_box.centerPoint);
    float maxAxis =qAbs(qMax(qMax(m_box.width,m_box.height),m_box.depth)) ;
    
    matrixPerspect.ortho(2*minPos[0], 2*maxPos[0], 2*minPos[1], 2*maxPos[1], -2*maxAxis, 2*maxAxis);
    
    matrixView.lookAt(QVector3D(0, 0, 1.0),QVector3D(0.0,0.0,-1),QVector3D(0.0,1.0,0.0));
    matrixView.translate(m_lineMove.x(),m_lineMove.y(),m_lineMove.z());
    
    matrixModel.rotate(m_rotate);
    matrixModel.scale(m_scale);
    
    matrix = matrixPerspect*matrixView*matrixModel;
    
    m_Program->setUniformValue(m_matrixUniform, matrix);
}

void MyQOpenglWidget::initCloud()
{
    m_PointsVertex.clear();
    VertexInfo point;
    point.pos[0] = 0.0f ;
    point.pos[1] = 0.0f ;
    point.pos[2] = 0.0f ;
    point.color[0] = m_backgroundColor.x();
    point.color[1] = m_backgroundColor.y();
    point.color[2] = m_backgroundColor.z();
    point.color[3] = m_backgroundColor.w();
    point.normal[0] = 0.0f;
    point.normal[1] = 1.0f;
    point.normal[2] = 0.0f;
    m_PointsVertex.push_back(point);
}

void MyQOpenglWidget::onTimerOut()
{
    if(this->isVisible())
    {
        repaint();
    }
}

void MyQOpenglWidget::mousePressEvent(QMouseEvent *e)
{
    if (e->buttons()&Qt::LeftButton || e->buttons()&Qt::MidButton)
    {
        setMouseTracking(true);
        m_lastPoint = QVector2D(e->localPos());
    }   
}

void MyQOpenglWidget::mouseMoveEvent(QMouseEvent *e)
{	
    if (e->buttons()&Qt::LeftButton)
    {
        Rotate(QVector2D(m_lastPoint), QVector2D(e->localPos()));
    }
    if (e->buttons()&Qt::MidButton)
    {
        LineMove(m_lastPoint, QVector2D(e->localPos()));
    }
    m_lastPoint = QVector2D(e->localPos());
}

void MyQOpenglWidget::mouseReleaseEvent(QMouseEvent *e)
{
    setMouseTracking(false);
}

void MyQOpenglWidget::wheelEvent(QWheelEvent *e)
{   
    if(e->delta() > 0)
    {
        modelZoomInOrOut(true);
    }
    else
    {
        modelZoomInOrOut(false);
    }
}

void MyQOpenglWidget::keyPressEvent(QKeyEvent *e)
{
    if (e->key() == Qt::Key_R)
    {
        ResetView();
    }
    QWidget::keyPressEvent(e);
}

void MyQOpenglWidget::leaveEvent(QEvent *)
{
    releaseKeyboard();
}

void MyQOpenglWidget::enterEvent(QEvent *)
{
    grabKeyboard();
}

GLuint MyQOpenglWidget::createGPUProgram(QString nVertexShaderFile, QString nFragmentShaderFile)
{
    m_VertexShader = new QOpenGLShader(QOpenGLShader::Vertex);
    bool isOK = m_VertexShader->compileSourceFile(nVertexShaderFile);
    if (!isOK)
    {
        delete m_VertexShader;
        m_VertexShader = nullptr;
        qDebug() << "compile vertex shader fail" ;
        return 0;
    }
    
    m_FragmentShader = new QOpenGLShader(QOpenGLShader::Fragment);
    if (!m_FragmentShader->compileSourceFile(nFragmentShaderFile))
    {
        delete m_VertexShader;
        delete m_FragmentShader;
        m_FragmentShader = nullptr;
        qDebug() << "compile fragment shader fail";
        return 0;
    }
    m_Program = new QOpenGLShaderProgram(this);
    m_Program->addShader(m_VertexShader);
    m_Program->addShader(m_FragmentShader);
    m_Program->link();
    GetShaderUniformPara();
    return m_Program->programId();
}

void MyQOpenglWidget::LineMove(QVector2D posOrgin, QVector2D posEnd)
{
    float ratio = 1.0f;
    float xoffset = posEnd.x() - posOrgin.x();
    float yoffset = posEnd.y() - posOrgin.y();
    
    m_lineMove.setX(m_lineMove.x()+xoffset*ratio);
    m_lineMove.setY(m_lineMove.y()-yoffset*ratio);
}

void MyQOpenglWidget::Rotate(QVector2D posOrgin, QVector2D posEnd)
{
    QVector2D diff = posEnd - posOrgin;
    qreal acc = diff.length() / 100.0;
    if (acc<0.01f)
    {
        return;
    }
    calRotation(posOrgin,posEnd);
}

void MyQOpenglWidget::modelZoomInOrOut(bool ZoomInOrOut)
{
    if(ZoomInOrOut)//zoom in
    {
        m_scale*=1.1f;
    }
    else
    {
        m_scale*=0.9f;
        if(m_scale<0.5f)
        {
            m_scale = 0.5f;
        }
    }
}

void MyQOpenglWidget::calRotation(QVector2D posOrgin, QVector2D posEnd)
{
    QVector3D orginViewPos = pixelPosToViewPos(posOrgin);
    QVector3D endViewPos = pixelPosToViewPos(posEnd);
    float RotateAngle;
    RotateAngle = qRadiansToDegrees(std::acos(QVector3D::dotProduct(orginViewPos,endViewPos)));
    QVector3D axis;
    axis=QVector3D::crossProduct(orginViewPos, endViewPos);
    axis.normalize();
    m_rotate=  QQuaternion::fromAxisAndAngle(axis, RotateAngle)*m_rotate;
}

QVector3D MyQOpenglWidget::pixelPosToViewPos(const QVector2D &p)
{
    QVector3D viewPos(2.0 * float(p.x()) / width() - 1.0,  1.0 - 2.0 * float(p.y()) / height(), 0);
    float sqrZ = 1 - QVector3D::dotProduct(viewPos, viewPos);
    if(sqrZ>0)
    {
        viewPos.setZ(std::sqrt(sqrZ));
    }
    else
    {     
        viewPos.normalize();
    }
    return viewPos;
}

效果如下:
在这里插入图片描述
完整工程文件见下载链接:
https://download.csdn.net/download/taifyang/89862113

QOpenGLWidgetQt中的一个用于在OpenGL上下文中显示图形的小部件。要在QOpenGLWidget显示QImage,可以按照以下步骤进行操作: 1. 创建一个继承自QOpenGLWidget的自定义小部件类,并重写其initializeGL、resizeGL和paintGL函数。 2. 在initializeGL函数中,进行OpenGL的初始化工作,例如设置背景颜色、启用深度测试等。 3. 在resizeGL函数中,根据窗口大小调整OpenGL视口的大小。 4. 在paintGL函数中,进行绘制操作。这里需要将QImage转换为OpenGL可用的纹理,并使用OpenGL的绘制函数进行绘制。 下面是一个简单的示例代码: ```cpp class MyOpenGLWidget : public QOpenGLWidget { public: MyOpenGLWidget(QWidget *parent = nullptr) : QOpenGLWidget(parent) {} protected: void initializeGL() override { // 初始化OpenGL glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glEnable(GL_DEPTH_TEST); } void resizeGL(int w, int h) override { // 调整OpenGL视口大小 glViewport(0, 0, w, h); } void paintGL() override { // 绘制操作 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 将QImage转换为OpenGL纹理 QImage image; // 假设已经有一个QImage对象image GLuint texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image.bits()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 绘制纹理 glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex2f(-1.0f, -1.0f); glTexCoord2f(1.0f, 0.0f); glVertex2f(1.0f, -1.0f); glTexCoord2f(1.0f, 1.0f); glVertex2f(1.0f, 1.0f); glTexCoord2f(0.0f, 1.0f); glVertex2f(-1.0f, 1.0f); glEnd(); // 删除纹理 glDeleteTextures(1, &texture); } }; ``` 使用这个自定义的MyOpenGLWidget类,你可以将其添加到你的Qt应用程序中,并在其中显示QImage。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

给算法爸爸上香

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值