項目結構
首先,你需要在 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_OBJECTpublic: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, // 頂點21.0f, -1.0f, 0.0f // 頂點3};// 3.2 創建 VAO 和 VBOglGenVertexArrays(1, &VAO); // 創建 VAOglBindVertexArray(VAO); // 綁定 VAOglGenBuffers(1, &VBO); // 創建 VBOglBindBuffer(GL_ARRAY_BUFFER, VBO); // 綁定 VBOglBufferData(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(); // 釋放 VBOglBindVertexArray(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博客