这篇文章主要是介绍一些被qt封装之后的函数,相比openGl的原始函数更加简单。
要绘制一个立方体首先需要顶点坐标和索引坐标
QOpenGLBuffer arrayBuf;
QOpenGLBuffer indexBuf;
需要着色器管理类
QOpenGLShaderProgram m_shaderProgram;
需要openGl核心函数
QOpenGLFunctions_3_3_Core * m_core;
在定义好这些变量之后,需要能显示openGL的窗口类 QOpenGLWidget,有三个关键性的函数需要重载。
void initializeGL() override;
void resizeGL(int w, int h) override;
void paintGL() override;
1 initializeGL() 需要初始化一些参数
m_core = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_3_Core>();
m_core->initializeOpenGLFunctions(); //为当前上下文初始化提供OpenGL函数解析
initCube();
// 创建顶点着色器
m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/vertexShader.vert");
// 创建片段着色器
m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/fragShader.frag");
// 链接 shaders
m_shaderProgram.link();
int vertexLocation = m_shaderProgram.attributeLocation("aPos");
m_shaderProgram.enableAttributeArray(vertexLocation);
m_shaderProgram.setAttributeBuffer(vertexLocation, GL_FLOAT, 0, 3, 3 * sizeof(GLfloat));
// 指定绘图的模式,这里设定为GL_LINE,此模式只绘制线框而不进行填充
m_core->glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
下面是initCube()函数的代码段,这个函数是为了初始化顶点数组和索引数组。
arrayBuf.create(); // 创建顶点数组
indexBuf.create(); // 创建索引数组
normalize(m_vertexBuffer); // 对输入的数据进行归一化,在这里绑定顶点数组
GLuint index[]={
0,1,2,0,3,2, // 后
4,5,7,5,6,7, // 前
4,5,0,0,1,5, // 左
2,6,7,2,3,7, // 右
1,5,6,6,2,1, // 上
0,4,7,0,3,7 // 下
};
// Transfer index data to VBO 1
indexBuf.bind();
indexBuf.allocate(index, 36 * sizeof(GLuint)); // 把索引数据给indexBuf
下面是normalize()函数的代码段
void MainWindow::normalize(QVector<int> data)
{
int max=0,min=0;
for(int i=0;i<data.size();i++){
if(max <data.at(i)) max = data.at(i);
if(min > data.at(i)) min = data.at(i);
}
GLfloat *vertexArr = (GLfloat *)malloc(sizeof(GLfloat)*data.size());
// 将最大映射到1,最小映射到0
float scale = 1.0/(max-min);
for(int i=0;i<data.size();i++){
vertexArr[i] = (data[i]-min)*scale +min;
}
// Transfer vertex data to VBO 0
arrayBuf.bind();
arrayBuf.allocate(vertexArr, 24 * sizeof(vertexArr));
}
在QT里索引坐标和顶点数据不需要繁琐的创建绑定连接了。只需要create创建之后再用的地方之前bind()一下。传数据的时候用allocate进行分配内存。
着色器的创建绑定连接也只需要一个函数就能添加。
2 paintGL()进行对数据进行绘制
void MainWindow::paintGL()
{
QTime time;
time.start();
QMatrix4x4 view;
QMatrix4x4 projection;
projection.perspective(50,width()/height(),0.1,100);
view.translate(0.0,0.0,-3);
// 设置Clear的属性
m_core->glClearColor(0.1f,0.5f,0.3f,1.0f);
// 使用
m_core->glEnable(GL_DEPTH_TEST);
m_core->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 使用shader
m_shaderProgram.bind();
// 使用顶点和索引数据
arrayBuf.bind();
indexBuf.bind();
switch (m_shap) {
case Rect:
m_shaderProgram.setUniformValue("view",view);
m_shaderProgram.setUniformValue("projection",projection);
// 6个点可以画一个面,每个面给不同的颜色
m_shaderProgram.setUniformValue("timeColor",0.4f,0.6f,0.0f,1.0f);
m_core->glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,0/*&index*/);
m_shaderProgram.setUniformValue("timeColor",0.1f,0.7f,0.3f,1.0f);
m_core->glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,(GLvoid*)(6*sizeof (GLuint))/*&index*/);
m_shaderProgram.setUniformValue("timeColor",0.2f,0.3f,0.6f,1.0f);
m_core->glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,(GLvoid*)(12*sizeof (GLuint))/*&index*/);
m_shaderProgram.setUniformValue("timeColor",0.8f,0.46f,0.2f,1.0f);
m_core->glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,(void*)(18*sizeof (GLuint))/*&index*/);
m_shaderProgram.setUniformValue("timeColor",0.4f,0.2f,0.9f,1.0f);
m_core->glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,(void*)(24*sizeof (GLuint))/*&index*/);
m_shaderProgram.setUniformValue("timeColor",0.1f,0.4f,0.7f,1.0f);
m_core->glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,(void*)(30*sizeof (GLuint))/*&index*/);
break;
default:
break;
}
qDebug("paintGL %d ms",time.elapsed());
}
3 设置定时器进行旋转
定时写在了timerEvent里面不用多说,要旋转则需要openGL知识不多介绍只需要知道几个矩阵相乘运算之后可以得到旋转的效果,在QT里对矩阵的运算也进行了封装。下面是代码
void MainWindow::timerEvent(QTimerEvent *ev)
{
QTime time;
time.start();
makeCurrent();
int timeValue = QTime::currentTime().second();
// float colorValue = (sin(timeValue)/2.0f) + 0.5f;
// m_shaderProgram.setUniformValue("timeColor",0.0f,colorValue,0.0f,1.0f);
QMatrix4x4 model;
model.rotate(timeValue*10,0.7f,0.1f,0.4f);
m_shaderProgram.setUniformValue("model",model);
doneCurrent();
update();
qDebug("timerEvent %d ms",time.elapsed());
}
4 frag文件和vert文件
这两个文件来控制流水线里顶点坐标的运算和着色器处理方法
下面是frag文件
#version 330 core out vec4 FragColor; uniform vec4 timeColor; in vec3 ourColor; void main() { FragColor =timeColor; }
vert文件
#version 330 core layout (location = 0) in vec3 aPos; //layout (location = 1) in vec3 aColor; //out vec3 ourColor; uniform mat4 model; uniform mat4 view; uniform mat4 projection; void main() { gl_Position = projection*view*model*vec4(aPos.x-0.5,aPos.y-0.5,aPos.z,1.0f); // ourColor = aColor; };
下面是全部的代码首先是.h文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QOpenGLFunctions>
#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QOpenGLWidget
{
Q_OBJECT
public:
enum Shap{Rect,Circle};
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void drawShap(Shap shap);
void setWireFrame(bool wireFrame);
void setVertexBuffer(QVector<int> data);
// 绘制圆柱体
void drawCylinder();
protected:
void initializeGL() override;
void resizeGL(int w, int h) override;
void paintGL() override;
void timerEvent(QTimerEvent* ev);
void initCube();
void normalize(QVector<int> data);
private:
Ui::MainWindow *ui;
Shap m_shap;
QOpenGLBuffer arrayBuf;
QOpenGLBuffer indexBuf;
QVector<int> m_vertexBuffer;
QOpenGLFunctions_3_3_Core * m_core;
// 这里使用非静态变量glCreateProgram会报错,目前还不清楚具体的是为什么(也可能是我环境本身的问题)
QOpenGLShaderProgram m_shaderProgram;
// 这里使用非静态变量报错,目前还不清楚具体的是为什么(也可能是我环境本身的问题)
};
#endif // MAINWINDOW_H
然后是.cpp文件
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTime>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent): indexBuf(QOpenGLBuffer::IndexBuffer)
{
qDebug()<<__FUNCTION__;
startTimer(500);
}
MainWindow::~MainWindow()
{
qDebug()<<__FUNCTION__;
m_core->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
arrayBuf.destroy();
indexBuf.destroy();
}
void MainWindow::drawShap(MainWindow::Shap shap)
{
qDebug()<<__FUNCTION__;
m_shap = shap;
update();
}
void MainWindow::setWireFrame(bool wireFrame)
{
qDebug()<<__FUNCTION__;
makeCurrent();
if(wireFrame){
// 指定绘图的模式,这里设定为GL_LINE,此模式只绘制线框而不进行填充
m_core->glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
}
else{
m_core->glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
}
doneCurrent();
update();
}
void MainWindow::setVertexBuffer(QVector<int> data)
{
m_vertexBuffer = data;
}
void MainWindow::drawCylinder()
{
}
void MainWindow::normalize(QVector<int> data)
{
int max=0,min=0;
for(int i=0;i<data.size();i++){
if(max <data.at(i)) max = data.at(i);
if(min > data.at(i)) min = data.at(i);
}
GLfloat *vertexArr = (GLfloat *)malloc(sizeof(GLfloat)*data.size());
// 将最大映射到1,最小映射到0
float scale = 1.0/(max-min);
for(int i=0;i<data.size();i++){
vertexArr[i] = (data[i]-min)*scale +min;
}
// Transfer vertex data to VBO 0
arrayBuf.bind();
arrayBuf.allocate(vertexArr, 24 * sizeof(vertexArr));
}
void MainWindow::initializeGL()
{
qDebug()<<__FUNCTION__;
m_core = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_3_Core>();
m_core->initializeOpenGLFunctions(); //为当前上下文初始化提供OpenGL函数解析
initCube();
// 创建顶点着色器
m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/vertexShader.vert");
// 创建片段着色器
m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/fragShader.frag");
// 链接 shaders
m_shaderProgram.link();
int vertexLocation = m_shaderProgram.attributeLocation("aPos");
m_shaderProgram.enableAttributeArray(vertexLocation);
m_shaderProgram.setAttributeBuffer(vertexLocation, GL_FLOAT, 0, 3, 3 * sizeof(GLfloat));
// 指定绘图的模式,这里设定为GL_LINE,此模式只绘制线框而不进行填充
m_core->glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
}
void MainWindow::resizeGL(int w, int h)
{
// qDebug()<<__FUNCTION__;
// m_core->glViewport(0.0f,0.0f,w,h); //调整视口
}
void MainWindow::paintGL()
{
QTime time;
time.start();
QMatrix4x4 view;
QMatrix4x4 projection;
projection.perspective(50,width()/height(),0.1,100);
view.translate(0.0,0.0,-3);
// 设置Clear的属性
m_core->glClearColor(0.1f,0.5f,0.3f,1.0f);
// 使用
m_core->glEnable(GL_DEPTH_TEST);
m_core->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 使用shader
m_shaderProgram.bind();
// 使用顶点和索引数据
arrayBuf.bind();
indexBuf.bind();
switch (m_shap) {
case Rect:
m_shaderProgram.setUniformValue("view",view);
m_shaderProgram.setUniformValue("projection",projection);
// 6个点可以画一个面,每个面给不同的颜色
m_shaderProgram.setUniformValue("timeColor",0.4f,0.6f,0.0f,1.0f);
m_core->glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,0/*&index*/);
m_shaderProgram.setUniformValue("timeColor",0.1f,0.7f,0.3f,1.0f);
m_core->glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,(GLvoid*)(6*sizeof (GLuint))/*&index*/);
m_shaderProgram.setUniformValue("timeColor",0.2f,0.3f,0.6f,1.0f);
m_core->glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,(GLvoid*)(12*sizeof (GLuint))/*&index*/);
m_shaderProgram.setUniformValue("timeColor",0.8f,0.46f,0.2f,1.0f);
m_core->glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,(void*)(18*sizeof (GLuint))/*&index*/);
m_shaderProgram.setUniformValue("timeColor",0.4f,0.2f,0.9f,1.0f);
m_core->glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,(void*)(24*sizeof (GLuint))/*&index*/);
m_shaderProgram.setUniformValue("timeColor",0.1f,0.4f,0.7f,1.0f);
m_core->glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,(void*)(30*sizeof (GLuint))/*&index*/);
break;
default:
break;
}
qDebug("paintGL %d ms",time.elapsed());
}
void MainWindow::timerEvent(QTimerEvent *ev)
{
QTime time;
time.start();
makeCurrent();
int timeValue = QTime::currentTime().second();
// float colorValue = (sin(timeValue)/2.0f) + 0.5f;
// m_shaderProgram.setUniformValue("timeColor",0.0f,colorValue,0.0f,1.0f);
QMatrix4x4 model;
model.rotate(timeValue*10,0.7f,0.1f,0.4f);
m_shaderProgram.setUniformValue("model",model);
doneCurrent();
update();
qDebug("timerEvent %d ms",time.elapsed());
}
void MainWindow::initCube()
{
arrayBuf.create(); // 创建顶点数组
indexBuf.create(); // 创建索引数组
normalize(m_vertexBuffer); // 对输入的数据进行归一化
GLuint index[]={
0,1,2,0,3,2, // 后
4,5,7,5,6,7, // 前
4,5,0,0,1,5, // 左
2,6,7,2,3,7, // 右
1,5,6,6,2,1, // 上
0,4,7,0,3,7 // 下
};
// Transfer index data to VBO 1
indexBuf.bind();
indexBuf.allocate(index, 36 * sizeof(GLuint)); // 把索引数据给indexBuf
}
最后是main文件
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.drawShap(MainWindow::Shap::Rect);
QVector<int> data ={
0, 0, 0,
0, 100, 0,
100, 100, 0,
100, 0, 0,
0, 0, 100,
0, 100, 100,
100, 100, 100,
100, 0, 100,
};
w.setVertexBuffer(data);
w.show();
w.setWireFrame(false);
return a.exec();
}
数据这里给的是立方体的8个顶点。
最后展示一下效果图