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

运行效果图:

相关推荐
啊呦.超能力2 小时前
QT开发---多线程编程
开发语言·qt
程序员编程指南4 小时前
Qt 与 SQLite 嵌入式数据库开发
c语言·数据库·c++·qt
fyzy5 小时前
qt编译时一直循环报错打印-spec win32-g++ “CONFIG+=debug“ “CONFIG+=qml_debug“
qt
程序员编程指南10 小时前
Qt 网络编程进阶:WebSocket 通信
c语言·网络·c++·qt·websocket
不断努力的根号七19 小时前
qt框架,使用webEngine如何调试前端
开发语言·前端·qt
范纹杉想快点毕业1 天前
基于C语言的Zynq SOC FPGA嵌入式裸机设计和开发教程
c语言·开发语言·数据库·嵌入式硬件·qt·fpga开发·嵌入式实时数据库
程序员编程指南1 天前
Qt容器类:QList、QMap等的高效使用
c语言·开发语言·c++·qt
程序员编程指南1 天前
Qt 元对象系统(Meta-Object System)解析
c语言·开发语言·c++·qt
XXYBMOOO1 天前
使用全局变量访问 Qt UI 组件的方法文档
c++·qt·ui
kaikaile19951 天前
基于Qt的仿QQ聊天系统设计
开发语言·qt