基于Qt的OpenGL可编程管线学习- 绘制一个三角形
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于Qt的OpenGL可编程管线学习- 绘制一个三角形相关的知识,希望对你有一定的参考价值。
0、写在前面的话
这里只是学习的时候做的笔记记录方便日后的查看,如果有大神看到觉得有问题的地方希望能给予指出,方便日后的学习,谢谢!
我是用的Qt版本为Qt5.6,开发环境为Qt Creator
1、QOpenGLWidget
在Qt开发环境下,使用OpenGL的可编程管线绘制一个三角形
效果如下图所示:
这里使用QOpenGLWidget进行绘制的,在QOpenGLWidget中需要重写
void initializeGL(void);
void resizeGL(int w, int h);
void paintGL(void);
这三个函数,initializeGL()函数为初始化窗口调用的函数,resizeGL()函数改变窗口大小时调用,paintGL()是每一帧绘制的函数。
2、创建Program,加载Shader
使用OpenGL的shader绘制三角形,就要创建一个shader的程序,Qt中实现了QOpenGLShader、QOpenGLShaderProgram类,方便程序的创建和添加Shader。使用代码如下,本例中程序加载两个shader(vertex shader 和fragment shader):
// 创建一个GPU程序
GLuint createGPUProgram(QString nVertexShaderFile, QString nFragmentShaderFile);
QOpenGLShader *m_VertexShader;
QOpenGLShader *m_FragmentShader;
QOpenGLShaderProgram *m_Program;
createGPUProgram()函数实现如下:
GLuint OpenGLWidget::createGPUProgram(QString nVertexShaderFile, QString nFragmentShaderFile)
{
m_VertexShader = new QOpenGLShader(QOpenGLShader::Vertex);
bool isOK = m_VertexShader->compileSourceFile(nVertexShaderFile);
if (!isOK)
{
delete m_VertexShader;
m_VertexShader = nullptr;
return 0;
}
m_FragmentShader = new QOpenGLShader(QOpenGLShader::Fragment);
if (!m_FragmentShader->compileSourceFile(nFragmentShaderFile))
{
delete m_VertexShader;
delete m_FragmentShader;
m_FragmentShader = nullptr;
return 0;
}
m_Program = new QOpenGLShaderProgram(this);
m_Program->addShader(m_VertexShader);
m_Program->addShader(m_FragmentShader);
m_Program->link();
return m_Program->programId();
}
其中Vertex shader和fragment shader的内容如下:
3、shader内容
在OpenGL的Shader使用,GLSL语言,与C语言很类似。下面是关于GLSL的一些简单的介绍:
attribute CPU与Shader传递数据,只能在CPU与Vertex Shader之间使用。
uniform 可用于CPU与任意Shader传递数据,一般用于传递矩阵等较大的数据。
varying 可用于Shader间传递数据。
本例子中,数据是CPU->Vertex Shader->Fragmen Shader顺序传递,Vertex shader是处理数据,Fragment shader会终的转换为像素的处理。最后通过OpenGL渲染到屏幕输出。
Vertex Shader:
attribute vec3 pos;
attribute vec4 color;
uniform mat4 M;
uniform mat4 V;
uniform mat4 P;
varying vec4 M_Color;
void main()
{
M_Color = color;
gl_Position = P * V * M * vec4(pos, 1.0);
}
由于OpenGL中为列向量和矩阵,这里需要使用矩阵左乘的形式。Vertex shader对应一个点的调用。
其中,M为传递的模型矩阵、V为视图矩阵、P为投影矩阵。
Fragment Shader:
varying vec4 M_Color;
void main()
{
gl_FragColor = M_Color;
}
4、为Shader中传入数据
为attribute中和uniform中的数据赋值,需要获取Shader中的Location
// 获取Uniform ID & Attribute ID
m_MLocationMat = OpenGLCore->glGetUniformLocation(programId, "M");
m_VLocationMat = OpenGLCore->glGetUniformLocation(programId, "V");
m_PLocationMat = OpenGLCore->glGetUniformLocation(programId, "P");
m_PosVector = OpenGLCore->glGetAttribLocation(programId, "pos");
m_ColorVector = OpenGLCore->glGetAttribLocation(programId, "color");
其中OpenGLCore为
OpenGLCore = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_2_Core>();
使用OpenGL3.2的核心,programId为QOpenGLShaderProgram的programId。
顶点数据如下:
struct VertexInfo
{
float pos[3];
float color[4];
};
VertexInfo nVertexInfo[3];
nVertexInfo[0].pos[0] = 0.0f;
nVertexInfo[0].pos[1] = 0.0f;
nVertexInfo[0].pos[2] = -30.0f;
nVertexInfo[0].color[0] = 1.0f;
nVertexInfo[0].color[1] = 0.0f;
nVertexInfo[0].color[2] = 0.0f;
nVertexInfo[0].color[3] = 1.0f;
nVertexInfo[1].pos[0] = 10.0f;
nVertexInfo[1].pos[1] = 0.0f;
nVertexInfo[1].pos[2] = -30.0f;
nVertexInfo[1].color[0] = 0.0f;
nVertexInfo[1].color[1] = 1.0f;
nVertexInfo[1].color[2] = 0.0f;
nVertexInfo[1].color[3] = 1.0f;
nVertexInfo[2].pos[0] = 0.0f;
nVertexInfo[2].pos[1] = 10.0f;
nVertexInfo[2].pos[2] = -30.0f;
nVertexInfo[2].color[0] = 0.0f;
nVertexInfo[2].color[1] = 0.0f;
nVertexInfo[2].color[2] = 1.0f;
nVertexInfo[2].color[3] = 1.0f;
创建VBO(Vertex Buffer Object)
// 创建VBO
OpenGLCore->glGenBuffers(1, &m_VBO);
OpenGLCore->glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
OpenGLCore->glBufferData(GL_ARRAY_BUFFER, sizeof(VertexInfo) * 3, nVertexInfo, GL_STATIC_DRAW);
OpenGLCore->glBindBuffer(GL_ARRAY_BUFFER, 0);
创建IBO(Element Buffer Object)
// 创建IBO
unsigned int index[3] = {0, 1, 2};
OpenGLCore->glGenBuffers(1, &m_IBO);
OpenGLCore->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IBO);
OpenGLCore->glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(index), index, GL_STATIC_DRAW);
OpenGLCore->glBindBuffer(GL_ARRAY_BUFFER, 0);
为uniform数据赋值
Qt提供了矩阵类,QMatrix4x4(默认为列向量)
OpenGLCore->glUniformMatrix4fv(m_MLocationMat, 1, GL_FALSE, nMormalMat.data());
OpenGLCore->glUniformMatrix4fv(m_VLocationMat, 1, GL_FALSE, nMormalMat.data());
OpenGLCore->glUniformMatrix4fv(m_PLocationMat, 1, GL_FALSE, mProjectionMatrix.data());
为attribute数据赋值
OpenGLCore->glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
OpenGLCore->glEnableVertexAttribArray(m_PosVector);
OpenGLCore->glVertexAttribPointer(m_PosVector, 3, GL_FLOAT, GL_FALSE, sizeof(VertexInfo), (void*)0);
OpenGLCore->glEnableVertexAttribArray(m_ColorVector);
OpenGLCore->glVertexAttribPointer(m_ColorVector, 4, GL_FLOAT, GL_FALSE, sizeof(VertexInfo), (void*)(sizeof(float) * 3));
OpenGLCore->glBindBuffer(GL_ARRAY_BUFFER, 0);
5、矩阵值的设置及绘制指令
投影矩阵设置,在resizeGL()函数中
mProjectionMatrix.perspective(45.0f, w * 1.0f / h, 0.1f, 500.0f);
这里模型矩阵和视图矩阵都使用单位矩阵
在paintGL()函数中
使用VBO绘制(使用VBO中的数值为attribute中变量赋值,一共赋值三次)
OpenGLCore->glDrawArrays(GL_TRIANGLES, 0, 3);
使用IBO绘制(利用IBO中的Index绘制,即VBO中的索引)
// 使用DeawElement绘制
OpenGLCore->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IBO);
OpenGLCore->glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);
OpenGLCore->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
6、完整代码
triangle.h
#ifndef TRIANGLE_H
#define TRIANGLE_H
//#include <glew.h>
#include <QObject>
#include <QOpenGLWidget>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QOpenGLFunctions_3_2_Core>
#include <QFile>
#include <QOpenGLShader>
#include <QOpenGLShaderProgram>
#include <QMatrix>
#include <QTimer>
#include <GL/gl.h>
struct VertexInfo
{
float pos[3];
float color[4];
};
class OpenGLWidget : public QOpenGLWidget
{
Q_OBJECT
public:
OpenGLWidget();
~OpenGLWidget();
private:
// 创建一个GPU程序
GLuint createGPUProgram(QString nVertexShaderFile, QString nFragmentShaderFile);
QOpenGLShader *m_VertexShader;
QOpenGLShader *m_FragmentShader;
QOpenGLShaderProgram *m_Program;
// MVP Location
GLint m_MLocationMat;
GLint m_VLocationMat;
GLint m_PLocationMat;
GLint m_PosVector;
GLint m_ColorVector;
// VBO
GLuint m_VBO;
// IBO
GLuint m_IBO;
QMatrix4x4 mProjectionMatrix;
QTimer *m_Timer;
QOpenGLFunctions_3_2_Core *OpenGLCore = nullptr;
protected:
virtual void initializeGL(void);
virtual void resizeGL(int w, int h);
virtual void paintGL(void);
protected slots:
void onTimerOut(void);
};
#endif // TRIANGLE_H
triangle.cpp
#include "triangle.h"
#include <QDebug>
OpenGLWidget::OpenGLWidget()
{
m_Timer = new QTimer;
}
OpenGLWidget::~OpenGLWidget()
{
}
GLuint OpenGLWidget::createGPUProgram(QString nVertexShaderFile, QString nFragmentShaderFile)
{
m_VertexShader = new QOpenGLShader(QOpenGLShader::Vertex);
bool isOK = m_VertexShader->compileSourceFile(nVertexShaderFile);
if (!isOK)
{
delete m_VertexShader;
m_VertexShader = nullptr;
return 0;
}
m_FragmentShader = new QOpenGLShader(QOpenGLShader::Fragment);
if (!m_FragmentShader->compileSourceFile(nFragmentShaderFile))
{
delete m_VertexShader;
delete m_FragmentShader;
m_FragmentShader = nullptr;
return 0;
}
m_Program = new QOpenGLShaderProgram(this);
m_Program->addShader(m_VertexShader);
m_Program->addShader(m_FragmentShader);
m_Program->link();
return m_Program->programId();
}
void OpenGLWidget::initializeGL(void)
{
createGPUProgram("trianglevertexshader.vert", "trianglefragmentshader.frag");
OpenGLCore = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_2_Core>();
GLuint programId = m_Program->programId();
// 获取Uniform ID & Attribute ID
m_MLocationMat = OpenGLCore->glGetUniformLocation(programId, "M");
m_VLocationMat = OpenGLCore->glGetUniformLocation(programId, "V");
m_PLocationMat = OpenGLCore->glGetUniformLocation(programId, "P");
m_PosVector = OpenGLCore->glGetAttribLocation(programId, "pos");
m_ColorVector = OpenGLCore->glGetAttribLocation(programId, "color");
// 创建VBO
VertexInfo nVertexInfo[3];
nVertexInfo[0].pos[0] = 0.0f;
nVertexInfo[0].pos[1] = 0.0f;
nVertexInfo[0].pos[2] = -30.0f;
nVertexInfo[0].color[0] = 1.0f;
nVertexInfo[0].color[1] = 0.0f;
nVertexInfo[0].color[2] = 0.0f;
nVertexInfo[0].color[3] = 1.0f;
nVertexInfo[1].pos[0] = 10.0f;
nVertexInfo[1].pos[1] = 0.0f;
nVertexInfo[1].pos[2] = -30.0f;
nVertexInfo[1].color[0] = 0.0f;
nVertexInfo[1].color[1] = 1.0f;
nVertexInfo[1].color[2] = 0.0f;
nVertexInfo[1].color[3] = 1.0f;
nVertexInfo[2].pos[0] = 0.0f;
nVertexInfo[2].pos[1] = 10.0f;
nVertexInfo[2].pos[2] = -30.0f;
nVertexInfo[2].color[0] = 0.0f;
nVertexInfo[2].color[1] = 0.0f;
nVertexInfo[2].color[2] = 1.0f;
nVertexInfo[2].color[3] = 1.0f;
// 创建VBO
OpenGLCore->glGenBuffers(1, &m_VBO);
OpenGLCore->glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
OpenGLCore->glBufferData(GL_ARRAY_BUFFER, sizeof(VertexInfo) * 3, nVertexInfo, GL_STATIC_DRAW);
OpenGLCore->glBindBuffer(GL_ARRAY_BUFFER, 0);
// 创建IBO
unsigned int index[3] = {0, 1, 2};
OpenGLCore->glGenBuffers(1, &m_IBO);
OpenGLCore->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IBO);
OpenGLCore->glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(index), index, GL_STATIC_DRAW);
OpenGLCore->glBindBuffer(GL_ARRAY_BUFFER, 0);
glClearColor(41.0f / 255.0f, 71.0f / 255.0f, 121.0f / 255.0f, 1.0f);
QObject::connect(m_Timer, SIGNAL(timeout()), this, SLOT(onTimerOut()));
m_Timer->start(10);
}
void OpenGLWidget::resizeGL(int w, int h)
{
glViewport(0, 0, w, h);
mProjectionMatrix.perspective(45.0f, w * 1.0f / h, 0.1f, 500.0f);
repaint();
}
void OpenGLWidget::paintGL(void)
{
bool result = m_Program->bind();
if (!result)
return;
QMatrix4x4 nMormalMat;
OpenGLCore->glUniformMatrix4fv(m_MLocationMat, 1, GL_FALSE, nMormalMat.data());
OpenGLCore->glUniformMatrix4fv(m_VLocationMat, 1, GL_FALSE, nMormalMat.data());
OpenGLCore->glUniformMatrix4fv(m_PLocationMat, 1, GL_FALSE, mProjectionMatrix.data());
OpenGLCore->glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
// 使用DrawArrays绘制
OpenGLCore->glEnableVertexAttribArray(m_PosVector);
OpenGLCore->glVertexAttribPointer(m_PosVector, 3, GL_FLOAT, GL_FALSE, sizeof(VertexInfo), (void*)0);
OpenGLCore->glEnableVertexAttribArray(m_ColorVector);
OpenGLCore->glVertexAttribPointer(m_ColorVector, 4, GL_FLOAT, GL_FALSE, sizeof(VertexInfo), (void*)(sizeof(float) * 3));
//OpenGLCore->glDrawArrays(GL_TRIANGLES, 0, 3);
OpenGLCore->glBindBuffer(GL_ARRAY_BUFFER, 0);
// 使用DeawElement绘制
OpenGLCore->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IBO);
OpenGLCore->glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);
OpenGLCore->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
m_Program->release();
}
void OpenGLWidget::onTimerOut(void)
{
repaint();
}
trianglevertexshader.vert
attribute vec3 pos;
attribute vec4 color;
uniform mat4 M;
uniform mat4 V;
uniform mat4 P;
varying vec4 M_Color;
void main()
{
M_Color = color;
gl_Position = P * V * M * vec4(pos, 1.0);
}
trianglefragmentshader.frag
varying vec4 M_Color;
void main()
{
gl_FragColor = M_Color;
}
main.cpp
#include <QApplication>
#include "triangle.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
OpenGLWidget wdt;
wdt.show();
return a.exec();
}
本文出自 “不会飞的纸飞机” 博客,请务必保留此出处http://douzhq.blog.51cto.com/12552184/1930925
以上是关于基于Qt的OpenGL可编程管线学习- 绘制一个三角形的主要内容,如果未能解决你的问题,请参考以下文章
基于Qt的OpenGL可编程管线学习- 使用Instanced方式绘制