一、QOpenGLShaderProgram 编译过程的封装
1 、bool addShaderFromSourceCode(QGLShader::ShaderType type, const char * source);
2 、bool addShaderFromSourceFile(QGLShader::ShaderType type, const QString & fileName);
3 、virtual bool link();
4 、bool bind();
5 、QString log() const;
6 、一般将顶点着色器和片段着色器源码放入文件,后缀分别为.vert和.frag。
7 、着色器文件可以放入工程目录中,但更推荐放入Qt资源文件中。
8、为了在Qt中正常加载并编辑shader文件,shader文件设置编码为: UTF-8。
二、代码示例
cpp
//MyOpenGLWidget.h
#pragma once
//需要两个头文件
#include <QOpenGLWidget>
#include <QOpenGLFunctions_4_5_Core>
#include <QOpenGLShaderProgram>
class MyOpenGLWidget : public QOpenGLWidget, QOpenGLFunctions_4_5_Core
{
Q_OBJECT
public:
MyOpenGLWidget(QWidget * parent = nullptr);
~MyOpenGLWidget();
enum Shape { None, Rect, Circle, Triangle };
void DrawShape(Shape shape);
void WireFrame(bool bWire);
protected:
void initializeGL() override;
void resizeGL(int w, int h) override;
void paintGL() override;
private:
unsigned int VAO;
unsigned int VBO;
unsigned int EBO;
Shape m_shape = None;
QOpenGLShaderProgram m_shaderProgram;
};
//MyOpenGLWidget.cpp
#include "MyOpenGLWidget.h"
#include <QDebug>
MyOpenGLWidget::MyOpenGLWidget(QWidget * parent) : QOpenGLWidget(parent)
{
}
MyOpenGLWidget::~MyOpenGLWidget()
{
makeCurrent();
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
glDeleteVertexArrays(1, &VAO);
doneCurrent();
}
void MyOpenGLWidget::DrawShape(Shape shape)
{
//触发重新绘制
m_shape = shape;
update();
}
void MyOpenGLWidget::WireFrame(bool bWire)
{
makeCurrent();
if (bWire)
{
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
else
{
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
doneCurrent();
update();
}
void MyOpenGLWidget::initializeGL()
{
initializeOpenGLFunctions();
//创建VAO、VBO、EBO,并赋予ID
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
//绑定VAO、VBO、EBO对象
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); //注意, 因为VAO已经绑定,此句会被VAO的最后一个指针偷偷记录下来, 指向EBO。
//顶点数据
float vertices[] =
{
//位置 //颜色
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 右上角
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 右下角
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, // 左下角
-0.5f, 0.5f, 0.0f, 0.3f, 0.5f, 0.8f, // 左上角
};
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//索引数据
unsigned int indices[] =
{
0, 1, 3, // 第一个三角形
1, 2, 3 // 第二个三角形
};
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
//配置VAO位置属性指针
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0);
//配置VAO颜色属性指针
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void *)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
//解绑VAO、VBO、EBO
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); //注意,因为VAO已经解绑了,所以此句不会被VAO记录下来,VAO的最后一个指针依然指向EBO,这样好处是绘制前,只要再绑定VAO即可,EBO、VBO都不用再绑定。
//着色器程序
m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/1_shader.vert");
m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/1_shader.frag");
bool success = m_shaderProgram.link();
if (!success)
{
qDebug() << "ERROR: " << m_shaderProgram.log();
}
}
void MyOpenGLWidget::resizeGL(int w, int h)
{
}
//绘制都是在此函数中,其它地方都是触发绘制
void MyOpenGLWidget::paintGL()
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArray(VAO);
m_shaderProgram.bind();
switch (m_shape)
{
case MyOpenGLWidget::None:
break;
case MyOpenGLWidget::Rect:
{
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
break;
}
case MyOpenGLWidget::Circle:
break;
case MyOpenGLWidget::Triangle:
break;
default:
break;
}
}
//MainWindow.h
#pragma once
#include <QtWidgets/QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindowClass; };
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget * parent = nullptr);
~MainWindow();
private:
void ActDrawRectTriggered(bool checked);
void ActClearTriggered(bool checked);
void ActWireFrameTriggered(bool checked);
private:
Ui::MainWindowClass * ui;
};
//MainWindow.cpp
#include "MainWindow.h"
#include "ui_MainWindow.h"
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindowClass())
{
ui->setupUi(this);
setCentralWidget(ui->openGLWidget);
connect(ui->actDrawRect, &QAction::triggered, this, &MainWindow::ActDrawRectTriggered);
connect(ui->actClear, &QAction::triggered, this, &MainWindow::ActClearTriggered);
connect(ui->actWireFrame, &QAction::triggered, this, &MainWindow::ActWireFrameTriggered);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::ActDrawRectTriggered(bool checked)
{
ui->openGLWidget->DrawShape(MaLanOpenGLWidget::Rect);
}
void MainWindow::ActClearTriggered(bool checked)
{
ui->openGLWidget->DrawShape(MaLanOpenGLWidget::None);
}
void MainWindow::ActWireFrameTriggered(bool checked)
{
ui->openGLWidget->WireFrame(checked);
}
//1_shader.vert
#version 330 core
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec3 aColor;
out vec3 ourColor;
void main()
{
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
ourColor = aColor;
}
//1_shader.frag
#version 330 core
in vec3 ourColor;
out vec4 FragColor;
void main()
{
FragColor = vec4(ourColor, 1.0);
}
