找回密码
 立即注册
首页 业界区 业界 OpenGL渲染YUV实战:GPU加速转换与MipMap模糊效果实现 ...

OpenGL渲染YUV实战:GPU加速转换与MipMap模糊效果实现

任佳湍 5 天前
本文介绍如何采用 Qt + OpenGL 绘制 YUV 数据,并通过 OpenGL 来实现画质模糊。
前言

我们在开发音视频程序的时候,对于解码后帧的渲染往往有几个操作需要做:

  • 将 YUV420 格式的图像数据转换成 RGB 格式
  • 渲染 RGB 图像
在普通方案中,格式转换以及渲染都是在 CPU 中去做。而要降低 CPU 使用率,我们可以将上面这两个操作通通移动到 GPU 中去做,具体如果实现,请往下看。
OpenGL 渲染

我们通过 Qt 提供的 QOpenGLWidget 和 QOpenGLFunction 来进行画面的 GPU 渲染(其实 Qt 也只是对 OpenGL 接口的一套封装,类似于 GLAD 和 GLFW)。关于如何使用这两个类,本文不再赘述,如果不明白的同学,可以看我的这篇文章。
YUV420P

下面先让我们简单的学习一下 YUV420P 。YUV420P是一种常用的图像格式,主要用于视频处理和存储。它由三个平面构成:Y(亮度)、U(色度蓝色)和V(色度红色)。Y平面存储所有像素的亮度信息,而U和V平面只存储每四个Y像素对应的色度信息。这种采样方式使得YUV420P比RGB更高效,因为它减少了色度数据的存储量,从而节省了存储空间和带宽
结构


  • Y平面:包含所有像素的亮度信息。
  • U平面:包含每个2x2像素块的蓝色色度信息。
  • V平面:包含每个2x2像素块的红色色度信息。
1.png

转换

将YUV420P转换为RGB需要对U和V进行插值,然后应用转换公式。例如,每个Y值对应一个U和V值,通过插值得到全分辨率的U和V平面,再转换为RGB。
YUV 直出渲染

了解完 YUV420P 格式之后,我们来讲一讲应该怎样去渲染。在代码中,我们将 Y、U、V 三个分量分别存放在三个纹理中,将解码后的 AVFrame 中的 YUV 数据分别拷贝到对应的纹理。
  1. // 假设有一个AVFrame对象名为 m_pFrame
  2. // Y纹理
  3. glBindTexture(GL_TEXTURE_2D, m_texture[0]);
  4. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  5. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  6. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  7. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  8. glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_pFrame.linesize[0], m_frameSize.height(), 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, m_pFrame.data[0]);
  9. // U纹理
  10. glBindTexture(GL_TEXTURE_2D, m_texture[1]);
  11. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  12. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  13. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  14. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  15. glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_pFrame.linesize[1], m_frameSize.height() / 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, m_pFrame.data[1]);
  16. // V纹理
  17. glBindTexture(GL_TEXTURE_2D, m_texture[2]);
  18. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  19. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  20. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  21. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  22. glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_pFrame.linesize[2], m_frameSize.height() / 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, m_pFrame.data[2]);
复制代码
然后在片段着色器中将三个纹理合成。
  1. attribute vec3 vertexIn;    // xyz顶点坐标
  2. attribute vec2 textureIn;   // xy纹理坐标
  3. varying vec2 textureOut;    // 传递给片段着色器的纹理坐标
  4. void main(void)
  5. {
  6.     gl_Position = vec4(vertexIn, 1.0);  // 1.0表示vertexIn是一个顶点位置
  7.     textureOut = textureIn; // 纹理坐标直接传递给片段着色器
  8. }
复制代码
  1. #version 330
  2. vec3 yuv2rgb(vec3 yuv) {
  3.         float r = 1.164 * yuv.x + 1.596 * yuv.z;
  4.         float g = 1.164 * yuv.x - 0.392 * yuv.y - 0.813 * yuv.z;
  5.         float b = 1.164 * yuv.x + 2.017 * yuv.y;
  6.         return vec3(r, g, b);
  7. }
  8. varying vec2 textureOut;
  9. uniform sampler2D textureY;
  10. uniform sampler2D textureU;
  11. uniform sampler2D textureV;
  12. void main(void)
  13. {
  14.     vec3 yuv;
  15.     vec3 rgb;
  16.     yuv.x = texture(textureY, textureOut).r - 0.063;
  17.     yuv.y = texture(textureU, textureOut).r - 0.502;
  18.     yuv.z = texture(textureV, textureOut).r - 0.502;
  19.     rgb = yuv2rgb(yuv);
  20.     gl_FragColor = vec4(rgb, 1);
  21. }
复制代码
OpenGL 实现画质模糊

在我们将软件商业化时,往往需要一些付费点,例如:画质。我们可以让非会员的画质降低,而降低画质又可以有几种方法:

  • 降低输入的画质
  • FFmpeg 下采样+上采样
  • OpenGL MipMap 多级纹理
直接降低输入画质是最简单的,但可能存在某些特殊情况不能直接在源头降低。而采用 FFmpeg 的方式则会增加两次重采样,白白消耗 CPU。而采用 MipMap 的模式,则能在不增加太多 GPU 占用率的情况下,完全不占用 CPU。
接下来我将为大家介绍一种使用 MipMap 来让画质模糊的方式。首先,让我们了解了解什么叫 MipMap。
The height and width of each image, or level, in the mipmap is a factor of two smaller than the previous level.
2.jpeg

Mipmap(多级渐减图像)是OpenGL中用于优化纹理采样的一种技术。具体而言,Mipmap为每个纹理生成一系列不同分辨率的版本,每个级别(Level of Detail, LOD)的尺寸是前一级别的1/2。当渲染场景时,渲染器会根据纹理在屏幕上的实际大小自动选择合适的Mipmap级别,从而实现平滑过渡和抗锯齿效果。需要特别指出的是:Mipmap并不是简单的上采样(Upsampling)和下采样(Downsampling),而是通过预生成不同分辨率的纹理版本来实现高质量的模糊效果。这种预处理方式可以显著提升渲染效率,并确保纹理在不同缩放比例下都能保持视觉质量。
在实际应用中,可以通过以下方式配置Mipmap:

  • 调用glGenerateMipmap自动生成Mipmap
  • 设置合适的过滤器(Filter)来控制Mipmap的使用
  • 配置LOD偏移量(LOD bias)来调整Mipmap的使用级别
在 OpenGL 中,我们使用 glGenerateMipmap来生成 Mipmap。同时,我们需要将纹理环绕模式改成GL_LINEAR_MIPMAP_LINEAR。同时根据需要显示的不同模糊级别,为着色器中的 lodLevel设置不同的数值。
着色器代码如下:
  1. #version 330
  2. vec3 yuv2rgb(vec3 yuv) {
  3.         float r = 1.164 * yuv.x + 1.596 * yuv.z;
  4.         float g = 1.164 * yuv.x - 0.392 * yuv.y - 0.813 * yuv.z;
  5.         float b = 1.164 * yuv.x + 2.017 * yuv.y;
  6.         return vec3(r, g, b);
  7. }
  8. varying vec2 textureOut;
  9. uniform sampler2D textureY;
  10. uniform sampler2D textureU;
  11. uniform sampler2D textureV;
  12. uniform float lodLevel;
  13. void main(void)
  14. {
  15.     vec3 yuv;
  16.     vec3 rgb;
  17.     // 跟普通纹理采样函数不同的是,这里我们调用的是textureLod,也就是选择不同等级的纹理。
  18.     yuv.x = textureLod(textureY, textureOut, lodLevel).r - 0.063;
  19.     yuv.y = textureLod(textureU, textureOut, lodLevel).r - 0.502;
  20.     yuv.z = textureLod(textureV, textureOut, lodLevel).r - 0.502;
  21.     rgb = yuv2rgb(yuv);
  22.     gl_FragColor = vec4(rgb, 1);
  23. }
复制代码
textureLod的各个参数意义为:

  • _sampler_:指定绑定到纹理的采样器,即哪个纹理要被采样。
  • _P_:采样的纹理坐标。
  • _lod_:指定采样级别。
代码如下:
  1. // 假设有一个AVFrame对象名为 m_pFrame
  2. // Y纹理
  3. glBindTexture(GL_TEXTURE_2D, m_texture[0]);
  4. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
  5. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  6. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  7. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  8. glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_pFrame.linesize[0], m_frameSize.height(), 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, m_pFrame.data[0]);
  9. // 生成mipmap
  10. glGenerateMipmap(m_texture[0]);
  11. // U纹理
  12. glBindTexture(GL_TEXTURE_2D, m_texture[1]);
  13. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
  14. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  15. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  16. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  17. glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_pFrame.linesize[1], m_frameSize.height() / 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, m_pFrame.data[1]);
  18. // 生成mipmap
  19. glGenerateMipmap(m_texture[1]);
  20. // V纹理
  21. glBindTexture(GL_TEXTURE_2D, m_texture[2]);
  22. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
  23. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  24. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  25. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  26. // 生成mipmap
  27. glGenerateMipmap(m_texture[2]);
  28. glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_pFrame.linesize[2], m_frameSize.height() / 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, m_pFrame.data[2]);
  29. m_shaderProgram.setUniformValue("textureY", 0);
  30. m_shaderProgram.setUniformValue("textureU", 1);
  31. m_shaderProgram.setUniformValue("textureV", 2);
  32. // 设置0为原画
  33. m_shaderProgram.setUniformValue("lodLevel", 0);
  34. // 设置1为一级模糊
  35. //m_shaderProgram.setUniformValue("lodLevel", 1);
复制代码
其中,GL_TEXTURE_MIN_FILTER 表示我们要配置纹理的缩小过滤方式(当纹理被应用到比其实际尺寸小的区域时如何采样)。GL_LINEAR_MIPMAP_LINEAR 是一个三线性过滤(Trilinear Filtering)模式,具体含义是:

  • 在相邻mipmap层级之间执行线性插值(例如介于1级和2级mipmap之间)
  • 在每个mipmap层级内部也执行线性插值(即双线性过滤)
陷阱

在我实际运用到项目上时发现,窗口内容竟然变黑了?一查日志才知道,着色器竟然编译失败了,报错内容如下:
ERROR: 0:27: 'textureLod' : no matching overloaded function found
ERROR: 0:27: 'r' :  field selection requires structure, vector, or matrix on left hand side
ERROR: 0:28: 'textureLod' : no matching overloaded function found
ERROR: 0:28: 'r' :  field selection requires structure, vector, or matrix on left hand side
ERROR: 0:29: 'textureLod' : no matching overloaded function found
ERROR: 0:29: 'r' :  field selection requires structure, vector, or matrix on left hand side
但都 5202 年了,一般不会有显卡不支持 OpenGL 3.3 吧,那为什么会这样报错呢?当我们调用 OpenGL API 打印 OpenGL 版本和当前渲染器名称时:
  1. const GLubyte* version = glGetString(GL_VERSION);   // 获取OpenGL版本(如 "OpenGL ES 2.0")
  2. const GLubyte* renderer = glGetString(GL_RENDERER); // 获取渲染器名称(如GPU型号)
复制代码
结果显示为:
OpenGL ES 2.0
Microsoft Basic Render Driver
为什么渲染器是"Microsoft Basic Render Driver"?同时,OpenGL 的版本为什么是 OpenGL ES 2.0?
OpenGL ES 2.0 是移动设备和嵌入式系统的常见标准,与桌面版 OpenGL 存在差异(如精度、扩展支持)。OpenGL ES 并不是桌面端应用的,为什么会在桌面应用中出现 OpenGL ES 呢?通过一番搜索,最终我找到了答案:
如果系统没有硬件加速(如显卡驱动未安装),Windows 会使用  "Microsoft Basic Render Driver" ,此时需要降级到兼容模式(如使用 OpenGL ES 2.0 类似的特性),而 OpenGL ES 2.0 中不支持 textureLod 函数,所以自然就会报错。并且完全基于 CPU 计算(软件渲染),不支持 GPU 硬件加速,因此性能极低。有关Microsoft Basic Render Driver,可以看这:Microsoft 基本显示驱动程序 - Windows drivers。
其实这个问题根本原因是找到为什么会降级,但是苦于找不到具体原因,手上有没有一台可以复现的机器。所以,只能采用另外一条路,使用 OpenGL ES 2.0 的拓展:GL_EXT_shader_texture_lod
我们只需要小小的修改着色器代码,将 textureLod 改为 texture2DLodEXT,就能使用 MipMap 了:
  1. #extension GL_EXT_shader_texture_lod : require
  2. precision mediump float;
  3. vec3 yuv2rgb(vec3 yuv) {
  4.         float r = 1.164 * yuv.x + 1.596 * yuv.z;
  5.         float g = 1.164 * yuv.x - 0.392 * yuv.y - 0.813 * yuv.z;
  6.         float b = 1.164 * yuv.x + 2.017 * yuv.y;
  7.         return vec3(r, g, b);
  8. }
  9. varying vec2 textureOut;
  10. uniform sampler2D textureY;
  11. uniform sampler2D textureU;
  12. uniform sampler2D textureV;
  13. uniform float lodLevel;
  14. void main(void)
  15. {
  16.     vec3 yuv;
  17.     vec3 rgb;
  18.     yuv.x = texture2DLodEXT(textureY, textureOut, lodLevel).r - 0.063;
  19.     yuv.y = texture2DLodEXT(textureU, textureOut, lodLevel).r - 0.502;
  20.     yuv.z = texture2DLodEXT(textureV, textureOut, lodLevel).r - 0.502;
  21.     rgb = yuv2rgb(yuv);
  22.     gl_FragColor = vec4(rgb, 1);
  23. }
复制代码
首先,在开头添加一行
extension GL_EXT_shader_texture_lod : require

代表开启 GL_EXT_shader_texture_lod拓展,其次调用 texture2DLodEXT来加载不同的 mipmap。
完整代码
  1. #pragma once
  2. #include <vector>
  3. #include <QOpenGLBuffer>
  4. #include <QOpenGLWidget>
  5. #include <QOpenGLShaderProgram>
  6. #include <QOpenGLFunctions>
  7. #include "FrameObserver.h"
  8. struct AVFrame;
  9. class COpenGLRenderWidget : public QOpenGLWidget, protected QOpenGLFunctions, public IFrameObserver
  10. {
  11.     Q_OBJECT
  12. public:
  13.     explicit COpenGLRenderWidget(QWidget *parent = nullptr);
  14.     ~COpenGLRenderWidget() override;
  15.     void OnFrame(const AVFrame* frame, bool isHardwareFrame) override;
  16.     void OnInputCodecContext(const AVCodecContext* ctx) override;
  17.     HWND GetWindowHandle() const override;
  18. private:
  19.     void InitShaders();
  20.     void InitTextures();
  21.     void DeinitTextures();
  22. private:
  23.     void initializeGL() override;
  24.     void paintGL() override;
  25.     void resizeGL(int w, int h) override;
  26.    
  27. private:
  28.     AVFrame* m_pFrame = nullptr;
  29.     QOpenGLShaderProgram m_shaderProgram;
  30.     QOpenGLBuffer m_vbo;
  31.     std::vector<GLuint> m_textures;
  32.     bool m_bIsNeedUpdate = false;
  33. };
复制代码
  1. #include "OpenGLRenderWidget.h"extern "C"{#include "libavformat/avformat.h"}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    0.0f, 1.0f,    1.0f, 1.0f,    0.0f, 0.0f,    1.0f, 0.0f};constexpr auto VERTEX_SHADER = R"(attribute vec3 vertexIn;    // xyz顶点坐标
  2. attribute vec2 textureIn;   // xy纹理坐标
  3. varying vec2 textureOut;    // 传递给片段着色器的纹理坐标
  4. void main(void)
  5. {
  6.     gl_Position = vec4(vertexIn, 1.0);  // 1.0表示vertexIn是一个顶点位置
  7.     textureOut = textureIn; // 纹理坐标直接传递给片段着色器
  8. })";constexpr auto FRAGMENT_SHADER = R"(#version 330vec3 yuv2rgb(vec3 yuv) {        float r = 1.164 * yuv.x + 1.596 * yuv.z;        float g = 1.164 * yuv.x - 0.392 * yuv.y - 0.813 * yuv.z;        float b = 1.164 * yuv.x + 2.017 * yuv.y;        return vec3(r, g, b);}varying vec2 textureOut;uniform sampler2D textureY; uniform sampler2D textureU; uniform sampler2D textureV; uniform float lodLevel;void main(void) {     vec3 yuv;     vec3 rgb;     // 跟普通纹理采样函数不同的是,这里我们调用的是textureLod,也就是选择不同等级的纹理。    //yuv.x = textureLod(textureY, textureOut, lodLevel).r - 0.063;     //yuv.y = textureLod(textureU, textureOut, lodLevel).r - 0.502;     //yuv.z = textureLod(textureV, textureOut, lodLevel).r - 0.502;     yuv.x = texture(textureY, textureOut).r - 0.063;     yuv.y = texture(textureU, textureOut).r - 0.502;     yuv.z = texture(textureV, textureOut).r - 0.502;     rgb = yuv2rgb(yuv);     gl_FragColor = vec4(rgb, 1); })";COpenGLRenderWidget::COpenGLRenderWidget(QWidget *parent)    : QOpenGLWidget(parent){}COpenGLRenderWidget::~COpenGLRenderWidget(){}void COpenGLRenderWidget::OnFrame(const AVFrame * frame, bool isHardwareFrame){    if (frame->width != m_pFrame->width || frame->height != m_pFrame->height)    {        m_bIsNeedUpdate = true;    }    av_frame_unref(m_pFrame);    av_frame_ref(m_pFrame, frame);    update();}void COpenGLRenderWidget::OnInputCodecContext(const AVCodecContext* ctx){}HWND COpenGLRenderWidget::GetWindowHandle() const{    return HWND();}void COpenGLRenderWidget::initializeGL(){    if (!m_pFrame)    {        m_pFrame = av_frame_alloc();    }    initializeOpenGLFunctions();    glDisable(GL_DEPTH_TEST);    m_vbo.create();    m_vbo.bind();    m_vbo.allocate(coordinate, sizeof(coordinate));    InitShaders();    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);    glClear(GL_COLOR_BUFFER_BIT);}void COpenGLRenderWidget::paintGL(){    m_shaderProgram.bind();    if (m_bIsNeedUpdate)    {        DeinitTextures();        InitTextures();        m_bIsNeedUpdate = false;    }    if (m_textures.size() != 3)    {        return;    }    glActiveTexture(GL_TEXTURE0);    glBindTexture(GL_TEXTURE_2D, m_textures[0]);    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_LUMINANCE, m_pFrame->linesize[0], m_pFrame->height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, m_pFrame->data[0]);    // U纹理    glActiveTexture(GL_TEXTURE1);    glBindTexture(GL_TEXTURE_2D, m_textures[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_LUMINANCE, m_pFrame->linesize[1], m_pFrame->height / 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, m_pFrame->data[1]);    // V纹理    glActiveTexture(GL_TEXTURE2);    glBindTexture(GL_TEXTURE_2D, m_textures[2]);    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_LUMINANCE, m_pFrame->linesize[2], m_pFrame->height / 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, m_pFrame->data[2]);    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);    m_shaderProgram.release();}void COpenGLRenderWidget::resizeGL(int w, int h){    glViewport(0, 0, w, h);    update();}void COpenGLRenderWidget::InitShaders(){    QOpenGLShader vertexShader(QOpenGLShader::Vertex);    if (!vertexShader.compileSourceCode(VERTEX_SHADER))    {        qDebug() height / 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, nullptr);    glBindTexture(GL_TEXTURE_2D, m_textures[2]);    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);    glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_pFrame->linesize[2], m_pFrame->height / 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, nullptr);}void COpenGLRenderWidget::DeinitTextures(){    if (m_textures.size() != 3)    {        return;    }    glDeleteTextures(3, m_textures.data());    m_textures.clear();}
复制代码
更高效率

虽然我们将画面渲染通过 OpenGL 来实现 GPU 绘制,但是,将纹理从内存拷贝的 GPU 的显存,同样是需要消耗 CPU 的。同样,FFmpeg 的解码也是需要消耗 CPU 的。那有没有一种方法能够将所有的工作都交给 GPU 呢?答案当然是有的,敬请期待接下来的内容:

  • FFmpeg 硬解码
  • DXVA2+D3D9 实现零拷贝渲染

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册