当前位置: 首页 > news >正文

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 和 EBOvao.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 corelayout(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 corein 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 和 EBOvao.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 corelayout(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 corein 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 Studying《Effective STL》
  • 使用ArcPy批量处理矢量数据
  • inux系统基本操作命令(系统信息查看)
  • MyBatis04:SpringBoot整合MyBatis——多表关联|延迟加载|MyBatisX插件|SQL注解
  • Linux 基础指令入门指南:解锁命令行的实用密码
  • 常见 Web 安全问题
  • MySQL中的锁
  • ESP32之Linux编译环境搭建流程
  • webfuture:提示“Strict-Transport-Security头未设置”漏洞的解决方法
  • 在树莓派3B上用Python编程完成流水灯实验
  • 【更正补全】edu教育申请通过方案
  • UE5 创建2D角色帧动画学习笔记
  • IO模型IO模型
  • 房屋租赁系统 Java+Vue.js+SpringBoot,包括房屋类型、房屋信息、预约看房、合同信息、房屋报修、房屋评价、房主管理模块
  • 计算机组成原理核心剖析:CPU、存储、I/O 与总线系统全解
  • PCIe-Error Detection(一)
  • 花卉目标检测数据集介绍(共 12 类,10490 张图像)
  • ⚡️ Linux grep 命令参数详解
  • JavaSE 字符串:深入解析 String、StringBuilder与 StringBuffer
  • Playwright 测试框架 - Node.js
  • 嘉兴本地推广网站/网站seo外包公司有哪些
  • 有做微信婚介网站的吗/长春网站优化方案
  • 设计参考网站有哪些/网站交易平台
  • 公司网站改版设计/关键词提取工具app
  • 建德网站制作公司/软文推广哪个平台好
  • 校园网站建设用什么软件写/seo是怎么优化的