Qt OpenGL 3D 编程入门

Qt 提供了强大的 OpenGL 集成功能,使得在 Qt 应用中实现 3D 图形变得更加简单。以下是使用 Qt 进行 OpenGL 3D 编程的基础知识。

1. 环境配置

创建 Qt 项目

  1. 新建 Qt Widgets Application 项目

  2. .pro 文件中添加 OpenGL 模块:

qmake

复制代码
QT       += core gui opengl

基本 OpenGL 窗口类

Qt 提供了 QOpenGLWidget 作为 OpenGL 渲染的基础组件。

openglwidget.h

复制代码
#ifndef OPENGLWIDGET_H
#define OPENGLWIDGET_H

#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QTime>

class OpenGLWidget : public QOpenGLWidget, protected QOpenGLFunctions_3_3_Core
{
    Q_OBJECT
public:
    explicit OpenGLWidget(QWidget *parent = nullptr);
    ~OpenGLWidget();

protected:
    void initializeGL() override;
    void resizeGL(int w, int h) override;
    void paintGL() override;

    void mousePressEvent(QMouseEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;
    void wheelEvent(QWheelEvent *event) override;

private:
    QOpenGLVertexArrayObject vao;
    QOpenGLBuffer vbo{QOpenGLBuffer::VertexBuffer};
    QOpenGLBuffer ebo{QOpenGLBuffer::IndexBuffer};
    QOpenGLShaderProgram shaderProgram;
    QOpenGLTexture *texture;
    QTime time;
    QPoint lastMousePos;
    float xRot = 0.0f;
    float yRot = 0.0f;
    float zRot = 0.0f;
    float zoom = -5.0f;
};

#endif // OPENGLWIDGET_H

2. 基本框架实现

openglwidget.cpp

复制代码
#include "openglwidget.h"
#include <QDebug>
#include <QMouseEvent>
#include <QImage>

OpenGLWidget::OpenGLWidget(QWidget *parent) : QOpenGLWidget(parent)
{
    setFocusPolicy(Qt::StrongFocus);
    setMouseTracking(true);
    time.start();
}

OpenGLWidget::~OpenGLWidget()
{
    makeCurrent();
    vao.destroy();
    vbo.destroy();
    ebo.destroy();
    delete texture;
    doneCurrent();
}

void OpenGLWidget::initializeGL()
{
    initializeOpenGLFunctions();
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glEnable(GL_DEPTH_TEST);

    // 立方体顶点数据 (位置 + 颜色 + 纹理坐标)
    float scale = 2.0f;
    float vertices[] = {
        // 前面
        -0.5f*scale, -0.5f*scale,  0.5f*scale,  1.0f, 0.0f, 0.0f, 1.0f,  0.0f, 0.0f,
        0.5f*scale, -0.5f*scale,  0.5f*scale,  0.0f, 1.0f, 0.0f, 1.0f,  1.0f, 0.0f,
        0.5f*scale,  0.5f*scale,  0.5f*scale,  0.0f, 0.0f, 1.0f, 1.0f,  1.0f, 1.0f,
        -0.5f*scale,  0.5f*scale,  0.5f*scale,  1.0f, 1.0f, 0.0f, 1.0f,  0.0f, 1.0f,

        // 后面
        -0.5f*scale, -0.5f*scale, -0.5f*scale,  1.0f, 0.0f, 1.0f, 1.0f,  1.0f, 0.0f,
        0.5f*scale, -0.5f*scale, -0.5f*scale,  0.0f, 1.0f, 1.0f, 1.0f,  0.0f, 0.0f,
        0.5f*scale,  0.5f*scale, -0.5f*scale,  1.0f, 1.0f, 1.0f, 1.0f,  0.0f, 1.0f,
        -0.5f*scale,  0.5f*scale, -0.5f*scale,  0.5f, 0.5f, 0.5f, 1.0f,  1.0f, 1.0f,

        // 左面
        -0.5f*scale, -0.5f*scale, -0.5f*scale,  0.6f, 0.0f, 0.0f, 1.0f,  0.0f, 0.0f,
        -0.5f*scale, -0.5f*scale,  0.5f*scale,  0.6f, 0.0f, 0.0f, 1.0f,  1.0f, 0.0f,
        -0.5f*scale,  0.5f*scale,  0.5f*scale,  0.6f, 0.0f, 0.0f, 1.0f,  1.0f, 1.0f,
        -0.5f*scale,  0.5f*scale, -0.5f*scale,  0.6f, 0.0f, 0.0f, 1.0f,  0.0f, 1.0f,

        // 右面
        0.5f*scale, -0.5f*scale, -0.5f*scale,  1.0f, 0.2f, 0.2f, 1.0f,  1.0f, 0.0f,
        0.5f*scale, -0.5f*scale,  0.5f*scale,  1.0f, 0.2f, 0.2f, 1.0f,  0.0f, 0.0f,
        0.5f*scale,  0.5f*scale,  0.5f*scale,  1.0f, 0.2f, 0.2f, 1.0f,  0.0f, 1.0f,
        0.5f*scale,  0.5f*scale, -0.5f*scale,  1.0f, 0.2f, 0.2f, 1.0f,  1.0f, 1.0f,

        // 顶面
        -0.5f*scale,  0.5f*scale,  0.5f*scale,  1.0f, 0.1f, 0.1f, 1.0f,  0.0f, 0.0f,
        0.5f*scale,  0.5f*scale,  0.5f*scale,  1.0f, 0.1f, 0.1f, 1.0f,  1.0f, 0.0f,
        0.5f*scale,  0.5f*scale, -0.5f*scale,  1.0f, 0.1f, 0.1f, 1.0f,  1.0f, 1.0f,
        -0.5f*scale,  0.5f*scale, -0.5f*scale,  1.0f, 0.1f, 0.1f, 1.0f,  0.0f, 1.0f,

        // 底面
        -0.5f*scale, -0.5f*scale,  0.5f*scale,  0.5f, 0.0f, 0.0f, 1.0f,  0.0f, 0.0f,
        0.5f*scale, -0.5f*scale,  0.5f*scale,  0.5f, 0.0f, 0.0f, 1.0f,  1.0f, 0.0f,
        0.5f*scale, -0.5f*scale, -0.5f*scale,  0.5f, 0.0f, 0.0f, 1.0f,  1.0f, 1.0f,
        -0.5f*scale, -0.5f*scale, -0.5f*scale,  0.5f, 0.0f, 0.0f, 1.0f,  0.0f, 1.0f
    };

    // 索引数据保持不变
    unsigned int indices[] = {
        0, 1, 2, 2, 3, 0,
        4, 5, 6, 6, 7, 4,
        8, 9, 10, 10, 11, 8,
        12, 13, 14, 14, 15, 12,
        16, 17, 18, 18, 19, 16,
        20, 21, 22, 22, 23, 20
    };

    // 初始化 VAO, VBO 和 EBO
    vao.create();
    vbo.create();
    ebo.create();

    vao.bind();
    vbo.bind();
    vbo.allocate(vertices, sizeof(vertices));
    ebo.bind();
    ebo.allocate(indices, sizeof(indices));

    // 顶点着色器
    const char *vertexShaderSource = R"(
        #version 330 core
        layout(location = 0) in vec3 aPos;
        layout(location = 1) in vec4 aColor;
        layout(location = 2) in vec2 aTexCoord;
        out vec4 ourColor;
        out vec2 TexCoord;
        uniform mat4 model;
        uniform mat4 view;
        uniform mat4 projection;
        void main()
        {
            gl_Position = projection * view * model * vec4(aPos, 1.0);
            ourColor = aColor;
            TexCoord = aTexCoord;
        }
    )";

    // 片段着色器
    const char *fragmentShaderSource = R"(
        #version 330 core
        in vec4 ourColor;
        in vec2 TexCoord;
        out vec4 FragColor;
        uniform sampler2D texture1;
        void main()
        {
            vec4 texColor = texture(texture1, TexCoord);
            FragColor = texColor * ourColor;
        }
    )";

    // 编译着色器
    shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
    shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
    if (!shaderProgram.link()) {
        qDebug() << "Shader Program Link Error:" << shaderProgram.log();
        return;
    }

    shaderProgram.bind();

    // 设置顶点属性指针
    shaderProgram.setAttributeBuffer(0, GL_FLOAT, 0, 3, 9 * sizeof(float));
    shaderProgram.enableAttributeArray(0);
    shaderProgram.setAttributeBuffer(1, GL_FLOAT, 3 * sizeof(float), 4, 9 * sizeof(float));
    shaderProgram.enableAttributeArray(1);
    shaderProgram.setAttributeBuffer(2, GL_FLOAT, 7 * sizeof(float), 2, 9 * sizeof(float));
    shaderProgram.enableAttributeArray(2);

    // 加载纹理
    QImage img(":/textures/test.jpeg");
    if (img.isNull()) {
        qDebug() << "Failed to load texture image!";
        img = QImage(2, 2, QImage::Format_RGB32);
        img.fill(Qt::red);
    }

    texture = new QOpenGLTexture(img.mirrored());
    texture->setMinificationFilter(QOpenGLTexture::Linear);
    texture->setMagnificationFilter(QOpenGLTexture::Linear);
    texture->setWrapMode(QOpenGLTexture::Repeat);

    shaderProgram.setUniformValue("texture1", 0);

    vao.release();
    shaderProgram.release();
}

void OpenGLWidget::resizeGL(int w, int h)
{
    glViewport(0, 0, w, h);
}

void OpenGLWidget::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    shaderProgram.bind();
    vao.bind();
    texture->bind(0);

    QMatrix4x4 model;
    QMatrix4x4 view;
    QMatrix4x4 projection;

    model.rotate(xRot, 1.0f, 0.0f, 0.0f);
    model.rotate(yRot, 0.0f, 1.0f, 0.0f);
    model.rotate(zRot, 0.0f, 0.0f, 1.0f);
    view.translate(0.0f, 0.0f, zoom);
    projection.perspective(45.0f, width() / float(height()), 0.1f, 100.0f);

    shaderProgram.setUniformValue("model", model);
    shaderProgram.setUniformValue("view", view);
    shaderProgram.setUniformValue("projection", projection);

    glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);

    texture->release();
    vao.release();
    shaderProgram.release();

    zRot += 0.5f;
    if (zRot > 360.0f) zRot -= 360.0f;
    //update();
}

void OpenGLWidget::mousePressEvent(QMouseEvent *event)
{
    lastMousePos = event->pos();
}

void OpenGLWidget::mouseMoveEvent(QMouseEvent *event)
{
    if (event->buttons() & Qt::LeftButton) {
        float dx = event->pos().x() - lastMousePos.x();
        float dy = event->pos().y() - lastMousePos.y();
        xRot += dy;
        yRot += dx;
        lastMousePos = event->pos();
        update();
    }
}

void OpenGLWidget::wheelEvent(QWheelEvent *event)
{
    float delta = event->angleDelta().y() / 120.0f;
    zoom += delta * 0.5f;
    zoom = qBound(-10.0f, zoom, -1.0f);
    update();
}

3. 渲染 3D 立方体

准备顶点数据

复制代码
  // 立方体顶点数据 (位置 + 颜色 + 纹理坐标)
    float scale = 2.0f;
    float vertices[] = {
        // 前面
        -0.5f*scale, -0.5f*scale,  0.5f*scale,  1.0f, 0.0f, 0.0f, 1.0f,  0.0f, 0.0f,
        0.5f*scale, -0.5f*scale,  0.5f*scale,  0.0f, 1.0f, 0.0f, 1.0f,  1.0f, 0.0f,
        0.5f*scale,  0.5f*scale,  0.5f*scale,  0.0f, 0.0f, 1.0f, 1.0f,  1.0f, 1.0f,
        -0.5f*scale,  0.5f*scale,  0.5f*scale,  1.0f, 1.0f, 0.0f, 1.0f,  0.0f, 1.0f,

        // 后面
        -0.5f*scale, -0.5f*scale, -0.5f*scale,  1.0f, 0.0f, 1.0f, 1.0f,  1.0f, 0.0f,
        0.5f*scale, -0.5f*scale, -0.5f*scale,  0.0f, 1.0f, 1.0f, 1.0f,  0.0f, 0.0f,
        0.5f*scale,  0.5f*scale, -0.5f*scale,  1.0f, 1.0f, 1.0f, 1.0f,  0.0f, 1.0f,
        -0.5f*scale,  0.5f*scale, -0.5f*scale,  0.5f, 0.5f, 0.5f, 1.0f,  1.0f, 1.0f,

        // 左面
        -0.5f*scale, -0.5f*scale, -0.5f*scale,  0.6f, 0.0f, 0.0f, 1.0f,  0.0f, 0.0f,
        -0.5f*scale, -0.5f*scale,  0.5f*scale,  0.6f, 0.0f, 0.0f, 1.0f,  1.0f, 0.0f,
        -0.5f*scale,  0.5f*scale,  0.5f*scale,  0.6f, 0.0f, 0.0f, 1.0f,  1.0f, 1.0f,
        -0.5f*scale,  0.5f*scale, -0.5f*scale,  0.6f, 0.0f, 0.0f, 1.0f,  0.0f, 1.0f,

        // 右面
        0.5f*scale, -0.5f*scale, -0.5f*scale,  1.0f, 0.2f, 0.2f, 1.0f,  1.0f, 0.0f,
        0.5f*scale, -0.5f*scale,  0.5f*scale,  1.0f, 0.2f, 0.2f, 1.0f,  0.0f, 0.0f,
        0.5f*scale,  0.5f*scale,  0.5f*scale,  1.0f, 0.2f, 0.2f, 1.0f,  0.0f, 1.0f,
        0.5f*scale,  0.5f*scale, -0.5f*scale,  1.0f, 0.2f, 0.2f, 1.0f,  1.0f, 1.0f,

        // 顶面
        -0.5f*scale,  0.5f*scale,  0.5f*scale,  1.0f, 0.1f, 0.1f, 1.0f,  0.0f, 0.0f,
        0.5f*scale,  0.5f*scale,  0.5f*scale,  1.0f, 0.1f, 0.1f, 1.0f,  1.0f, 0.0f,
        0.5f*scale,  0.5f*scale, -0.5f*scale,  1.0f, 0.1f, 0.1f, 1.0f,  1.0f, 1.0f,
        -0.5f*scale,  0.5f*scale, -0.5f*scale,  1.0f, 0.1f, 0.1f, 1.0f,  0.0f, 1.0f,

        // 底面
        -0.5f*scale, -0.5f*scale,  0.5f*scale,  0.5f, 0.0f, 0.0f, 1.0f,  0.0f, 0.0f,
        0.5f*scale, -0.5f*scale,  0.5f*scale,  0.5f, 0.0f, 0.0f, 1.0f,  1.0f, 0.0f,
        0.5f*scale, -0.5f*scale, -0.5f*scale,  0.5f, 0.0f, 0.0f, 1.0f,  1.0f, 1.0f,
        -0.5f*scale, -0.5f*scale, -0.5f*scale,  0.5f, 0.0f, 0.0f, 1.0f,  0.0f, 1.0f
    };

    // 索引数据保持不变
    unsigned int indices[] = {
        0, 1, 2, 2, 3, 0,
        4, 5, 6, 6, 7, 4,
        8, 9, 10, 10, 11, 8,
        12, 13, 14, 14, 15, 12,
        16, 17, 18, 18, 19, 16,
        20, 21, 22, 22, 23, 20
    };

初始化 VAO, VBO 和 EBO

复制代码
    // 初始化 VAO, VBO 和 EBO
    vao.create();
    vbo.create();
    ebo.create();

    vao.bind();
    vbo.bind();
    vbo.allocate(vertices, sizeof(vertices));
    ebo.bind();
    ebo.allocate(indices, sizeof(indices));

创建着色器程序

复制代码
 // 顶点着色器
    const char *vertexShaderSource = R"(
        #version 330 core
        layout(location = 0) in vec3 aPos;
        layout(location = 1) in vec4 aColor;
        layout(location = 2) in vec2 aTexCoord;
        out vec4 ourColor;
        out vec2 TexCoord;
        uniform mat4 model;
        uniform mat4 view;
        uniform mat4 projection;
        void main()
        {
            gl_Position = projection * view * model * vec4(aPos, 1.0);
            ourColor = aColor;
            TexCoord = aTexCoord;
        }
    )";

    // 编译着色器
    shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);

 // 片段着色器
    const char *fragmentShaderSource = R"(
        #version 330 core
        in vec4 ourColor;
        in vec2 TexCoord;
        out vec4 FragColor;
        uniform sampler2D texture1;
        void main()
        {
            vec4 texColor = texture(texture1, TexCoord);
            FragColor = texColor * ourColor;
        }
    )";

 shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);

4. 实现 3D 渲染

复制代码
void OpenGLWidget::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    shaderProgram.bind();
    vao.bind();
    texture->bind(0);

    QMatrix4x4 model;
    QMatrix4x4 view;
    QMatrix4x4 projection;

    model.rotate(xRot, 1.0f, 0.0f, 0.0f);
    model.rotate(yRot, 0.0f, 1.0f, 0.0f);
    model.rotate(zRot, 0.0f, 0.0f, 1.0f);
    view.translate(0.0f, 0.0f, zoom);
    projection.perspective(45.0f, width() / float(height()), 0.1f, 100.0f);

    shaderProgram.setUniformValue("model", model);
    shaderProgram.setUniformValue("view", view);
    shaderProgram.setUniformValue("projection", projection);

    glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);

    texture->release();
    vao.release();
    shaderProgram.release();

    zRot += 0.5f;
    if (zRot > 360.0f) zRot -= 360.0f;
}

5. 添加纹理

加载纹理

复制代码
 // 加载纹理
    QImage img(":/textures/test.jpeg");
    if (img.isNull()) {
        qDebug() << "Failed to load texture image!";
        img = QImage(2, 2, QImage::Format_RGB32);
        img.fill(Qt::red);
    }

    texture = new QOpenGLTexture(img.mirrored());
    texture->setMinificationFilter(QOpenGLTexture::Linear);
    texture->setMagnificationFilter(QOpenGLTexture::Linear);
    texture->setWrapMode(QOpenGLTexture::Repeat);

    shaderProgram.setUniformValue("texture1", 0);

更新顶点属性指针

复制代码
    // 设置顶点属性指针
    shaderProgram.setAttributeBuffer(0, GL_FLOAT, 0, 3, 9 * sizeof(float));
    shaderProgram.enableAttributeArray(0);
    shaderProgram.setAttributeBuffer(1, GL_FLOAT, 3 * sizeof(float), 4, 9 * sizeof(float));
    shaderProgram.enableAttributeArray(1);
    shaderProgram.setAttributeBuffer(2, GL_FLOAT, 7 * sizeof(float), 2, 9 * sizeof(float));
    shaderProgram.enableAttributeArray(2);

完整工程代码:

https://gitee.com/byxdaz/opengl3d-instance

运行效果图:

相关推荐
杨了个杨898210 小时前
memcached部署
qt·websocket·memcached
无小道12 小时前
Qt-qrc机制简单介绍
开发语言·qt
zhooyu12 小时前
C++和OpenGL手搓3D游戏编程(20160207进展和效果)
开发语言·c++·游戏·3d·opengl
CodeKwang13 小时前
Qt实战:简易Excel表格 | 附完整源码
qt·excel·qtabwidget·qt控件
C++ 老炮儿的技术栈16 小时前
Qt 编写 TcpClient 程序 详细步骤
c语言·开发语言·数据库·c++·qt·算法
打工哪有不疯的16 小时前
使用 MSYS2 为 Qt (MinGW 32/64位) 完美配置 OpenSSL
c++·qt
LYOBOYI12318 小时前
qtcpSocket详解
c++·qt
无小道20 小时前
Qt——网络编程
开发语言·qt
云中飞鸿20 小时前
VS编写QT程序,如何向linux中移植?
linux·开发语言·qt
草莓熊Lotso21 小时前
Qt 主窗口核心组件实战:菜单栏、工具栏、状态栏、浮动窗口全攻略
运维·开发语言·人工智能·python·qt·ui