在 LearnOpenGL 学习opengl的时候,示例中使用的是 glfw 这个应用框架去承载opengl,这个工具我们在工作或学习中是不必要使用的,Qt 对opengl的支持相当完善,我们将学习在Qt中使用opengl。
在Qt5.4之前,Qt 使用 QGLWidget 类来承载opengl的渲染,在 QGLWidget的文档中有如下内容:
This class is obsolete. It is provided to keep old source code working. We strongly advise against using it in new code.
这个类已经过时了。提供它是为了保持旧源代码的工作。我们强烈建议不要在新代码中使用它。
Note: This class is part of the legacy Qt OpenGL module and, like the
other QGL classes, should be avoided in the new applications. Instead,
starting from Qt 5.4, prefer using QOpenGLWidget and the QOpenGL
classes.
注意:这个类是遗留Qt OpenGL模块的一部分,像其他QGL类一样,应该在新的应用程序中避免使用。相反,从Qt5.4开始,更喜欢使用QOpenGLWidget和QOpenGL类。
我们直接选择较为新颖的 QOpenGLWidget;
首先,我们创建一个QWidgets工程,增加头文件,修改基类继承 QOpenGLWidget 和 QOpenGLFunctions_3_3_core
注意:继承于 QOpenGLFunctions 无法使用 gl3.h 中的功能
class Widget : public QOpenGLWidget, public QOpenGLFunctions_3_3_Core
实现以下三个虚函数:
void initializeGL() override;
void resizeGL(int w, int h) override;
void paintGL() override;
paintGL() :渲染OpenGL场景,每当需要更新小部件时调用。
resizeGL() : 设置OpenGL视口,投影等。每当小部件被调整大小时(当它第一次显示时也会调用,因为所有新创建的小部件都会自动获得一个resize事件)。
initializeGL() : 设置OpenGL资源和状态。在第一次调用resizeGL()或paintGL()之前调用一次。
基本实现如下:
void Widget::initializeGL()
{
initializeOpenGLFunctions();
}
void Widget::resizeGL(int w, int h)
{
glViewport(0.0f, 0.0f, w, h);
}
void Widget::paintGL()
{
glClearColor(1.0f, 0.0f, 0.0f, 1.0f); //清屏
glClear(GL_COLOR_BUFFER_BIT); //清除颜色缓冲
}
此时运行的结果是一个红色的窗口:
按照LearnOpenGL的示例,画出第一个三角形,添加顶点和片段着色器,并在 paintGL实现:
const GLchar* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"void main()\n"
"{\n"
"gl_Position = vec4(position.x, position.y, position.z, 1.0);\n"
"}\0";
const GLchar* fragmentShaderSource = "#version 330 core\n"
"out vec4 color;\n"
"void main()\n"
"{\n"
"color = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";
void Widget::paintGL()
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f); //清屏
glClear(GL_COLOR_BUFFER_BIT); //清除颜色缓冲
//顶点着色器
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
//检测编译错误
GLint success;
GLchar infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
qDebug() << "error(vertex compile):" << infoLog;
}
// 片段着色器
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
//检测编译错误
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
qDebug() << "error(fragment compile):" << infoLog;
}
// 程序对象链接着色器
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// 检查链接错误
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
qDebug() << "error(shader link):" << infoLog;
}
//删除,先行标记为删除
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// 设置顶点数据(和缓冲区)和属性指针
GLfloat vertices[] =
{
-0.5f, -0.5f, 0.0f, // Left
0.5f, -0.5f, 0.0f, // Right
0.0f, 0.5f, 0.0f // Top
};
GLuint VBO, VAO;
glGenBuffers(1, &VBO);
// 首先绑定顶点数组对象,然后绑定和设置顶点缓冲区和属性指针
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
//注意,这是允许的,调用glVertexAttribPointer将VBO注册为当前绑定的顶点缓冲区对象,这样之后我们就可以安全地解除绑定
glBindBuffer(GL_ARRAY_BUFFER, 0);
// 画出第一个三角形
glUseProgram(shaderProgram);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
// 适当地分配所有的资源,一旦它们超过了它们的作用
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
}
运行结果如下:
opengl的渲染也可以和 Qt 的自绘相结合,重写QOpenGLWidget 的paintEvent以实现组合功能:
void Widget::paintEvent(QPaintEvent *event)
{
QOpenGLWidget::paintEvent(event);
QPainter painter(this);
painter.fillRect(0, 0, 100, 100, Qt::green);
}
运行如下:
需要注意的地方:
如重写 QOpenGLWidget::paintEvent, 需在paintEvent中调用 基类 QOpenGLWidget的 paintEvent,因为 paintGL 函数是在QOpenGLWidget::paintEvent中调用,如不主动调用会导致opengl渲染无效;
如果想要在opengl渲染层上添加Qt绘制的图形,需在手动调用 QOpenGLWidget::paintEvent之后进行绘制。
附完整工程:qt-opengl