QT之OpenGL

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

介绍

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);

privatestatic 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的使用。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ithewei

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值