OPenGL的环境配置由很多种,这儿使用的环境就是glfw和glad组合。选择Qt来作为界面库使用。
vs2013 + Qt5.5 + OpenGL3.3
和一般的环境配置一样,也需要一个三维绘图窗口和一个用于管理函数指针的库,在Qt中对应的就是两个类QOpenGLWidget和QOpenGLFunctions,只需要继承他们就可以了。
class QOPenWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
QOPenWidget(QWidget *parent = 0);
~QOPenWidget();
private:
void initializeGL(); /*写渲染前的配置信息*/
void resizeGL(int w, int h); /*窗口变化*/
void paintGL(); /*写渲染循环的内容*/
};
注意QOpenGLFunctions是基于OpenGL ES 2.0的API,如果想用任意版本的原生OPenGL,需要用的一个类QAbstractOpenGLFunctions,它有很多子类,对应不同版本的OpenGL。如图这是Qt文档中的说明。
对于本示例来说使用的是3.3版,继承的类就是QOpenGLFunctions_3_3_Compatibility类。其他可按自己需要的版本来继承。下面来个完整的示例。
QOPenWidget.h
#ifndef QOPENWIDGET_H
#define QOPENWIDGET_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
class QOPenWidget : public QOpenGLWidget, protected QOpenGLFunctions_3_3_Compatibility
{
Q_OBJECT
public:
QOPenWidget(QWidget *parent = 0);
~QOPenWidget();
private:
void initializeGL();
void resizeGL(int w, int h);
void paintGL();
//加载着色器程序
void CompileShader(unsigned int *id, const char* vertexPath, const char* fragmentPath, const char* geometryPath = NULL);
private:
//缓存对象
unsigned int VBO, VAO, EBO;
//着色器程序ID
unsigned int ID;
//定时器
QTimer timer;
};
#endif
QOPenWidget.cpp
#include "QOPenWidget.h"
#include
#include
#include
#include
QOPenWidget::QOPenWidget(QWidget *parent) :QOpenGLWidget(parent)
{
setUpdateBehavior(QOpenGLWidget::NoPartialUpdate);
connect(&timer, SIGNAL(timeout()), this, SLOT(update()));
timer.start(0);
}
QOPenWidget::~QOPenWidget()
{
}
void QOPenWidget::initializeGL()
{
initializeOpenGLFunctions(); //初始化
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glEnable(GL_DEPTH_TEST); //开启深度缓存
//添加着色器
CompileShader(&ID,"texture.vs", "texture.fs");
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.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f
};
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
//绑定VAO
glBindVertexArray(VAO);
//绑定VBO
glBindBuffer(GL_ARRAY_BUFFER, VBO);
//把顶点数组复制到VBO
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 颜色属性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
//解除绑定VBO
glBindBuffer(GL_ARRAY_BUFFER, 0);
//解除绑定EBO
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
//解除绑定VAO
glBindVertexArray(0);
}
void QOPenWidget::resizeGL(int w, int h)
{
glViewport(0, 0, w, h);
}
void QOPenWidget::paintGL()
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//激活着色器
glUseProgram(ID);
//绑定顶点缓存对象
glBindVertexArray(VAO);
//开始绘制
glDrawArrays(GL_TRIANGLES, 0, 3);
//解除绑定
glBindVertexArray(0);
}
//加载着色器程序
void QOPenWidget::CompileShader(unsigned int *id, const char* vertexPath, const char* fragmentPath, const char* geometryPath)
{
std::string vertexCode;
std::string fragmentCode;
std::string geometryCode;
std::ifstream vShaderFile;
std::ifstream fShaderFile;
std::ifstream gShaderFile;
// 打开文件
vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath);
std::stringstream vShaderStream, fShaderStream;
// 读取文件的缓冲内容到数据流中
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
// 关闭文件处理器
vShaderFile.close();
fShaderFile.close();
// 转换数据流到string
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
if (geometryPath != NULL)
{
gShaderFile.open(geometryPath);
std::stringstream gShaderStream;
gShaderStream << gShaderFile.rdbuf();
gShaderFile.close();
geometryCode = gShaderStream.str();
}
const char* vShaderCode = vertexCode.c_str();
const char* fShaderCode = fragmentCode.c_str();
// 2. 编译着色器
unsigned int vertex, fragment;
int success;
char infoLog[512];
// 顶点着色器
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
//编译着色器
glCompileShader(vertex);
//检测编译时错误
glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
//如果编译错误进入语句
if (!success)
{
//获取错误信息
glGetShaderInfoLog(vertex, 512, NULL, infoLog);
//打印错误
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
//片元着色器
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
//检测错误
glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
if (!success)
{
//获取错误原因
glGetShaderInfoLog(fragment, 512, NULL, infoLog);
//打印
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
//几何着色器
unsigned int geometry;
if (geometryPath != nullptr)
{
const char * gShaderCode = geometryCode.c_str();
geometry = glCreateShader(GL_GEOMETRY_SHADER);
glShaderSource(geometry, 1, &gShaderCode, NULL);
glCompileShader(geometry);
}
// 着色器程序
*id = glCreateProgram();
glAttachShader(*id, vertex);
glAttachShader(*id, fragment);
if (geometryPath != nullptr)
glAttachShader(*id, geometry);
glLinkProgram(*id);
//检测错误
glGetProgramiv(*id, GL_LINK_STATUS, &success);
if (!success)
{
//获取错误信息
glGetProgramInfoLog(ID, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
// 删除着色器,它们已经链接到我们的程序中了,已经不再需要了
glDeleteShader(vertex);
glDeleteShader(fragment);
if (geometryPath != nullptr)
glDeleteShader(geometry);
}
着色器
texture.vs
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
out vec3 ourColor;
void main()
{
gl_Position = vec4(aPos, 1.0);
ourColor = aColor;
}
texture.fs
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
void main()
{
FragColor = vec4(ourColor, 1.0);
}