手把手教你使用Qt自带的opengl创建显示立方体顶点

例程主要时在三维空间中绘制了一个立方体的8个顶点,并且可以绕x,y,z轴进行旋转,运行效果如下:

 

1.先创建一个窗口类程序,如下图:

 2.添加一个类,取名叫GLWidget,这个类就是用来调用opengl显示三维数据的!

 

3.在工程目录下新建个文件夹,放个纯色的图片进去,方便给三维空间中的顶点着色用,然后新建资源文件,命名为texture,步骤如下:

 准备工作做完了,接下来详细编写GLWidget类了,代码如下:

头文件:

#ifndef GLWIDGET_H
#define GLWIDGET_H
#include <QOpenGLWidget>//为了方便进行界面设计,一般直接继承QOpenGLWidget作为窗口部件进行布局处理
#include <QOpenGLFunctions>//QT将OpenGL中的底层函数都封装在一个命名为QOPenGLFunctions的类
#include <QOpenGLBuffer>
QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram)//类前置声明
QT_FORWARD_DECLARE_CLASS(QOpenGLTexture)
class GLWidget: public QOpenGLWidget, protected QOpenGLFunctions
{
    Q_OBJECT

public:
    explicit GLWidget(QWidget *parent = 0);
    ~GLWidget();
//    QSize minimumSizeHint() const override;
//    QSize sizeHint() const override;
    void rotateBy(int xAngle, int yAngle, int zAngle);//旋转顶点
    void setClearColor(const QColor &color);

signals:
    void clicked();

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 mouseReleaseEvent(QMouseEvent *event) override;

private:
    void makeObject();//创建三维空间中的顶点数据
    QColor clearColor;//opengl窗口背景颜色
    QPoint lastPos;//鼠标上一次位置
    int xRot;//绕x轴旋转角度
    int yRot;//绕y轴旋转角度
    int zRot;//绕z轴旋转角度
    QOpenGLTexture *textures;
    QOpenGLShaderProgram *program;//定义一个着色器程序类变量,用来执行opengl的着色器语言代码(GLSL或者GLSL/ES语言)
    QOpenGLBuffer vbo;//在Opengl中创建的缓冲区对象,复制该变量时,可作为底层Opengl缓冲区对象的引用进行复制,
    //即修改副本时,原始对象将受影响
};

#endif // GLWIDGET_H

源文件:

#include "glwidget.h"
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QMouseEvent>

GLWidget::GLWidget(QWidget *parent)
    : QOpenGLWidget(parent),
      clearColor(Qt::black),
      xRot(0),
      yRot(0),
      zRot(0),
      program(0)
{

}

GLWidget::~GLWidget()
{
    makeCurrent();
    vbo.destroy();
    delete program;
    doneCurrent();
}

void GLWidget::rotateBy(int xAngle, int yAngle, int zAngle)
{
    xRot += xAngle;
    yRot += yAngle;
    zRot += zAngle;
    update();
}

void GLWidget::setClearColor(const QColor &color)
{
    clearColor = color;
    update();
}

void GLWidget::initializeGL()
{
    initializeOpenGLFunctions();//为当前环境初始化opengl功能
    makeObject();//构建立方体每个顶点坐标
    //开启Opengl的一些功能
    glEnable(GL_DEPTH_TEST);//如果启用,请进行深度比较并更新深度缓冲区。请注意,
    //即使深度缓冲区存在,并且深度掩模非零,如果禁用了深度测试,深度缓冲区也不会更新

    //glEnable(GL_CULL_FACE);//如果开启,在当前窗口坐标中,根据环绕方式剔除多边形

#define PROGRAM_VERTEX_ATTRIBUTE 0
#define PROGRAM_TEXCOORD_ATTRIBUTE 1

    //在可编程管线中,需要纯手写顶点和片段着色器,用到opengl的着色器语言GLSL
    //GLSL是在GPU上执行的,使渲染管线中不同层次具有可编程性,比如试图转换,投影转换等
    QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this);
    const char *vsrc =
        "attribute highp vec4 vertex;\n"
        "attribute mediump vec4 texCoord;\n"
        "varying mediump vec4 texc;\n"
        "uniform mediump mat4 matrix;\n"
        "void main(void)\n"
        "{\n"
        "    gl_Position = matrix * vertex;\n"
        "    texc = texCoord;\n"
        "}\n";//opengl的着色器语言(GLSL)源码
    vshader->compileSourceCode(vsrc);//设置顶点着色器的源码,并编译

    QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this);
    const char *fsrc =
        "uniform sampler2D texture;\n"
        "varying mediump vec4 texc;\n"
        "void main(void)\n"
        "{\n"
        "    gl_FragColor = texture2D(texture, texc.st);\n"
        "}\n";//opengl的着色器语言(GLSL)源码
    fshader->compileSourceCode(fsrc);//设置片段着色器源码,并编译

    program = new QOpenGLShaderProgram;
    program->addShader(vshader);
    program->addShader(fshader);
    program->bindAttributeLocation("vertex", PROGRAM_VERTEX_ATTRIBUTE);
    program->bindAttributeLocation("texCoord", PROGRAM_TEXCOORD_ATTRIBUTE);
    program->link();
    program->bind();
    program->setUniformValue("texture", 0);
}

void GLWidget::paintGL()
{
    glClearColor(clearColor.redF(), clearColor.greenF(), clearColor.blueF(), clearColor.alphaF());
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    QMatrix4x4 m;
    m.ortho(-0.5f, +0.5f, +0.5f, -0.5f, 4.0f, 15.0f);//定义一个长方体,长方体正面与camera的光轴垂直,4.0f表示长方体正面与camera的距离,15.0f表示长方体背面与camera的距离
    m.translate(0.0f, 0.0f, -10.0f);//平移
    m.rotate(xRot / 16.0f, 1.0f, 0.0f, 0.0f);//旋转
    m.rotate(yRot / 16.0f, 0.0f, 1.0f, 0.0f);//旋转
    m.rotate(zRot / 16.0f, 0.0f, 0.0f, 1.0f);//旋转
//注意:上面关于m的操作在程序运行过程中,是先将顶点旋转,然后平移,最后通过ortho()定义视景体
    program->setUniformValue("matrix", m);//将当前上下文中名为matrix的变量赋值为矩阵m
    program->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE);//启用此着色器程序中位于PROGRAM_VERTEX_ATTRIBUTE位置的顶点数组
    program->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE);//启用此着色器程序中位于enableAttributeArray位置的顶点数组
    //QOpenGLShaderProgram::setAttributeBuffer在此着色器程序中属性的位置设置一个顶点值数组,从当前绑定的顶点缓冲区中的特定偏移量开始。
    //步幅表示顶点之间的字节数。默认的步幅值为零,表示顶点密集地排列在值数组中。 该类型表示顶点值数组中元素的类型,通常是gl_float、gl_unsigned_byte等
    program->setAttributeBuffer(PROGRAM_VERTEX_ATTRIBUTE, GL_FLOAT, 0, 3, 5 * sizeof(GLfloat));
    program->setAttributeBuffer(PROGRAM_TEXCOORD_ATTRIBUTE, GL_FLOAT, 3 * sizeof(GLfloat), 2, 5 * sizeof(GLfloat));
//    for (int i = 0; i < 6; ++i) {
        textures[i]->bind();
//        glDrawArrays(GL_POINTS, i * 4, 4);
        glDrawArrays(GL_TRIANGLE_FAN, i * 4, 4);
//    }
    textures->bind();
    glDrawArrays(GL_POINTS, 0, 8);
}

void GLWidget::resizeGL(int width, int height)
{
    int side = qMin(width, height);
    glViewport((width - side) / 2, (height - side) / 2, side, side);

}

void GLWidget::mousePressEvent(QMouseEvent *event)
{
    lastPos = event->pos();
}

void GLWidget::mouseMoveEvent(QMouseEvent *event)
{
    int dx = event->x() - lastPos.x();
    int dy = event->y() - lastPos.y();

    if (event->buttons() & Qt::LeftButton) {
        rotateBy(8 * dy, 8 * dx, 0);
    } else if (event->buttons() & Qt::RightButton) {
        rotateBy(8 * dy, 0, 8 * dx);
    }
    lastPos = event->pos();
}

void GLWidget::mouseReleaseEvent(QMouseEvent *event)
{
    emit clicked();
}

void GLWidget::makeObject()
{
    static const int coords[8][3] = {
        { +1, -1, -1 }, { -1, -1, -1 }, { -1, +1, -1 }, { +1, +1, -1 } ,
        { +1, -1, +1 }, { -1, -1, +1 }, { -1, +1, +1 }, { +1, +1, +1 }
    };
    QVector<GLfloat> vertData;
    for (int j = 0; j < 8; ++j) {
        // vertex position
        vertData.append(0.2 * coords[j][0]);
        vertData.append(0.2 * coords[j][1]);
        vertData.append(0.2 * coords[j][2]);
        // texture coordinate
        vertData.append(j == 0 || j == 3);
        vertData.append(j == 0 || j == 1);
    }
    vbo.create();//在opengl中创建缓冲
    vbo.bind();//绑定opengl上i想安稳
    vbo.allocate(vertData.constData(), vertData.count() * sizeof(GLfloat));//为缓冲区申请控件,之前的缓冲区内容将被清空
    textures = new QOpenGLTexture(QImage(QString(":/images/side1.png")).mirrored());//纹理坐标与绘制设备坐标不同,所以需进行镜像操作
}

然后在主界面mainwindow中放一个verticalLayout布局,用于放GLWidget类对象,如下:

 然后mainwindow.h如下:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
class GLWidget;
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    GLWidget *currentGlWidget;
};
#endif // MAINWINDOW_H

mainwindow.cpp代码如下:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "glwidget.h"
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QColor clearColor;//默认黑色
    clearColor.setBlue(100);
    currentGlWidget = new GLWidget;
    currentGlWidget->setClearColor(clearColor);
    currentGlWidget->rotateBy(+42 * 16, +42 * 16, -21 * 16);
    ui->verticalLayout->addWidget(currentGlWidget);
}

MainWindow::~MainWindow()
{
    delete ui;
}

然后编译运行就行。

Guess you like

Origin blog.csdn.net/weixin_43935474/article/details/119638037