Qt中使用GLEW进行OpenGL开发(Windows平台)
为什么要使用GLEW
因为工作需求绘制视频,原本使用的Qt(我使用的是5.9.8的版本)自带的QOpenGL进行视频绘制,但是在稳定行测试中经常会出现界面不更新的情况。找了很久也没有好的解决方案,后来干脆使用GLEW进行OpenGL的开发。
目前Qt的OpenGL多少会存在些问题或者不完善。
使用VS创建Qt项目
1.创建一个Widget窗口即可。
#include <QtWidgets/QWidget>
#include "ui_GLEW4QT.h"
class GLEW4QT : public QWidget
{
Q_OBJECT
public:
GLEW4QT(QWidget *parent = Q_NULLPTR);
private:
Ui::GLEW4QTClass ui;
};
设置Widget
GLEW4QT::GLEW4QT(QWidget *parent)
: QWidget(parent, Qt::MSWindowsOwnDC)
{
setAttribute(Qt::WA_PaintOnScreen);
setAttribute(Qt::WA_NoSystemBackground);
setAutoFillBackground(true);
ui.setupUi(this);
}
1.在构造函数中设置一个Qt::MSWindowsOwnDC( 在窗口上为窗口提供自己的显示上下文)。
2.设置属性Qt::WA_PaintOnScreen,设置该属性后需要重新实现paintEngine函数。
3.设置Qt::WA_NoSystemBackground属性。
4.设置自动填充背景
重新实现paintEngine
直接返回NULL即可
virtual QPaintEngine* paintEngine() const { return NULL; }
添加GLEW库到工程
GLEW下载地址: 传送门
引入头文件
#define GLEW_STATIC
#include <GL/glew.h>
#include <GL/wglew.h>
#ifdef GLEW_STATIC
//使用静态库
#pragma comment(lib, "glew32s.lib")
#else
//使用动态库
#pragma comment(lib, "glew32.lib")
#endif
#pragma comment(lib, "opengl32.lib")
注意:如果使用glew静态库则需要在#include <GL/glew.h>前定义GLEW_STATIC
设置OpenGL上下文
设置GL像素格式
创建静态函数设置
/*
* 设置GL像素格式
*/
static bool SetupPixelFormat(HDC dc)
{
//像素格式描述符,渲染的像素格式
PIXELFORMATDESCRIPTOR pfd;
ZeroMemory(&pfd, sizeof(PIXELFORMATDESCRIPTOR));
pfd.nVersion = 1;
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.cColorBits = 32;
pfd.cDepthBits = 24;
pfd.cStencilBits = 8;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.iLayerType = PFD_MAIN_PLANE;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
int pixelformat = 0;
if ((pixelformat = ChoosePixelFormat(dc, &pfd)) == 0)
return false;
if (SetPixelFormat(dc, pixelformat, &pfd) == FALSE)
return false;
return true;
}
检测支持OpenGL的力度
这里也创建一个静态函数检测
/*
* 检测支持OpenGL的力度
* @param major 主版本号
* @param minor 次版本号
* @return -1 不支持GL, -2 glinit初始化失败, 0 支持GL, 1 支持多重采样
*/
static int CheckSupportGLLevel(int *major, int *minor)
{
static bool checked = false;
static int _major = 0;
static int _minor = 0;
static int support = 0;
if (checked)
{
if (major)
*major = _major;
if (minor)
*minor = _minor;
return support;
}
checked = true;
HDC dc = nullptr;
HGLRC rc = nullptr;
QWidget w;
HWND hwnd = (HWND)w.winId();
dc = GetDC(hwnd);
if (!SetupPixelFormat(dc))
{
support = -1;
goto end;
}
rc = wglCreateContext(dc);
wglMakeCurrent(dc, rc);
if (glewInit() != GLEW_OK)
{
support = -2;
goto end;
}
glGetIntegerv(GL_MAJOR_VERSION, &_major);
glGetIntegerv(GL_MINOR_VERSION, &_minor);
if (wglChoosePixelFormatARB)
support = 1;
if (major)
*major = _major;
if (minor)
*minor = _minor;
end:
if (rc)
wglDeleteContext(rc);
if (dc)
ReleaseDC(hwnd, dc);
rc = nullptr;
wglMakeCurrent(nullptr, nullptr);
ReleaseDC(hwnd, dc);
return support;
}
创建GLRC
/*
* 创建HGLRC
* @param dc HDC
* @param supportMSAA 是否支持msaa
* @param msaaLevel msaa等级 // 8, 16, 32
* @return NULL 为失败,否则返回HGLRC
*/
static HGLRC CreateGLRC(HDC dc, bool supportMSAA, int msaaLevel)
{
HGLRC rc;
if (!supportMSAA)
{
if (!SetupPixelFormat(dc))
return nullptr;
rc = wglCreateContext(dc);
return rc;
}
ZeroMemory(&rc, sizeof(HGLRC));
GLint attribs[]{
WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
WGL_RED_BITS_ARB, 8,
WGL_GREEN_BITS_ARB, 8,
WGL_BLUE_BITS_ARB, 8,
WGL_ALPHA_BITS_ARB, 8,
WGL_DEPTH_BITS_ARB, 24,
WGL_STENCIL_BITS_ARB, 8,
WGL_SAMPLE_BUFFERS_ARB, GL_TRUE,
WGL_SAMPLES_ARB, msaaLevel,//8,16,32
NULL,
NULL
};
int pixelFormat[256] = { 0 };
UINT formatNum = 0;
int ret = wglChoosePixelFormatARB(dc, attribs, NULL, 256, pixelFormat, &formatNum);
if (formatNum > 0)
{
PIXELFORMATDESCRIPTOR pfd;
DescribePixelFormat(dc, pixelFormat[0], sizeof(PIXELFORMATDESCRIPTOR), &pfd);
SetPixelFormat(dc, pixelFormat[0], &pfd);
int contexAttributes[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
0
};
rc = wglCreateContextAttribsARB(dc, nullptr, contexAttributes);
}
return rc;
}
把当前窗口设置为OpenGL上下文
在构造函数中处理
GLEW4QT::GLEW4QT(QWidget *parent)
: QWidget(parent, Qt::MSWindowsOwnDC)
{
setAttribute(Qt::WA_PaintOnScreen);
setAttribute(Qt::WA_NoSystemBackground);
setAutoFillBackground(true);
int major = 0, minor = 0;
dc = GetDC((HWND)winId());
bool supportMSAA = CheckSupportGLLevel(&major, &minor) == 1;
rc = CreateGLRC(dc, supportMSAA, 16);
qDebug() << "OpenGL Version:" << major << "." << minor;
if (!wglMakeCurrent(dc, rc))
throw;
ui.setupUi(this);
}
也可以关闭垂直同步,让OpenGL火力全开
//是否开启垂直同步
wglSwapIntervalEXT(false);
在析构函数中要记得释放DC和RC
GLEW4QT::~GLEW4QT()
{
wglMakeCurrent(nullptr, nullptr);
if (rc)
wglDeleteContext(rc);
if (dc)
ReleaseDC((HWND)winId(), dc);
}
绘制背景颜色
更新视口
重写resizeEvent,并更新OpenGL视口
void GLEW4QT::resizeEvent(QResizeEvent *event)
{
glViewport(0, 0, event->size().width(), event->size().height());
}
绘制
void GLEW4QT::Renderer()
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
SwapBuffers(dc);
}
Renderer不要直接调用,我们把他放在event中调用
添加QtEvent
class QtEvent : public QEvent
{
public:
enum Type
{
GL_Renderer = QEvent::User + 1,
};
QtEvent(Type type) : QEvent(QEvent::Type(type)) {};
};
调用绘制
bool GLEW4QT::event(QEvent *event)
{
if (event->type() == QtEvent::GL_Renderer)
{
Renderer();
return true;
}
return QWidget::event(event);
}
Post绘制事件
void GLEW4QT::GLUpdate()
{
QApplication::postEvent(this, new QtEvent(QtEvent::GL_Renderer));
}
这样在需要重新绘制的地方调用GLUpdate即可
void GLEW4QT::resizeEvent(QResizeEvent *event)
{
glViewport(0, 0, event->size().width(), event->size().height());
GLUpdate();
}
绘制测试
通过改变颜色测试OGL环境
构造函数中添加定时器改变red值
...
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, [this] {
red += 0.01f;
if (red > 1.0f) red = 0.0f;
GLUpdate();
});
timer->start(30);
...
Renderer更新颜色值
...
glClearColor(red, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
...
测试效果
源码下载
如果您在测试的过程中某个环节出错了,请下载源码查看
源码链接: 传送门.
开启新征程
通过上面的一番操作就可以在Qt中使用原生的OpenGL了。