You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

245 lines
8.1 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <QCoreApplication>
#include <QOpenGLTexture>
#include "qyuvopenglwidget.h"
// 存储顶点坐标和纹理坐标
// 存在一起缓存在vbo
// 使用glVertexAttribPointer指定访问方式即可
static const GLfloat coordinate[] = {
// 顶点坐标存储4个xyz坐标
// 坐标范围为[-1,1],中心点为 0,0
// 二维图像z始终为0
// GL_TRIANGLE_STRIP的绘制方式
// 使用前3个坐标绘制一个三角形使用后三个坐标绘制一个三角形正好为一个矩形
// x y z
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
-1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
// 纹理坐标存储4个xy坐标
// 坐标范围为[0,1],左下角为 0,0
// TODO 为什么这个顺序指定四个顶点?顶点坐标和纹理坐标如何映射的?
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f
};
// 顶点着色器
static const QString s_vertShader = R"(
attribute vec3 vertexIn; // xyz顶点坐标
attribute vec2 textureIn; // xy纹理坐标
varying vec2 textureOut; // 传递给片段着色器的纹理坐标
void main(void)
{
gl_Position = vec4(vertexIn, 1.0); // 1.0表示vertexIn是一个顶点位置
textureOut = textureIn; // 纹理坐标直接传递给片段着色器
}
)";
// 片段着色器
static QString s_fragShader = R"(
varying vec2 textureOut; // 由顶点着色器传递过来的纹理坐标
uniform sampler2D textureY; // uniform 纹理单元,利用纹理单元可以使用多个纹理
uniform sampler2D textureU; // sampler2D是2D采样器
uniform sampler2D textureV; // 声明yuv三个纹理单元
void main(void)
{
vec3 yuv;
vec3 rgb;
// 根据指定的纹理textureY和坐标textureOut来采样
yuv.x = texture2D(textureY, textureOut).r;
yuv.y = texture2D(textureU, textureOut).r - 0.5;
yuv.z = texture2D(textureV, textureOut).r - 0.5;
// 采样完转为rgb
rgb = mat3(1.0, 1.0, 1.0,
0.0, -0.39465, 2.03211,
1.13983, -0.58060, 0.0) * yuv;
// 输出颜色值
gl_FragColor = vec4(rgb, 1.0);
}
)";
QYUVOpenGLWidget::QYUVOpenGLWidget(QWidget *parent) : QOpenGLWidget(parent)
{
}
QYUVOpenGLWidget::~QYUVOpenGLWidget()
{
makeCurrent();
m_vbo.destroy();
deInitTextures();
doneCurrent();
}
QSize QYUVOpenGLWidget::minimumSizeHint() const
{
return QSize(50, 50);
}
QSize QYUVOpenGLWidget::sizeHint() const
{
return size();
}
void QYUVOpenGLWidget::setFrameSize(const QSize &frameSize)
{
if (m_frameSize != frameSize) {
m_frameSize = frameSize;
m_needUpdate = true;
// inittexture immediately
repaint();
}
}
const QSize& QYUVOpenGLWidget::frameSize()
{
return m_frameSize;
}
void QYUVOpenGLWidget::updateTextures(quint8 *dataY, quint8 *dataU, quint8 *dataV, quint32 linesizeY, quint32 linesizeU, quint32 linesizeV)
{
if (m_textureInited) {
updateTexture(m_texture[0], 0, dataY, linesizeY);
updateTexture(m_texture[1], 1, dataU, linesizeU);
updateTexture(m_texture[2], 2, dataV, linesizeV);
update();
}
}
void QYUVOpenGLWidget::initializeGL()
{
initializeOpenGLFunctions();
glDisable(GL_DEPTH_TEST);
// 顶点缓冲对象初始化
m_vbo.create();
m_vbo.bind();
m_vbo.allocate(coordinate, sizeof(coordinate));
initShader();
// 设置背景清理色为黑色
glClearColor(0.0,0.0,0.0,0.0);
// 清理颜色背景
glClear(GL_COLOR_BUFFER_BIT);
}
void QYUVOpenGLWidget::paintGL()
{
if (m_needUpdate) {
deInitTextures();
initTextures();
m_needUpdate = false;
}
if (m_textureInited) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_texture[0]);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, m_texture[1]);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, m_texture[2]);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
}
void QYUVOpenGLWidget::resizeGL(int width, int height)
{
glViewport(0, 0, width, height);
repaint();
}
void QYUVOpenGLWidget::initShader()
{
// opengles的float、int等要手动指定精度
if (QCoreApplication::testAttribute(Qt::AA_UseOpenGLES)) {
s_fragShader.prepend(R"(
precision mediump int;
precision mediump float;
)");
}
m_shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, s_vertShader);
m_shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, s_fragShader);
m_shaderProgram.link();
m_shaderProgram.bind();
// 指定顶点坐标在vbo中的访问方式
// 参数解释顶点坐标在shader中的参数名称顶点坐标为float起始偏移为0顶点坐标类型为vec3步幅为3个float
m_shaderProgram.setAttributeBuffer("vertexIn", GL_FLOAT, 0, 3, 3 * sizeof(float));
// 启用顶点属性
m_shaderProgram.enableAttributeArray("vertexIn");
// 指定纹理坐标在vbo中的访问方式
// 参数解释纹理坐标在shader中的参数名称纹理坐标为float起始偏移为12个float跳过前面存储的12个顶点坐标纹理坐标类型为vec2步幅为2个float
m_shaderProgram.setAttributeBuffer("textureIn", GL_FLOAT, 12 * sizeof(float), 2, 2 * sizeof(float));
m_shaderProgram.enableAttributeArray("textureIn");
// 关联片段着色器中的纹理单元和opengl中的纹理单元opengl一般提供16个纹理单元
m_shaderProgram.setUniformValue("textureY", 0);
m_shaderProgram.setUniformValue("textureU", 1);
m_shaderProgram.setUniformValue("textureV", 2);
}
void QYUVOpenGLWidget::initTextures()
{
// 创建纹理
glGenTextures(1, &m_texture[0]);
glBindTexture(GL_TEXTURE_2D, m_texture[0]);
// 设置纹理缩放时的策略
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 设置st方向上纹理超出坐标时的显示策略
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_frameSize.width(), m_frameSize.height(), 0, GL_RED, GL_UNSIGNED_BYTE, NULL);
glGenTextures(1, &m_texture[1]);
glBindTexture(GL_TEXTURE_2D, m_texture[1]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_frameSize.width()/2, m_frameSize.height()/2, 0, GL_RED, GL_UNSIGNED_BYTE, NULL);
glGenTextures(1, &m_texture[2]);
glBindTexture(GL_TEXTURE_2D, m_texture[2]);
// 设置纹理缩放时的策略
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 设置st方向上纹理超出坐标时的显示策略
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_frameSize.width()/2, m_frameSize.height()/2, 0, GL_RED, GL_UNSIGNED_BYTE, NULL);
m_textureInited = true;
}
void QYUVOpenGLWidget::deInitTextures()
{
if (QOpenGLFunctions::isInitialized(QOpenGLFunctions::d_ptr)) {
glDeleteTextures(3, m_texture);
}
memset(m_texture, 0, 3);
m_textureInited = false;
}
void QYUVOpenGLWidget::updateTexture(GLuint texture, quint32 textureType, quint8 *pixels, quint32 stride)
{
if (!pixels)
return;
QSize size = 0 == textureType ? m_frameSize : m_frameSize/2;
makeCurrent();
glBindTexture(GL_TEXTURE_2D, texture);
glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size.width(), size.height(), GL_RED, GL_UNSIGNED_BYTE, pixels);
doneCurrent();
}