初始Opengl之QT
- 1. OpenGL 概述
- 2. QT 項目
1. OpenGL 概述
OpenGL
是一個規範,此規範有各個廠家最終實作。
OpenGL
的對外展現就是一個庫的概念。
OpenGL
現在分為核心模式和立即渲染模式(即固定的渲染管線),核心模式是
OpenGL 3.3
開始支援的,核心模式指可程式設計模式,核心模式的渲染管線(其實就是從一系列資料到最終配置圖像的流程)如下:
其中
頂點着色器
和
片段着色器
需要程式員自己實作。
若要寫一個
OpenGL
的可視化程式則需要另外兩個庫的輔助
GLFW
和
GLAD
。
-
的作用是用來解決圖形界面顯示、消息處理及使用者輸入處理。GLFW
-
則使得代碼可以用于不同的GLAD
驅動,即對OpenGL
做的封裝,使得不同系統的上層使用可以更簡便的使用OpenGL
。OpenGL
可以借助
QT
對
OpenGL
的封裝,實作無需配置第三方庫的簡便開發。
CPU
資料如何與
GPU
資料互動,我們在程式中書寫的頂點資料是在
CPU
中的,如果
GPU
要使用這些資料,我們應該明确的告訴
GPU
應該怎麼使用。這其中涉及到
VBO(Vertex Buffer Objects)
、
VAO(Vertex Array Objects)
及
VEO(Vertex Element Objects)
,其中
VEO
也被稱為
VAO(Vertex Index Objects)
-
: 用來在VBO
中建立記憶體并存儲頂點資料,頂點緩沖對象類型為GPU
GL_ARRAY_BUFFER
-
: 用來配置VAO
如果解析這些記憶體,OpenGL
中存儲的是VAO
資料類型的定義,而不存儲資料本身VBO
-
: 它用來存儲頂點資料的索引,繪圖時根據索引進行繪制VEO
、VBO
及VAO
的關系:VEO
2. QT 項目
UI界面:
黑色視窗為
OpenGL Widget
,這裡對其進行了提升即修改了其繼承的父類為自定的
MyOpenGLWidget
,在控件中右鍵選擇
提升
即可操作界面如下:
myopenglwidget.h内容如下:
#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
class MyOpenGLWidget : public QOpenGLWidget,QOpenGLFunctions_3_3_Core
{
Q_OBJECT
public:
explicit MyOpenGLWidget(QWidget *parent = nullptr);
~MyOpenGLWidget();
protected:
virtual void initializeGL() override;
virtual void paintGL() override;
signals:
private:
GLuint m_VBO;
GLuint m_VAO;
// 這裡使用非靜态變量glCreateProgram會報錯,目前還不清楚具體的是為什麼(也可能是我環境本身的問題)
static GLuint m_shaderProgram;
// 這裡使用非靜态變量報錯,目前還不清楚具體的是為什麼(也可能是我環境本身的問題)
static GLuint m_EBO;
};
#endif // MYOPENGLWIDGET_H
myopenglwidget.cpp内容如下:
#include "myopenglwidget.h"
#include <QDebug>
GLuint MyOpenGLWidget::m_shaderProgram = 0;
GLuint MyOpenGLWidget::m_EBO = 0;
MyOpenGLWidget::MyOpenGLWidget(QWidget *parent) : QOpenGLWidget(parent)
{
}
MyOpenGLWidget::~MyOpenGLWidget()
{
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
}
GLfloat vertex[]={
-0.5f,-0.5f,0.0f,
0.5f,-0.5f,0.0f,
0.0f,0.5f,0.0f
};
// 存在重複的坐标點
//GLfloat vertexRectangle[]={
// 0.5f,0.5f,0.0f,
// 0.5f,-0.5f,0.0f,
// -0.5f,0.5f,0.0f,
// 0.5f,-0.5f,0.0f,
// -0.5f,-0.5f,0.0f,
// -0.5f,0.5f,0.0f
//};
GLfloat vertexRectangle[]={
0.5f,0.5f,0.0f,
0.5f,-0.5f,0.0f,
-0.5f,-0.5f,0.0f,
-0.5f,0.5f,0.0f
};
GLuint index[]={
0,1,3,
1,2,3
};
const char* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos,1.0f);\n"
"}\n\0";
const char* fragmentShaderSource="#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f,0.5f,0.2f,1.0f);\n"
"}\n\0";
void MyOpenGLWidget::initializeGL()
{
/*
* 初始化OpenGL的函數接口
* 這個接口不能寫在構造函數中,否則會程式運作出錯,因為initializeOpenGLFunctions()中會需要視窗句柄,而視窗句柄在構造函數完成之後才會被建立
*/
initializeOpenGLFunctions();
// 建立VAO
glGenVertexArrays(1,&m_VAO);
// 建立VBO
glGenBuffers(1,&m_VBO);
// 綁定VAO和VBO對象
glBindVertexArray(m_VAO);
glBindBuffer(GL_ARRAY_BUFFER,m_VBO);
// 為緩沖對象建立一個新的資料存儲
glBufferData(GL_ARRAY_BUFFER,sizeof(vertexRectangle),vertexRectangle,GL_STATIC_DRAW);
// 告訴先看如何解析緩沖裡的屬性值
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,3 * sizeof(GLfloat),(void*)0);
// 開啟VAO管理的第一個屬性值
glEnableVertexAttribArray(0);
// 建立EBO,必須在VAO解綁之前建立,因為EBO也會被VAO所記錄,但是解綁VBO卻不會對VAO中的記錄産生影響
glGenBuffers(1,&m_EBO);
// 綁定EBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,m_EBO);
// 給EBO配置設定空間,并存儲資料
glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(index),index,GL_STATIC_DRAW);
// 解除EBO,若此處解綁EBO則VAO中就會不在記錄EBO内容,需要在使用的時候重新綁定EBO,這裡放在析構函數中解綁
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
// 解除VBO和VAO的綁定
glBindBuffer(GL_ARRAY_BUFFER,0);
glBindVertexArray(0);
// 建立頂點着色器
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
// 綁定着色器
glShaderSource(vertexShader,1,&vertexShaderSource,NULL);
// 編譯頂點着色器
glCompileShader(vertexShader);
// 建立片段着色器
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader,1,&fragmentShaderSource,NULL);
// 編譯片段着色器
glCompileShader(fragmentShader);
// 連結 shaders
m_shaderProgram = glCreateProgram();
glAttachShader(m_shaderProgram,vertexShader);
glAttachShader(m_shaderProgram,fragmentShader);
glLinkProgram(m_shaderProgram);
//釋放
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// 指定繪圖的模式,這裡設定為GL_LINE,此模式隻繪制線框而不進行填充
glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
}
void MyOpenGLWidget::paintGL()
{
// 設定Clear的屬性
glClearColor(0.2f,0.3f,0.3f,1.0f);
// 使用
glClear(GL_COLOR_BUFFER_BIT);
// 使用shader
glUseProgram(m_shaderProgram);
// 綁定VAO
glBindVertexArray(m_VAO);
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,m_EBO);
// 繪制圖形
//glDrawArrays(GL_TRIANGLES,0,3);
/*
* 根據索引繪制
* 若沒有建立EBO,最後一個參數可以直接傳遞索引的數組
*/
glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,0/*&index*/);
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
// 解除VAO
glBindVertexArray(0);
}
mainwindow.cpp内容如下:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
setCentralWidget(ui->openGLWidget);
}
MainWindow::~MainWindow()
{
delete ui;
}
其他代碼均為存在修改,運作效果如下: