Qt5+OpenGL学习笔记(用Qt封装的QOpenGL系列绘制有颜色有深度的三角形→_→)
最近学习OpenGL,虽然说Qt可以使用原生OpenGL的API,但是Qt也提供了封装的QOpenGL系列。我用原生的和封装的分别实现了一次简单渲染(都是渲染两个有深度和颜色的三角形)。
原生的OpenGL用法我就不赘述了(主要是总结不好TvT,而且有很多资料可查)。(PS:两个程序的源码附在最后了)
主要说一下封装的QOpenGL几个注意的地方
一、 QOpenGLBuffer类
在使用前,构造函数处可指定Buffer类型。默认为VertexBuffer。
在使用之前先要create(),之后bind到当前的OpenGL context上。
使用allocate()分配存储空间,同时可以用某个数据来初始化分配的空间。
相当于原生API中:
glGenBuffers();
glBindBuffer();
glBufferData();
二、 QOpenGLShader类
可以在构造函数的地方选择shader的类型。可以有多种方式添加源码,详情参见Qt帮助文档,很简单。
相当于原生API中:
三、 QOpenGLShaderProgram类
可以直接从文件、源码之类的添加Shader。也可以使用addShader添加现有的Shader。需要调用link()将添加到这个program的shader都链接起来。之后调用bind()使该program成为当前唯一绑定到OpenGL Context上的其它porgram将被释放(release)掉。
相当于原生API中:
之后使用enableAttributeArray来激活相应的顶点属性数组(Enables the vertex array at location in this shader program so that the value set by setAttributeArray() on location will be used by the shader program.)
相当于原生API中:
由于程序很简单,没有涉及QOpenGLContext类和其他一些类的介绍~感兴趣可以去翻阅他人的博客之类的。。。(PS:我还是喜欢用原生的。。。)
源代码传送门:
http://pan.baidu.com/s/1i3OmadZ
main.cpp
#include "mainwindow.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
看到这里,是不是感到很奇怪,就一个普通的QMainWindow,opengl的内容在哪调用?
这个倒是看到了以前没碰到多的方法,在mainwindow.ui中
class="xiaoGLWidget" name="openGLWidget">
name="sizePolicy">
hsizetype="Expanding" vsizetype="Expanding">
0
0
在UI布局中使用了一个自定义的widget,不知道他是怎么拖出来的,很神奇。
xiaoglwidget.h
#ifndef XIAOGLWIDGET_H
#define XIAOGLWIDGET_H
#include
#include
#include
#include
#include
class xiaoGLWidget : public QOpenGLWidget,protected QOpenGLFunctions_4_3_Compatibility
{
public:
xiaoGLWidget(QWidget *parent);
virtual ~xiaoGLWidget();
void installShader();
void sendDataToOpenGL();
protected:
void initializeGL();
void paintGL();
void resizeGL(int w,int h);
private:
QOpenGLShaderProgram m_program;
QOpenGLBuffer m_vertexBuf;
QOpenGLBuffer m_indexBuf;
};
#endif // XIAOGLWIDGET_H
xiaoglwidget.cpp
#include "xiaoglwidget.h"
extern const char* vertexShaderCode;
extern const char* fragmentShaderCode;
xiaoGLWidget::xiaoGLWidget(QWidget *parent):m_program(this),m_indexBuf(QOpenGLBuffer::IndexBuffer)
{
}
xiaoGLWidget::~xiaoGLWidget()
{
m_vertexBuf.destroy();
m_indexBuf.destroy();
}
void xiaoGLWidget::initializeGL()
{
initializeOpenGLFunctions();
installShader();
sendDataToOpenGL();
glEnable(GL_DEPTH_TEST);
}
void xiaoGLWidget::paintGL()
{
initializeOpenGLFunctions();
glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_SHORT,NULL);
}
void xiaoGLWidget::resizeGL(int w, int h)
{
}
void xiaoGLWidget::sendDataToOpenGL()
{
initializeOpenGLFunctions();
const GLfloat RED_TRIANGLE_DEPTH = -0.5f;
const GLfloat BLUE_TRIANGLE_DEPTH = 0.5f;
GLfloat verts[]={
+0.0f, -1.0f, RED_TRIANGLE_DEPTH,
+1.0f,+0.0f,+0.0f,
+1.0f, +1.0f, RED_TRIANGLE_DEPTH,
+1.0f,+0.0f,+0.0f,
-1.0f, +1.0f, RED_TRIANGLE_DEPTH,
+1.0f,+0.0f,+0.0f,
+0.0f, +1.0f, BLUE_TRIANGLE_DEPTH,
+0.0f, +0.0f, +1.0f,
-1.0f, -1.0f, BLUE_TRIANGLE_DEPTH,
+0.0f, +0.0f, +1.0f,
+1.0f, -1.0f, BLUE_TRIANGLE_DEPTH,
+0.0f, +0.0f, +1.0f,
};
GLushort indices[]={0,1,2,3,4,5};
this->m_vertexBuf.create();
this->m_indexBuf.create();
this->m_vertexBuf.bind();
this->m_indexBuf.bind();
this->m_vertexBuf.allocate(verts,sizeof(verts));
this->m_indexBuf.allocate(indices,sizeof(indices));
m_program.enableAttributeArray(0);
m_program.setAttributeBuffer(0, GL_FLOAT, 0, 3, sizeof(float)*6);
m_program.enableAttributeArray(1);
m_program.setAttributeBuffer(1,GL_FLOAT,sizeof(float)*3,3,sizeof(float)*6);
}
void xiaoGLWidget::installShader()
{
QOpenGLShader vertexShader(QOpenGLShader::Vertex);
vertexShader.compileSourceCode(vertexShaderCode);
QOpenGLShader fragmentShader(QOpenGLShader::Fragment);
fragmentShader.compileSourceCode(fragmentShaderCode);
m_program.addShader(&vertexShader);
m_program.addShader(&fragmentShader);
m_program.link();
m_program.bind();
}
shadercode.cpp
const char* vertexShaderCode =
"#version 430\r\n"
""
"in layout(location=0) vec3 position;"
"in layout(location=1) vec3 vertexColor;"
""
"out vec3 theColor;"
""
"void main()"
"{"
" gl_Position = vec4(position, 1.0);"
" theColor = vertexColor;"
"}";
const char* fragmentShaderCode =
"#version 430\r\n"
""
"out vec4 daColor;"
"in vec3 theColor;"
""
"void main()"
"{"
" daColor = vec4(theColor,1.0);"
"}";
定义了一个widget,
class xiaoGLWidget : public QOpenGLWidget,protected QOpenGLFunctions_4_3_Compatibility
继承自QOpenGLWidget,然后初始化opengl functions api。
关于shader,就和我前面的第二个程序写法不同了。
QOpenGLShader vertexShader(QOpenGLShader::Vertex);
QOpenGLShader fragmentShader(QOpenGLShader::Fragment);创建了两个着色器之后,使用 compileSourceCode 来加载程序
(
而之前的我是先创建一个项目对象,用项目对象的addShaderFromSourceCode来加载程序
去看下qt的源代码实现,就是调用者和被调用者的关系。
)
然后调用addShader加载进去
之后就是link、bind
const GLfloat RED_TRIANGLE_DEPTH = -0.5f;
const GLfloat BLUE_TRIANGLE_DEPTH = 0.5f;
z轴正向指向里,所以负数的会在正数上方,也就是红色在蓝色上方,哦,这就是他说的深度。
GLfloat verts[]={
+0.0f, -1.0f, RED_TRIANGLE_DEPTH,
+1.0f,+0.0f,+0.0f,
+1.0f, +1.0f, RED_TRIANGLE_DEPTH,
+1.0f,+0.0f,+0.0f,
-1.0f, +1.0f, RED_TRIANGLE_DEPTH,
+1.0f,+0.0f,+0.0f,
+0.0f, +1.0f, BLUE_TRIANGLE_DEPTH,
+0.0f, +0.0f, +1.0f,
-1.0f, -1.0f, BLUE_TRIANGLE_DEPTH,
+0.0f, +0.0f, +1.0f,
+1.0f, -1.0f, BLUE_TRIANGLE_DEPTH,
+0.0f, +0.0f, +1.0f,
};
这里定义了12组,为什么定义这么多?
this->m_vertexBuf.create();
this->m_indexBuf.create();
这里有两个buf,干什么用的?
GLushort indices[]={0,1,2,3,4,5};
this->m_vertexBuf.allocate(verts,sizeof(verts));
this->m_indexBuf.allocate(indices,sizeof(indices));一个存放顶点数据,一个存放索引 ?
m_program.enableAttributeArray(0);
m_program.enableAttributeArray(1);
使能了,那要先看下着色器,先看片段的
const char* fragmentShaderCode =
"#version 430\r\n"
""
"out vec4 daColor;"
"in vec3 theColor;"
""
"void main()"
"{"
" daColor = vec4(theColor,1.0);"
"}";有了之前的基础,看着就简单了,之前就知道了 120之后就没有了gl_FragColor,所以要自己定义一个输出,设置为输入的颜色,alpha为1.0f,输入即顶点着色器的颜色输出
const char* vertexShaderCode =
"#version 430\r\n"
""
"in layout(location=0) vec3 position;"
"in layout(location=1) vec3 vertexColor;"
""
"out vec3 theColor;"
""
"void main()"
"{"
" gl_Position = vec4(position, 1.0);"
" theColor = vertexColor;"
"}";颜色设置为输入的vertexColor颜色
他这里直接使用layout(location=0)定义了位置
使能后装载
m_program.setAttributeBuffer(0, GL_FLOAT, 0, 3, sizeof(float)*6);
m_program.setAttributeBuffer(1, GL_FLOAT, sizeof(float)*3, 3, sizeof(float)*6);(之前的我是用 glVertexAttribPointer装载顶点和颜色数据的)
这里装载前面的buffer
void setAttributeBuffer(int location, GLenum type, int offset, int tupleSize, int stride = 0);
void glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr)
使能深度测试
glEnable(GL_DEPTH_TEST);
绘制
glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, NULL);
没有看到buffer使用的地方
“
在使用之前先要create(),之后bind到当前的OpenGL context上。
使用allocate()分配存储空间,同时可以用某个数据来初始化分配的空间
”
十二组数据,是一组顶点数据,一组颜色数据,那怎么区分的呢
setAttributeBuffer(0, GL_FLOAT, 0, 3, sizeof(float)*6);offset = 0,第一组开始,
tuplesize = 3,三个数据,就是第一组三个数据
stride = sizeof(float) * 6,步幅这么长,下次去就是6个数据之后,就是第三组数据开始
setAttributeBuffer(1, GL_FLOAT, sizeof(float)*3, 3, sizeof(float)*6);offset = sizeof(float) * 3, 就是第二组数据的开始,
tuplesize = 3,也就是第二组三个数据,
stride = sizeof(float) * 6,步幅这么长,下次去就是6个数据之后,就是第四组数据开始
取的顺序呢,indices[]={0,1,2,3,4,5}; (这个应该是这样理解?)
退出时两个buffer销毁
m_vertexBuf.destroy();
m_indexBuf.destroy();
from: http://blog.csdn.net/fu851523125/article/details/51193528