完整例子和调用关系qt OpenGL
项目结构
首先,你需要在 Qt 项目中创建一个类,继承自 QOpenGLWidget 来进行 OpenGL 渲染。文件结构如下:
- main.cpp
- MyOpenGLWidget.h
- MyOpenGLWidget.cpp
- vertex_shader.glsl
- fragment_shader.glsl
1. main.cpp
这是 Qt 项目的入口文件,创建一个 QApplication 实例并显示一个包含 QOpenGLWidget 的窗口。
// 包含 Qt 应用程序的核心类,用于管理 Qt 应用程序的控制流。
#include <QApplication>
// 包含 Qt 的主窗口类,可以创建一个包含菜单、工具栏等常见组件的窗口。
#include <QMainWindow>
// 包含我们自定义的 OpenGL 渲染窗口类。
#include "MyOpenGLWidget.h"
int main(int argc, char *argv[]) {
/* 初始化 Qt 应用程序。argc 和 argv 是命令行参数,通常用来传递命令行参数到程序中。
QApplication 是所有 Qt 应用程序的基础,它管理应用程序的生命周期和事件循环。
*/
QApplication app(argc, argv);
// 创建一个主窗口对象 window,它是应用程序的主界面。
QMainWindow window;
// 创建自定义的 OpenGL 渲染窗口 glWidget。
// &window 将主窗口 window 作为父窗口传递给 glWidget。
MyOpenGLWidget *glWidget = new MyOpenGLWidget(&window);
// 将 glWidget 设为 window 的中心部件。
// QMainWindow 中的中心部件通常占据主窗口的最大区域。
window.setCentralWidget(glWidget);
// 设置窗口大小
window.resize(800, 600);
// 显示窗口
window.show();
return app.exec();
}
2. MyOpenGLWidget.h
这是声明文件,继承自 QOpenGLWidget 和 QOpenGLFunctions,
用于处理 OpenGL 渲染。在这里,我们声明必要的 OpenGL 初始化、绘制和清理函数。
#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H
// 包含 Qt 的 OpenGL 小部件类,这样我们可以创建一个 OpenGL 渲染窗口。
#include <QOpenGLWidget>
// 包含 OpenGL 功能的接口。我们可以通过这个类访问 OpenGL 的核心功能。
#include <QOpenGLFunctions>
// 包含 OpenGL 着色器程序类,负责加载、编译、链接和管理着色器程序。
#include <QOpenGLShaderProgram>
// 包含 OpenGL 缓冲区类,负责创建和管理顶点缓冲对象(VBO)和索引缓冲对象(EBO)。
#include <QOpenGLBuffer>
/* 声明一个名为 MyOpenGLWidget 的类,继承自 QOpenGLWidget 和 QOpenGLFunctions。
QOpenGLWidget 是 Qt 提供的 OpenGL 渲染窗口基类,QOpenGLFunctions 提供了 OpenGL 的基本功能。
protected QOpenGLFunctions 是为了能够访问 OpenGL 的函数接口。
*/
class MyOpenGLWidget : public QOpenGLWidget, protected QOpenGLFunctions {
Q_OBJECT
public:
MyOpenGLWidget(QWidget *parent = nullptr);
~MyOpenGLWidget();
protected:
// 初始化 OpenGL 环境的函数。
// 在这里你会设置 OpenGL 的状态,例如开启深度测试、设置视口、加载着色器等。
void initializeGL() override;
// 当窗口大小改变时自动调用。你可以在这里设置 OpenGL 视口以及投影矩阵。
void resizeGL(int w, int h) override;
// 每次需要重新绘制时调用。在这里绘制 OpenGL 图形。
void paintGL() override;
private:
// 用于初始化并编译着色器的函数。
void initializeShaders();
// 用于初始化顶点数据和缓冲区的函数。
void initializeBuffers();
// 指向 OpenGL 着色器程序的指针。用于管理着色器的加载、编译、链接和绑定。
QOpenGLShaderProgram *shaderProgram;
// 一个 QOpenGLBuffer 对象,用于存储顶点数据的缓冲区(VBO)。
QOpenGLBuffer vertexBuffer;
// 顶点数组对象的句柄。VAO 用于管理顶点属性和缓冲区对象。
GLuint VAO, VBO;
};
#endif // MYOPENGLWIDGET_H
3. MyOpenGLWidget.cpp
这是实现文件,包含了 OpenGL 初始化、着色器编译、数据传输和渲染的具体代码。
#include "MyOpenGLWidget.h"
// 包含 OpenGL 着色器类,负责加载着色器代码并编译它们。
#include <QOpenGLShader>
#include <QOpenGLBuffer>
#include <QDebug>
MyOpenGLWidget::MyOpenGLWidget(QWidget *parent)
: QOpenGLWidget(parent),shaderProgram(nullptr)
{
}
MyOpenGLWidget::~MyOpenGLWidget() {
// 销毁 shaderProgram,释放着色器程序的内存。
delete shaderProgram;
}
void MyOpenGLWidget::initializeGL() {
// 1. 初始化 OpenGL 函数,确保可以使用 OpenGL 的所有函数。
initializeOpenGLFunctions();
// 启用深度测试,确保物体在 3D 场景中的正确显示(即前面物体遮挡后面物体)。
glEnable(GL_DEPTH_TEST);
// 2. 初始化着色器程序
initializeShaders();
// 3. 初始化顶点数据
initializeBuffers();
}
void MyOpenGLWidget::initializeShaders() {
// 2.1 创建并编译顶点着色器
// 创建一个新的着色器程序对象。
shaderProgram = new QOpenGLShaderProgram();
// 加载并编译顶点着色器,QOpenGLShader::Vertex 指明这是顶点着色器,文件路径为 :/vertex_shader.glsl。
shaderProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/vertex_shader.glsl");
// 加载并编译片段着色器,QOpenGLShader::Fragment 指明这是片段
shaderProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/fragment_shader.glsl");
// 2.2 链接着色器程序
shaderProgram->link();
shaderProgram->bind();
}
void MyOpenGLWidget::initializeBuffers() {
// 3.1 顶点数据(一个简单的三角形)
GLfloat vertices[] = {
-1.0f, 1.0f, 0.0f, // 顶点1
-1.0f, -1.0f, 0.0f, // 顶点2
1.0f, -1.0f, 0.0f // 顶点3
};
// 3.2 创建 VAO 和 VBO
glGenVertexArrays(1, &VAO); // 创建 VAO
glBindVertexArray(VAO); // 绑定 VAO
glGenBuffers(1, &VBO); // 创建 VBO
glBindBuffer(GL_ARRAY_BUFFER, VBO); // 绑定 VBO
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 分配数据给 VBO
// 3.3 设置顶点属性(位置)
// 启用顶点属性(位置属性)
shaderProgram->enableAttributeArray(0);
// 设置位置数据的格式
shaderProgram->setAttributeBuffer(0, GL_FLOAT, 0, 3, 3 * sizeof(GLfloat));
vertexBuffer.release(); // 释放 VBO
glBindVertexArray(0); // 解绑 VAO
}
void MyOpenGLWidget::resizeGL(int w, int h) {
// 4. 设置视口大小
glViewport(0, 0, w, h);
// 4.1 设置投影矩阵(透视投影)
glMatrixMode(GL_PROJECTION); // 设定当前矩阵为投影矩阵
glLoadIdentity(); // 重置当前矩阵
// 设置透视投影
gluPerspective(45.0, (GLfloat)w / (GLfloat)h, 0.1, 100.0);
// 切换回模型视图矩阵
glMatrixMode(GL_MODELVIEW);
}
void MyOpenGLWidget::paintGL() {
// 5. 清空屏幕并准备绘制
// 清空颜色和深度缓冲
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
// 5.1 使用着色器程序
shaderProgram->bind();
glBindVertexArray(VAO);
// 5.2 绘制三角形
// 使用顶点数据绘制三角形
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
shaderProgram->release();
}
4. vertex_shader.glsl
顶点着色器(将顶点位置传递给片段着色器):
#version 330 core
layout(location = 0) in vec3 position;
void main() {
gl_Position = vec4(position, 1.0); // 直接输出位置
}
5. fragment_shader.glsl
片段着色器(指定最终颜色):
#version 330 core
out vec4 color;
void main() {
color = vec4(1.0, 0.0, 0.0, 1.0); // 输出红色
}
函数作用说明
- initializeGL():负责 OpenGL 环境的初始化工作,包括启用深度测试、初始化着色器和顶点数据。这个函数会在 QOpenGLWidget 创建后自动调用。
- initializeShaders():编译顶点和片段着色器,并将它们链接成一个 OpenGL 程序。着色器代码通常存储在外部文件中,这里用 addShaderFromSourceFile() 来加载。
- initializeBuffers():创建并初始化顶点缓冲对象(VBO)和顶点数组对象(VAO)。VBO 存储顶点数据,而 VAO 用于管理 VBO 的状态。顶点数据传送到 GPU 后,OpenGL 会使用它来绘制物体
- resizeGL(int w, int h):用于处理 OpenGL 上下文的大小变化,设置视口以及投影矩阵,使得绘制的图形不会在窗口大小变化时失真。
- paintGL():这个函数每次需要重新绘制时都会被调用。这里,我们清空屏幕,使用着色器程序,并通过 glDrawArrays() 绘制三角形。
调用关系和逻辑
- 在程序启动时,QOpenGLWidget 的构造函数会被调用。QOpenGLWidget 会创建一个 OpenGL 上下文,并在需要时调用 initializeGL()。
- 在 initializeGL() 中,我们初始化 OpenGL 状态(如深度测试),然后调用 initializeShaders() 来加载并编译着色器程序,接着调用 initializeBuffers() 初始化顶点数据。
- 每当窗口大小改变时,resizeGL() 会被自动调用来调整视口大小和设置合适的投影矩阵。
- 每次需要绘制时(例如每帧刷新时),paintGL() 会被调用。在此函数中,我们绑定着色器程序、顶点数组对象(VAO),并通过 glDrawArrays() 来绘制三角形。
总结
- initializeGL() -> 调用 initializeShaders() 初始化着色器,调用 initializeBuffers() 初始化顶点数据。
- paintGL() -> 每次绘制时使用着色器和顶点数据。
- resizeGL() -> 当窗口大小变化时调整视口和投影矩阵。
疑惑补充:
QOpenGLShaderProgram 对象-CSDN博客
OpenGL疑惑-CSDN博客