1.xvideo.h
#pragma once
#include <QOpenGLWidget>
#include<qopenglfunctions.h>
#include<qglshaderprogram.h>
class xvideo : public QOpenGLWidget,protected QOpenGLFunctions
{
Q_OBJECT
public:
xvideo(QWidget *parent);
~xvideo();
protected:
void initialGL();//初始化GL
void paintGL(); //绘制GL
void resizeGL(int width,int height);//改尺寸
private:
//通过该成员运行shader程序
QGLShaderProgram program;
//shader中yuv变量地址 unis_y,unis_u,unis_v
GLuint unis[3] = { 0 };
//opengl的 texture地址
GLuint texs[3] = { 0 };
//材质内存空间
unsigned char* datas[3] = { 0 };
int width = 240;
int height = 128;
};
2.void xvideo::initialGL()
void xvideo::initialGL() //初始化GL
{
qDebug() << "initialGL" << endl;
initializeOpenGLFunctions();
qDebug() << program.addShaderFromSourceCode(QGLShader::Fragment, Tstring);
//顶点shader
qDebug() << program.addShaderFromSourceCode(QGLShader::Vertex, Vstring);
用QT封装的program加载shader脚本(顶点shader和片元shader)
Tsting和Vstring为指针,指向的空间中放有我们定义的着色器的功能(顶点着色器用于转发,片元着色器用于将YUV转换成RGB)
const char* Vstring = GET_STR(
attribute vec4 vertexIn;//顶点输入
attribute vec2 textureIn;//材质输入
varying vec2 textureOut;//顶点与片元shader共享变量
void main(void)
{
//传进来的坐标值只能在顶点中获取
gl_Position = vertexIn;
textureOut = textureIn;
}
);
为了设置顶点着色器的输出,我们必须把位置数据赋值给预定义的gl_Position变量,它在幕后是vec4类型的。
program.bindAttributeLocation("vertexIn", A_VER);
program.bindAttributeLocation("textureIn", T_VER);
设置顶点坐标与材质坐标的变量 使用bindAttributeLocation来设置变量在着色器程序中参数列表的索引。(A_VER与T_VER都是经过宏定义的整型)
vertexIn为shader中的,OpenGL并不认识,因此这里将vertexIn与索引A_VER绑定,后面往OpenGL写顶点坐标或者材质坐标时往A_VER或者T_VER里传就行了,比如:glVertexAttribPointer(A_VER, 2, GL_FLOAT, 0, 0, ver);
qDebug() << "program.link() = " << program.link();
qDebug() << "program.bind() = " << program.bind();
编译shader
绑定shader与OpenGL
static const GLfloat ver[] = {
-1.0f,-1.0f,
1.0f,-1.0f,
-1.0f, 1.0f,
1.0f,1.0f
};
static const GLfloat tex[] = {
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f
};
传递顶点和材质坐标
顶点 z坐标默认都为0
顶点坐标在这个线程使用完后面draw还要用,因此设为static
glVertexAttribPointer(A_VER, 2, GL_FLOAT, 0, 0, ver);
glEnableVertexAttribArray(A_VER);
glVertexAttribPointer(T_VER, 2, GL_FLOAT, 0, 0, tex);
glEnableVertexAttribArray(T_VER);
将顶点与材质坐标写入OpenGL中
glVertexAttribPointer()指定了渲染时着色器索引值为 index 的顶点属性数组,(颜色、纹理、法线坐标)的数据格式和位置,用于从内存向显存上传数据
然后使坐标生效
unis[0] = program.uniformLocation("tex_y");
unis[1] = program.uniformLocation("tex_u");
unis[2] = program.uniformLocation("tex_v");
glGenTextures(3, texs);
从shader获取材质,等会渲染的时候再传过去
根据储存的纹理索(texs)引texs创建三层材质
texs:存储纹理索引的第一个元素指针
(glGenTextures就是用来产生你要操作的纹理对象的索引的,比如你告诉OpenGL,我需要3个纹理对象,它会从没有用到的整数里返回3个给你,然后将索引放到texs中
//Y 将上面创建的三个材质中的第一个绑定成2D的图像
glBindTexture(GL_TEXTURE_2D, texs[0]);
//放大缩小过滤,线性插值 GL_NEAREST(效率高,但马赛克严重)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//在显存中创建材质
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
glBindTexture(GL_TEXTURE_2D, texs[1]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//创建材质显卡空间
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width / 2, height / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
//V
glBindTexture(GL_TEXTURE_2D, texs[2]);
//放大过滤,线性插值
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//创建材质显卡空间
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width / 2, height / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
///分配材质内存空间
datas[0] = new unsigned char[width * height];
datas[1] = new unsigned char[width * height / 4];
datas[2] = new unsigned char[width * height / 4];
fp = fopen("out240x128.yuv", "rb");
if (!fp)
{
qDebug() << "out240x128.yuv file open failed!";
}
QTimer* ti = new QTimer(this);
connect(ti, SIGNAL(timeout()), this, SLOT(update()));
ti->start(40);
}
在初始化中只打开了YUV文件
初始化中主要完成了初始化,分配显存或内存空间,定义缩放算法等任务
其中因为我们所使用的YUV为YUV420P,即4个Y对应一组(共2个)UV,所以Y:U:V=4:1:1,所以Y占的内存空间为U的4倍,即U的宽高都为Y的一半
3.void xvideo::paintGL() //绘制GL
void xvideo::paintGL() //绘制GL
{
qDebug() << "paintGL" << endl;
if (feof(fp))fseek(fp, 0, SEEK_SET);
//将fp指向的视频数据中的数据读到datas中
fread(datas[0], 1, width * height, fp);
fread(datas[1], 1, width * height/4, fp);
fread(datas[2], 1, width * height/4, fp);
//激活材质,参数为材质的编号
glActiveTexture(GL_TEXTURE0);
//显卡中创建的材质绑定到第0层渲染材质
glBindTexture(GL_TEXTURE_2D, texs[0]); //显卡中的0层材质绑定到之前创建好索引的材质texs[0]
//修改材质内容(复制内存内容)将显卡中创建好的材质传到内存当中
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, datas[0]);
//现在第一个材质创建完毕,接下来将其与shader关联
// uni为shader中的变量,第一个unis与第0层材质相绑定
glUniform1i(unis[0], 0);
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_2D, texs[1]); //1层绑定到U材质
//修改材质内容(复制内存内容)
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width / 2, height / 2, GL_RED, GL_UNSIGNED_BYTE, datas[1]);
//与shader uni遍历关联
glUniform1i(unis[1], 1);
glActiveTexture(GL_TEXTURE0 + 2);
glBindTexture(GL_TEXTURE_2D, texs[2]); //2层绑定到V材质
//修改材质内容(复制内存内容)
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width / 2, height / 2, GL_RED, GL_UNSIGNED_BYTE, datas[2]);
//与shader uni遍历关联
glUniform1i(unis[2], 2);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);//从0开始画4个顶点开始绘制三角形
qDebug() << "paintGL";
}
glActiveTexture(GL_TEXTURE0 + 1);
由于OpenGL是状态机,当使用glBindTexture绑定一张纹理后,如果不再绑定新的纹理,则OpenGL之后的操作都会对应此纹理,
当一个纹理与目标绑定时,该目标之前的绑定关系(GL_TEXTURE0)将自动被打破,此时的绑定关系为(GL_TEXTURE1)
将以下四个相关联:
glBindTexture(GL_TEXTURE_2D, texs[1]);
将之前创建好索引的材质texs[1]绑定为2D图像,并且绑定到现在所使用的第1层材质上
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, datas[0]);
将显存中创建好的材质(texs[1]/第1层材质)传到内存当中
现在第一个材质创建完毕,接下来将其与shader关联
uni为shader中的变量,第一个unis与第0层材质相绑定
glUniform1i(unis[1], 1);