用qt的OpenGL绘制三维坐标系以及弧线球面,支持鼠标缩放平移旋转操作

建一个三维坐标系,在坐标轴上添加文本Z、X、Y,支持鼠标缩放平移旋转

用qt的opengl绘制三维坐标系,可以用于一些3维轨迹显示

.pro文件中需要添加opengl库

QT += core gui opengl

找不到gl_xxxxx定义解决方法

在.pro结尾加上以下:

win32:LIBS += -lOpengl32\
-lglu32
unix:LIBS += -lglut -lGLU

如此就可以找到gl的lib库了

qtdraw3dctrl.h

#ifndef QTDRAW3DCTRL_H
#define QTDRAW3DCTRL_H
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

class QtDraw3DCtrl : public QOpenGLWidget
{
    Q_OBJECT

public:
    QtDraw3DCtrl(QWidget *parent = 0);
    ~QtDraw3DCtrl();

    void setFieldData(double dField, double dTheta, double dPhi);

protected:
    virtual void initializeGL();
    virtual void resizeGL(int w, int h);
    virtual void paintGL();
    virtual void mousePressEvent(QMouseEvent *event);
    virtual void mouseReleaseEvent(QMouseEvent *event);
    virtual void mouseMoveEvent(QMouseEvent *event);
    virtual void wheelEvent(QWheelEvent *event);

public slots:
    void UpdateSlots();

private:
    int m_iScloe; //滚动放大缩小尺寸

    //旋转
    double m_dRotX;
    double m_dRotY;
    double m_dRotZ;

    //位移
    double m_dMoveX;
    double m_dMoveY;
    double m_dMoveZ;

    QPoint m_qpRotPosOld;
    int m_iSize;
    double m_dFieldX;
    double m_dFieldY;
    double m_dFieldZ;

    void GLGrid(float pt1x, float pt1y, float pt1z, float pt2x, float pt2y, float pt2z, int num);
    void drawSphere(float fX, float fY, float fZ, float fRadius, float M, float N);
    void drawInnerCircular(float fX, float fY, float fZ, float fRadius, float fAngleZ, float fAngleXY, float fStepZ, float fStepXY);
    void drawAxisText(float fStart[3], float fPoint[2], float fColor[3], QString strText);
    void drawAllAxisText();
    void drawFieldText();
    void drawAllAxis();
    QVector<double> SweepToAxis(double dField, double dTheta, double dPhi);
};

#endif

qtdraw3dctrl.cpp

#include "qtdraw3dctrl.h"

#define PI 3.1415926
#define RADIUS 4.5

QtDraw3DCtrl::QtDraw3DCtrl(QWidget *parent) : QOpenGLWidget(parent)
{
    m_dMoveX = 0;
    m_dMoveY = 0;
    m_dMoveZ = -13;
    m_iScloe = -13;
    m_dRotX = 0;
    m_dRotY = 0;
    m_dRotZ = 0;
    m_iSize = 2;
    QTimer* timer = new QTimer(this);
    timer->setInterval(10);
    connect(timer, SIGNAL(timeout()), this, SLOT(UpdateSlots()));
    timer->start();

    m_dFieldX = 2.59;
    m_dFieldY = 2.59;
    m_dFieldZ = 2.59;
}

QtDraw3DCtrl::~QtDraw3DCtrl()
{

}

void QtDraw3DCtrl::UpdateSlots()
{
    update();
}

void QtDraw3DCtrl::initializeGL()
{
    glClearColor(0.92, 0.94, 0.95, 1); //#EBF0F3
    glShadeModel(GL_SMOOTH);
    glEnable(GL_DEPTH);
}

void QtDraw3DCtrl::resizeGL(int w, int h)
{
    if (h == 0) {
        h = 1;
    }

    glViewport(0, 0, (GLint)w, (GLint)h); //重置当前的视口
    glMatrixMode(GL_PROJECTION); //选择投影矩阵

    glLoadIdentity(); //重置投影矩阵
    gluPerspective(45, (GLfloat)w / (GLfloat)h, 0.001, 1000); //建立透视投影矩阵

    glMatrixMode(GL_MODELVIEW); //选择模型观察矩阵
    glLoadIdentity(); //重置模型观察矩阵
}

/* 画坐标系上的文字 */
void QtDraw3DCtrl::drawAxisText(float fStart[3], float fPoint[2], float fColor[3], QString strText)
{
    QPainterPath path;
    glDisable(GL_DEPTH);
    path.addText(QPointF(fPoint[0], fPoint[1]), QFont("PMingLiU", 3), strText);
    QList<QPolygonF> poly = path.toSubpathPolygons();

    QList<QPolygonF>::iterator iter = poly.begin();
    while(iter != poly.end()) {
        glBegin(GL_LINE_LOOP);
        QPolygonF::iterator it = (*iter).begin();
        while(it != iter->end()) {
            glVertex3f(fStart[0] + it->rx() * 0.1, fStart[1] - it->ry() * 0.1, fStart[2]);
            glColor3f(fColor[0], fColor[1], fColor[2]);
            it++;
        }
        glEnd();
        iter++;
    }
    glEnable(GL_DEPTH);
}

/* 准备三个坐标系上的文字 */
void QtDraw3DCtrl::drawAllAxisText()
{
    float fStart[3];
    float fPoint[2];
    float fColor[3];

    fStart[0] = RADIUS;
    fStart[1] = 0;
    fStart[2] = 0;
    fPoint[0] = 2.5;
    fPoint[1] = 1;
    fColor[0] = 1;
    fColor[1] = 0;
    fColor[2] = 0;
    drawAxisText(fStart, fPoint, fColor, "X");

    fStart[0] = 0;
    fStart[1] = RADIUS;
    fStart[2] = 0;
    fPoint[0] = -1.2;
    fPoint[1] = -2.5;
    fColor[0] = 0;
    fColor[1] = 1;
    fColor[2] = 0;
    drawAxisText(fStart, fPoint, fColor, "Y");

    fStart[0] = 0;
    fStart[1] = 0;
    fStart[2] = RADIUS + 0.5;
    fPoint[0] = -1;
    fPoint[1] = 1;
    fColor[0] = 0;
    fColor[1] = 0;
    fColor[2] = 1;
    drawAxisText(fStart, fPoint, fColor, "Z");
}

/* 画磁场夹角和文本 */
void QtDraw3DCtrl::drawFieldText()
{
    float fStart[3];
    float fPoint[2];
    float fColor[3];

    float fAngleY = 0.34;
    if(m_dFieldY < 0) {
        fAngleY = -0.34;
    }

    float fAngleX = fAngleY * m_dFieldX / m_dFieldY;
    float fAngleZ = fAngleY * m_dFieldZ / m_dFieldY;

    glLineWidth(1.9);
    glBegin(GL_LINES);
    glEnable(GL_LINE_SMOOTH);
    glColor3f(1, 0, 0);
    glVertex3f(0, fAngleY, 0);
    glVertex3f(fAngleX, fAngleY, fAngleZ);
    glEnd();

    glLineWidth(1.9);
    glBegin(GL_LINES);
    glEnable(GL_LINE_SMOOTH);
    glColor3f(1, 0, 0);
    glVertex3f(0, 0, 0.34);
    glVertex3f(fAngleX, 0, fAngleZ);
    glEnd();

    fStart[0] = 0.1;
    fStart[1] = 0.3;
    fStart[2] = 0.1;
    fPoint[0] = 0;
    fPoint[1] = 0;
    fColor[0] = 0;
    fColor[1] = 0;
    fColor[2] = 0;
    drawAxisText(fStart, fPoint, fColor, QString("Φ"));

    fStart[0] = 0.1;
    fStart[1] = -0.1;
    fStart[2] = 0.6;
    fPoint[0] = 0;
    fPoint[1] = 0;
    fColor[0] = 0;
    fColor[1] = 0;
    fColor[2] = 0;
    drawAxisText(fStart, fPoint, fColor, QString("Θ"));

    //虚线
    glLineStipple(2, 0x5555);
    glEnable(GL_LINE_STIPPLE);
    glBegin(GL_LINE_STRIP);
    glColor3f(1, 0, 0);
    glVertex3f(0, 0, 0);
    glVertex3f(m_dFieldX, 0, m_dFieldZ);
    glVertex3f(m_dFieldX, m_dFieldY, m_dFieldZ);
    glEnd();
    glDisable(GL_LINE_STIPPLE);

    glFlush();
}

/* 画三个坐标轴 */
void QtDraw3DCtrl::drawAllAxis()
{
    GLUquadricObj *objCylinder = gluNewQuadric();
    int num = 1; //一个网格

    //X
    glPushMatrix();
    glColor3f(0.4, 0.4, 0.4);
    glTranslatef(0, 0, 0);
    GLGrid(0, 0, 0, RADIUS, 0, 0, num); //x轴只留一个网格
    glPopMatrix();

    glPushMatrix();
    glColor3f(1, 0, 0);
    glTranslatef(RADIUS, 0, 0);
    glRotatef(90, 0, 1, 0);
    gluCylinder(objCylinder, 0.1, 0, 0.2, 100, 1);
    glPopMatrix();

    //Y
    glPushMatrix();
    glTranslated(0, RADIUS, 0);
    glRotatef(90, 1, 0, 0);
    glColor3f(0.4, 0.4, 0.4);
    GLGrid(0, 0, 0, 0, 0, RADIUS, num); //y轴只留一个网格
    glPopMatrix();

    glPushMatrix();
    glColor3f(0, 1, 0);
    glTranslatef(0, RADIUS, 0);
    glRotatef(-90, 1, 0, 0);
    gluCylinder(objCylinder, 0.1, 0, 0.2, 100, 1);
    glPopMatrix();

    //Z
    glPushMatrix();
    glTranslatef(0, 0, 0);
    glRotatef(90, 0, 0, 1.0);
    glColor3f(0.4, 0.4, 0.4);
    GLGrid(0, 0, 0, 0, 0, RADIUS, num); //z只直留一个网格
    glPopMatrix();

    glPushMatrix();
    glColor3f(0, 0, 1);
    glTranslatef(0, 0, RADIUS);
    glRotatef(90, 0, 0, 1);
    gluCylinder(objCylinder, 0.1, 0, 0.2, 100, 1);
    glPopMatrix();
}

/* ρ,Θ,Φ转换为x,y,z */
QVector<double> QtDraw3DCtrl::SweepToAxis(double dField, double dTheta, double dPhi)
{
    QVector<double> vAxis(3);
    vAxis[0] = dField * sin(dTheta) * sin(dPhi);
    vAxis[1] = dField * sin(dTheta) * cos(dPhi);
    vAxis[2] = cos(dTheta);
    return vAxis;
}

void QtDraw3DCtrl::setFieldData(double dField, double dTheta, double dPhi)
{
    QVector<double> vAxis = SweepToAxis(dField, dTheta, dPhi);
    m_dFieldX = vAxis[0];
    m_dFieldY = vAxis[1];
    m_dFieldZ = vAxis[2];
}

void QtDraw3DCtrl::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕和深度缓存
    glLoadIdentity(); //重置当前的模型观察矩阵

    //旋转显示窗口
    glTranslatef(m_dMoveX, m_dMoveY, m_iScloe);
    glRotatef(sqrt(m_dRotX*m_dRotX+m_dRotY*m_dRotY), m_dRotY,m_dRotX,m_dRotZ);

    //轴
    drawAllAxis();

    //数据线
    glLineWidth(1.9);
    glBegin(GL_LINES);
    glEnable(GL_LINE_SMOOTH);
    glTranslatef(-m_iSize, -m_iSize, -m_iSize);
    glColor3f(1, 0.5, 1);

    glVertex3f(0, 0, 0);
    glVertex3f(m_dFieldX, m_dFieldY, m_dFieldZ);

    glEnd();
    glFlush();

    //球
    drawSphere(0, 0, 0, RADIUS, 270, 270);

    //坐标轴文本
    drawAllAxisText();

    //磁场文本
    drawFieldText();
}

/* 绘制圆 */
void QtDraw3DCtrl::drawInnerCircular(float fX, float fY, float fZ, float fRadius, float fAngleZ, float fAngleXY, float fStepZ, float fStepXY)
{
    float x[4], y[4], z[4];
    x[0] = fRadius * sin(fAngleZ) * cos(fAngleXY);
    y[0] = fRadius * sin(fAngleZ) * sin(fAngleXY);
    z[0] = fRadius * cos(fAngleZ);

    x[1] = fRadius * sin(fAngleZ + fStepZ) * cos(fAngleXY);
    y[1] = fRadius * sin(fAngleZ + fStepZ) * sin(fAngleXY);
    z[1] = fRadius * cos(fAngleZ + fStepZ);

    x[2] = fRadius * sin(fAngleZ + fStepZ) * cos(fAngleXY + fStepXY);
    y[2] = fRadius * sin(fAngleZ + fStepZ) * sin(fAngleXY + fStepXY);
    z[2] = fRadius * cos(fAngleZ + fStepZ);

    x[3] = fRadius * sin(fAngleZ) * cos(fAngleXY + fStepXY);
    y[3] = fRadius * sin(fAngleZ) * sin(fAngleXY + fStepXY);
    z[3] = fRadius * cos(fAngleZ);

    for(int k = 0; k < 4; k++) {
        glColor3f(0.69, 0.81, 0.92);
        glBlendFunc(GL_ONE, GL_ZERO);
        glTexCoord2f(0.1, 0.1);
        glVertex3f(fY + y[k], fZ + z[k],  fY + x[k]);
    }
}

/* 绘制球体,球心坐标为(xyz),球的半径为radius,M,N分别表示球体的横纵向被分成多少份 */
void QtDraw3DCtrl::drawSphere(float fX, float fY, float fZ, float fRadius, float M, float N)
{
    // 选择使用的纹理
    glBindTexture(GL_TEXTURE_2D, 1);

    float fStepZ = PI / M;
    float fStepXY = 2 * PI / N;

    int i = 0, j = 0;
    glBegin(GL_QUADS);
    for(i = 0; i < M; i++) {
        for(j = 0; j < N; j++) {
            if(j % 15 != 0) {
                continue;
            }
            drawInnerCircular(fX, fY, fZ, fRadius, i * fStepZ, j * fStepXY, fStepZ, fStepXY);
        }
    }

    for(i = 0; i < M; i++) {
        if(i % 15 != 0) {
            continue;
        }

        for(j = 0; j < N; j++) {
            drawInnerCircular(fX, fY, fZ, fRadius, i * fStepZ, j * fStepXY, fStepZ, fStepXY);
        }
    }
    glEnd();
}

void QtDraw3DCtrl::mousePressEvent(QMouseEvent *event)
{

}

void QtDraw3DCtrl::mouseReleaseEvent(QMouseEvent *event)
{


}

void QtDraw3DCtrl::mouseMoveEvent(QMouseEvent *event)
{
    QPoint pos = event->pos();

    //左键
    if( event->buttons() & Qt::LeftButton)
    {

        if (pos.x()  > m_qpRotPosOld.x())
        {
            m_dRotX += 1;
        }
        else if(pos.x()  < m_qpRotPosOld.x())
        {
            m_dRotX -= 1;
        }
        if(pos.y()  > m_qpRotPosOld.y())
        {
            m_dRotY += 1;
        }
        else if(pos.y()  < m_qpRotPosOld.y())
        {
            m_dRotY -= 1;
        }

    }
    if( event->buttons() & Qt::RightButton)             //右键
    {
        if (pos.x()  > m_qpRotPosOld.x())
        {
            m_dMoveX += 0.1;
        }
        else if(pos.x()  < m_qpRotPosOld.x())
        {
            m_dMoveX -= 0.1;
        }
        if(pos.y()  > m_qpRotPosOld.y())
        {
            m_dMoveY -= 0.1;
        }
        else if(pos.y()  < m_qpRotPosOld.y())
        {
            m_dMoveY += 0.1;
        }
    }
    m_qpRotPosOld = pos;


    update();
}

void QtDraw3DCtrl::wheelEvent(QWheelEvent *event)
{
    if (event->delta() < 0) {
        m_iScloe--;
    }
    else if (event->delta() > 0) {
        m_iScloe++;
    }

    update();
}

/* 画坐标轴和网格 */
void QtDraw3DCtrl::GLGrid(float pt1x, float pt1y, float pt1z, float pt2x, float pt2y, float pt2z, int num)
{
    const float _xLen = (pt2x - pt1x) / num;
    const float _yLen = (pt2y - pt1y) / num;
    const float _zLen = (pt2z - pt1z) / num;
    glLineWidth(1.9);

    glBegin(GL_LINES);
    glEnable(GL_LINE_SMOOTH);

    int xi = 0;
    int yi = 0;
    int zi = 0;

    //绘制平行于X的直线
    for (zi = 0; zi <= num; zi++) {
        float z = _zLen * zi + pt1z;
        for (yi = 0; yi <= num; yi++) {
            float y = _yLen * yi + pt1y;
            glVertex3f(pt1x, y, z);
            glVertex3f(pt2x, y, z);
        }
    }

    //绘制平行于Y的直线
    for (zi = 0; zi <= num; zi++) {
        float z = _zLen * zi + pt1z;
        for (xi = 0; xi <= num; xi++) {
            float x = _xLen * xi + pt1x;
            glVertex3f(x, pt1y, z);
            glVertex3f(x, pt2y, z);
        }
    }

    //绘制平行于Z的直线
    for (yi = 0; yi <= num; yi++) {
        float y = _yLen * yi + pt1y;
        for (xi = 0; xi <= num; xi++) {
            float x = _xLen * xi + pt1x;
            glVertex3f(x, y, pt1z);
            glVertex3f(x, y, pt2z);
        }
    }
    glEnd();
}

运行效果

如何显示

创建一个widget,将创建的widget作为opengl类的父类传入即可,例如:

QtDraw3DCtrl *draw = new QtDraw3DCtrl(ui->widget);
draw->setGeometry(ui->widget->geometry());

其中ui->widget是ui文件中创建的一个widget

widget调用效果

用qt的OpenGL绘制三维坐标系以及弧线球面,支持鼠标缩放平移旋转操作_第1张图片

或者直接可以新建一个主窗口,直接将主窗口当做opengl类的父类,将整个主窗口作为opengl类的父类

mainwindows.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QtDraw3DCtrl *draw = new QtDraw3DCtrl(this);
    draw->setGeometry(this->geometry());


}

MainWindow::~MainWindow()
{
    delete ui;


MAINWINDOWS调用效果

用qt的OpenGL绘制三维坐标系以及弧线球面,支持鼠标缩放平移旋转操作_第2张图片

如此就实现了用qt的opengl库绘制三维显示,可以在此基础上自己根据需要增删功能代码。

参考自:https://blog.csdn.net/ZHYFXY/article/details/126622333

你可能感兴趣的:(qt,开发语言,c++)