介绍
QT中提供了QOpenGLWidget来封装OpenGL流程,原先的QGLWidget已经被QOpenGLWidget取代。
QOpenGLWidget给出的接口更加简洁,方便使用,一般只需重写下面三个接口即可
virtual void initializeGL();
virtual void resizeGL(int w, int h);
virtual void paintGL();
initializeGL一般做一些初始化的工作,比如创建着色器,创建VBO,初始化基本设置等
resizeGL里一般需要重新设置视口glViewport,重新设置坐标系等;
paintGL则是我们需要绘制的东西,可以渲染纹理,绘制顶点等;
当然QOpenGLWidget是继承自QWidget类的,所以响应鼠标键盘事件不在话下。
代码示例
下面贴出一个我使用QOpenGLWidget控件来显示yuv视频的示例:
struct Texture{
GLuint texID; // glGenTextures分配的ID
GLuint type; // 数据类型如GL_RGB
GLint width;
GLint height;
GLint bpp;
GLubyte* data; // 像素数据
Texture(){
texID = 0;
type = GL_RGBA;
width = 0;
height = 0;
bpp = 0;
data = NULL;
}
~Texture(){
release();
}
void release(){
if (data != NULL){
free(data);
data = NULL;
}
width = 0;
height = 0;
}
};
struct DrawInfo{
int left;
int right;
int top;
int bottom;
GLuint color;
};
class HGLWidget : public QOpenGLWidget
{
Q_OBJECT
public:
HGLWidget(QWidget* parent = Q_NULLPTR);
virtual ~HGLWidget();
protected:
virtual void initializeGL();
virtual void resizeGL(int w, int h);
virtual void paintGL();
protected:
static void loadYUVShader();
void initVAO();
void drawYUV(Texture* tex);
void drawTex(Texture* tex, DrawInfo* di);
void drawStr(FTGLPixmapFont *pFont, const char* str, DrawInfo* di);
void drawRect(DrawInfo* di, bool bFill = false);
private:
static bool s_bInitGLEW;
static GLuint prog_yuv;
static GLuint texUniformY;
static GLuint texUniformU;
static GLuint texUniformV;
GLuint tex_yuv[3];
enum E_VER_ATTR{ver_attr_ver = 3, ver_attr_tex = 4, ver_attr_num};
}
新派生了一个类HGLWidget,继承自QOpenGLWidget,重写initializeGL、resizeGL和paintGL,Texture和DrawInfo皆是我定义的用来保存纹理图和绘画信息的结构体,drawXXX方法就是绘制YUV、纹理、字体、矩形的通用方法,下面给出cpp中具体的实现
void HGLWidget::initializeGL(){
if (!s_bInitGLEW){
if (glewInit() != 0){
qFatal("glewInit failed");
return;
}
loadYUVShader();
s_bInitGLEW = true;
}
initVAO();
}
void HGLWidget::resizeGL(int w, int h){
glViewport(0, 0, w, h);
}
void HGLWidget::paintGL(){
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
DrawInfo di;
DsItemInfo* item = g_dsCtx->getItem(svrid);
if (m_status & PLAY_VIDEO){
// draw yuv
item->mutex.lock();
if (item->tex_yuv.data && item->tex_yuv.width > 0 && item->tex_yuv.height > 0){
drawYUV(&item->tex_yuv);
}
item->mutex.unlock();
}
// draw title
if (m_title.length() > 0){
di.left = 2;
di.top = 2;
di.color = m_titcolor;
drawStr(g_dsCtx->m_pFont, m_title.c_str(), &di);
}
// draw outline
di.left = 1;
di.top = 1;
di.right = width() - 1;
di.bottom = height() - 1;
di.color = m_outlinecolor;
drawRect(&di);
}
void HGLWidget::loadYUVShader(){
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
char szVS[] = " \
attribute vec4 verIn; \
attribute vec2 texIn; \
varying vec2 texOut; \
\
void main(){ \
gl_Position = verIn; \
texOut = texIn; \
} \
";
const GLchar* pszVS = szVS;
GLint len = strlen(szVS);
glShaderSource(vs, 1, (const GLchar**)&pszVS, &len);
char szFS[] = " \
varying vec2 texOut; \
uniform sampler2D tex_y; \
uniform sampler2D tex_u; \
uniform sampler2D tex_v; \
\
void main(){ \
vec3 yuv; \
vec3 rgb; \
yuv.x = texture2D(tex_y, texOut).r; \
yuv.y = texture2D(tex_u, texOut).r - 0.5; \
yuv.z = texture2D(tex_v, texOut).r - 0.5; \
rgb = mat3( 1, 1, 1, \
0, -0.39465, 2.03211, \
1.13983, -0.58060, 0) * yuv; \
gl_FragColor = vec4(rgb, 1); \
} \
";
const GLchar* pszFS = szFS;
len = strlen(szFS);
glShaderSource(fs, 1, (const GLchar**)&pszFS, &len);
glCompileShader(vs);
glCompileShader(fs);
//#ifdef _DEBUG
GLint iRet = 0;
glGetShaderiv(vs, GL_COMPILE_STATUS, &iRet);
qDebug("vs::GL_COMPILE_STATUS=%d", iRet);
glGetShaderiv(fs, GL_COMPILE_STATUS, &iRet);
qDebug("fs::GL_COMPILE_STATUS=%d", iRet);
//#endif
prog_yuv = glCreateProgram();
glAttachShader(prog_yuv, vs);
glAttachShader(prog_yuv, fs);
glBindAttribLocation(prog_yuv, ver_attr_ver, "verIn");
glBindAttribLocation(prog_yuv, ver_attr_tex, "texIn");
glLinkProgram(prog_yuv);
//#ifdef _DEBUG
glGetProgramiv(prog_yuv, GL_LINK_STATUS, &iRet);
qDebug("prog_yuv=%d GL_LINK_STATUS=%d", prog_yuv, iRet);
//#endif
glValidateProgram(prog_yuv);
texUniformY = glGetUniformLocation(prog_yuv, "tex_y");
texUniformU = glGetUniformLocation(prog_yuv, "tex_u");
texUniformV = glGetUniformLocation(prog_yuv, "tex_v");
qDebug("loadYUVShader ok");
}
void HGLWidget::initVAO(){
static const GLfloat vertices[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f,
};
static const GLfloat textures[] = {
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
};
// reverse
//static const GLfloat textures[] = {
// 0.0f, 0.0f,
// 1.0f, 0.0f,
// 0.0f, 1.0f,
// 1.0f, 1.0f,
//};
//glGenBuffers(buffer_num, g_buffers);
//glBindBuffer(GL_ARRAY_BUFFER, g_buffers[buffer_ver]);
//glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//glVertexAttribPointer(ver_attr_ver, 2, GL_FLOAT, GL_FALSE, 0, 0);
glVertexAttribPointer(ver_attr_ver, 2, GL_FLOAT, GL_FALSE, 0, vertices);
glEnableVertexAttribArray(ver_attr_ver);
//glGenBuffers(buffer_num, g_buffers);
//glBindBuffer(GL_ARRAY_BUFFER, g_buffers[buffer_tex]);
//glBufferData(GL_ARRAY_BUFFER, sizeof(textures), textures, GL_STATIC_DRAW);
//glVertexAttribPointer(ver_attr_tex, 2, GL_FLOAT, GL_FALSE, 0, 0);
glVertexAttribPointer(ver_attr_tex, 2, GL_FLOAT, GL_FALSE, 0, textures);
glEnableVertexAttribArray(ver_attr_tex);
glGenTextures(3, tex_yuv);
for (int i = 0; i < 3; i++){
glBindTexture(GL_TEXTURE_2D, tex_yuv[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
}
void HGLWidget::drawYUV(Texture* tex){
glUseProgram(prog_yuv);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
int w = tex->width;
int h = tex->height;
//qDebug("drawYUV w=%d h=%d", w, h);
int y_size = w*h;
GLubyte* y = tex->data;
GLubyte* u = y + y_size;
GLubyte* v = u + (y_size>>2);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex_yuv[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, y);
glUniform1i(texUniformY, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, tex_yuv[1]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w/2, h/2, 0, GL_RED, GL_UNSIGNED_BYTE, u);
glUniform1i(texUniformU, 1);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, tex_yuv[2]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w/2, h/2, 0, GL_RED, GL_UNSIGNED_BYTE, v);
glUniform1i(texUniformV, 2);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
void HGLWidget::drawTex(Texture* tex, DrawInfo* di){
glUseProgram(0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, width(), height(), 0.0, -1.0, 1.0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex->texID);
gluBuild2DMipmaps(GL_TEXTURE_2D, tex->bpp/8, tex->width, tex->height, tex->type, GL_UNSIGNED_BYTE, tex->data);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBegin(GL_QUADS);
//glTexCoord2d(0,0);glVertex2f(-1, -1);
//glTexCoord2d(1,0);glVertex2f( 1, -1);
//glTexCoord2d(1,1);glVertex2f( 1, 1);
//glTexCoord2d(0,1);glVertex2f(-1, 1);
glTexCoord2d(0,0);glVertex2i(di->left, di->bottom);
glTexCoord2d(1,0);glVertex2i(di->right, di->bottom);
glTexCoord2d(1,1);glVertex2f(di->right, di->top);
glTexCoord2d(0,1);glVertex2f(di->left, di->top);
glEnd();
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
}
void HGLWidget::drawStr(FTGLPixmapFont *pFont, const char* str, DrawInfo* di){
if (!pFont)
return ;
glUseProgram(0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, width(), height(), 0.0, -1.0, 1.0);
const char* mbs = str;
int len_mbs = strlen(mbs);
#ifdef WIN32
setlocale(LC_ALL,".936");
#else
setlocale(LC_ALL,"zh_CN.utf8");
#endif
int len_wcs = mbstowcs(NULL, mbs, 0);
wchar_t* wcs = new wchar_t[len_wcs + 1];
mbstowcs(wcs, mbs, strlen(mbs)+1);
glColor3ub(R(di->color), G(di->color), B(di->color));
glRasterPos2i(di->left, di->top + pFont->LineHeight());
pFont->Render(wcs);
glColor3ub(255,255,255);
delete[] wcs;
}
void HGLWidget::drawRect(DrawInfo* di, bool bFill){
glUseProgram(0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, width(), height(), 0.0, -1.0, 1.0);
if (bFill){
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}else{
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4ub(R(di->color), G(di->color), B(di->color), A(di->color));
glRecti(di->left, di->top, di->right, di->bottom);
glColor4ub(255,255,255,255);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glDisable(GL_BLEND);
}
drawYUV中调用drawYUV的部分,我是从一块buffer中取yuv数据,然后绘制,这部分你替换成自己的就可以了。
其它定义的一些方法drawRect、drawStr、drawTex、drawYUV都是可以通用的,在paintGL中调用就可以了。
后记
之所以采用QT + OpenGL,因为QT构建界面比glut更完善,glut只适合做一个简单的显示窗口,SDL也有类似的考虑,不适合做实际项目的界面,而OpenGL利用GPU加速,释放出CPU的占用资源,特别是QT中提供的QOpenGLWidget类更加方便了OpenGL的使用。

本文介绍如何使用QT中的QOpenGLWidget类来封装OpenGL流程,并展示一个使用该类显示YUV视频的具体示例。通过继承QOpenGLWidget并重写关键方法,可以轻松地将OpenGL功能集成到QT应用程序中。
2560

被折叠的 条评论
为什么被折叠?



