这个兄弟实现了这个:qt使用opengl绘制YUV数据图像_求知者先的博客-CSDN博客
不过烦就烦在代码不全,好人做到底:
1、找一个qcif(176x144)分辨率的yuv文件到c盘
2、copy如下代码运行即可
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); { auto t = new QTimer(this); auto f = new QFile("C:/hall_qcif-176x144.yuv",this); auto ba = new QByteArray(176*144*1.5,0); f->open(QIODevice::ReadOnly); qDebug(" c=%d",ba->count()); connect(t,&QTimer::timeout,this,[=](){ // const int w=176,h=144; if(f->read(ba->data(),ba->count()) <=0){ f->seek(0); if(f->read(ba->data(),ba->count()) <=0){ qDebug("error again"); t->stop(); return; } } ui->yuvWidget->onShowYuv((uchar*)(ba->data()),w,h); }); t->setInterval(25); t->start(); } }
#ifndef GLYUVWIDGET_H #define GLYUVWIDGET_H #include <QOpenGLWidget> #include <QOpenGLFunctions> #include <QOpenGLBuffer> #include <QTimer> QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram) QT_FORWARD_DECLARE_CLASS(QOpenGLTexture) class GLYuvWidget : public QOpenGLWidget, protected QOpenGLFunctions { Q_OBJECT public: GLYuvWidget(QWidget *parent =0); ~GLYuvWidget(); public slots: void slotShowYuv(uchar *ptr,uint width,uint height); //显示一帧Yuv图像 protected: void initializeGL() Q_DECL_OVERRIDE; void paintGL() Q_DECL_OVERRIDE; private: QOpenGLShaderProgram *program; QOpenGLBuffer vbo; GLuint textureUniformY,textureUniformU,textureUniformV; //opengl中y、u、v分量位置 QOpenGLTexture *textureY = nullptr,*textureU = nullptr,*textureV = nullptr; GLuint idY,idU,idV; //自己创建的纹理对象ID,创建错误返回0 uint videoW,videoH; uchar *yuvPtr = nullptr; }; #endif // GLYUVWIDGET_H
#include "glyuvwidget.h" #include <QOpenGLShaderProgram> #include <QOpenGLTexture> #include <QDebug> #define VERTEXIN 0 #define TEXTUREIN 1 GLYuvWidget::GLYuvWidget(QWidget *parent): QOpenGLWidget(parent) { } GLYuvWidget::~GLYuvWidget() { makeCurrent(); vbo.destroy(); textureY->destroy(); textureU->destroy(); textureV->destroy(); doneCurrent(); } void GLYuvWidget::onShowYuv(uchar *ptr, uint width, uint height) { yuvPtr = ptr; videoW = width; videoH = height; update(); //调用update将执行paintEvent函数 } void GLYuvWidget::initializeGL() { initializeOpenGLFunctions(); glEnable(GL_DEPTH_TEST); static const GLfloat vertices[]{ //顶点坐标 -1.0f,-1.0f, -1.0f,+1.0f, +1.0f,+1.0f, +1.0f,-1.0f, //纹理坐标 0.0f,1.0f, 0.0f,0.0f, 1.0f,0.0f, 1.0f,1.0f, }; vbo.create(); vbo.bind(); vbo.allocate(vertices,sizeof(vertices)); //初始化顶点着色器对象 QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex,this); //顶点着色器源码 const char *vsrc = "attribute vec4 vertexIn; \ attribute vec2 textureIn; \ varying vec2 textureOut; \ void main(void) \ { \ gl_Position = vertexIn; \ textureOut = textureIn; \ }"; vshader->compileSourceCode(vsrc); //编译顶点着色器程序 //初始化片段着色器 功能gpu中yuv转换成rgb QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment,this); //片段着色器源码 const char *fsrc = #if defined(WIN32) //windows下opengl es 需要加上float这句话 "#ifdef GL_ES\n" "precision mediump float;\n" "#endif\n" #endif "varying vec2 textureOut; \ uniform sampler2D tex_y; \ uniform sampler2D tex_u; \ uniform sampler2D tex_v; \ void main(void) \ { \ vec3 yuv; \ vec3 rgb; \ yuv.x = texture2D(tex_y, textureOut).r; \ yuv.y = texture2D(tex_u, textureOut).r - 0.5; \ yuv.z = texture2D(tex_v, textureOut).r - 0.5; \ rgb = mat3( 1, 1, 1, \ 0, -0.39465, 2.03211, \ 1.13983, -0.58060, 0) * yuv; \ gl_FragColor = vec4(rgb, 1); \ }"; fshader->compileSourceCode(fsrc); //将glsl源码送入编译器编译着色器程序 //用于绘制矩形 program = new QOpenGLShaderProgram(this); program->addShader(vshader); //将顶点着色器添加到程序容器 program->addShader(fshader); //将片段着色器添加到程序容器 //绑定属性vertexIn到指定位置ATTRIB_VERTEX,该属性在顶点着色源码其中有声明 program->bindAttributeLocation("vertexIn",VERTEXIN); //绑定属性textureIn到指定位置ATTRIB_TEXTURE,该属性在顶点着色源码其中有声明 program->bindAttributeLocation("textureIn",TEXTUREIN); program->link(); //链接所有所有添入到的着色器程序 program->bind(); //激活所有链接 program->enableAttributeArray(VERTEXIN); program->enableAttributeArray(TEXTUREIN); program->setAttributeBuffer(VERTEXIN,GL_FLOAT,0,2,2*sizeof(GLfloat)); program->setAttributeBuffer(TEXTUREIN,GL_FLOAT,8*sizeof(GLfloat),2,2*sizeof(GLfloat)); //读取着色器中的数据变量tex_y, tex_u, tex_v的位置,这些变量的声明可以在片段着色器源码中可以看到 textureUniformY = program->uniformLocation("tex_y"); textureUniformU = program->uniformLocation("tex_u"); textureUniformV = program->uniformLocation("tex_v"); //分别创建y,u,v纹理对象 textureY = new QOpenGLTexture(QOpenGLTexture::Target2D); textureU = new QOpenGLTexture(QOpenGLTexture::Target2D); textureV = new QOpenGLTexture(QOpenGLTexture::Target2D); textureY->create(); textureU->create(); textureV->create(); idY = textureY->textureId(); //获取返回y分量的纹理索引值 idU = textureU->textureId(); //获取返回u分量的纹理索引值 idV = textureV->textureId(); //获取返回v分量的纹理索引值 glClearColor(0.0,0.0,0.0,0.0); //设置背景色为黑色 } void GLYuvWidget::paintGL() { //------- 加载y数据纹理 ------- glActiveTexture(GL_TEXTURE0); //激活纹理单元GL_TEXTURE0,系统里面的 glBindTexture(GL_TEXTURE_2D,idY); //绑定y分量纹理对象id到激活的纹理单元 //使用内存中的数据创建真正的y分量纹理数据 glTexImage2D(GL_TEXTURE_2D,0,GL_RED,videoW,videoH,0,GL_RED,GL_UNSIGNED_BYTE,yuvPtr); 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); //------- 加载u数据纹理 ------- glActiveTexture(GL_TEXTURE1); //激活纹理单元GL_TEXTURE1 glBindTexture(GL_TEXTURE_2D,idU); //使用内存中的数据创建真正的u分量纹理数据 glTexImage2D(GL_TEXTURE_2D,0,GL_RED,videoW >> 1, videoH >> 1,0,GL_RED,GL_UNSIGNED_BYTE,yuvPtr + videoW * videoH); 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); //------- 加载v数据纹理 ------- glActiveTexture(GL_TEXTURE2); //激活纹理单元GL_TEXTURE2 glBindTexture(GL_TEXTURE_2D,idV); //使用内存中的数据创建真正的v分量纹理数据 glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, videoW >> 1, videoH >> 1, 0, GL_RED, GL_UNSIGNED_BYTE, yuvPtr+videoW*videoH*5/4); 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); //指定y纹理要使用新值,只能用0,1,2等表示纹理单元的索引,这是opengl不人性化的地方 glUniform1i(textureUniformY, 0); //0对应纹理单元GL_TEXTURE0,//指定y纹理要使用新值 glUniform1i(textureUniformU, 1); //1对应纹理单元GL_TEXTURE1,//指定u纹理要使用新值 glUniform1i(textureUniformV, 2); //2对应纹理的单元GL_TEXTURE2,指定v纹理要使用新值 //使用顶点数组方式绘制图形 glDrawArrays(GL_TRIANGLE_FAN, 0, 4); }
注意,这是OPENGL2.x的代码,设为3.3不可运行。
同时不支持QPainter混合编程,要QPainter需要改为3.3版本的(测试发现)。需要的可以发邮件给我。