Эх сурвалжийг харах

V0.7.9
1、封装完成了着色器类

Apple 2 сар өмнө
parent
commit
4eced30e11

+ 15 - 8
demo/VideoPlayerGL/CMakeLists.txt

@@ -11,6 +11,7 @@ file(GLOB LOCAL_SRC
     ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/*.ui
     ${CMAKE_CURRENT_SOURCE_DIR}/GLWidget/*.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/GLShader/*.cpp
 
     ${CMAKE_SOURCE_DIR}/External/module/Logs/*.cpp
     ${CMAKE_SOURCE_DIR}/External/module/ThreadPool/*.cpp
@@ -42,6 +43,7 @@ target_include_directories(${this_exe} PRIVATE
     ${CMAKE_SOURCE_DIR}/External/module/RingQueue
 
     ${CMAKE_CURRENT_SOURCE_DIR}/GLWidget
+    ${CMAKE_CURRENT_SOURCE_DIR}/GLShader
     
     # ${CURL_INCLUDE_DIR}
     # ${FFMPEG_INCLUDE_DIR}
@@ -67,14 +69,19 @@ elseif(QT_VERSION_MAJOR EQUAL 6)
     )
 endif(QT_VERSION_MAJOR EQUAL 5)
 
-
-target_link_libraries(${this_exe} PRIVATE 
-    # ${CURL_LIBRARY}
-    # ${FFMPEG_LIBRARY}
-    ${spdlog_LIBRARY}
-    opengl32
-)
-
+if(CMAKE_SYSTEM_NAME MATCHES "Windows")
+    target_link_libraries(${this_exe} PRIVATE 
+        # ${CURL_LIBRARY}
+        # ${FFMPEG_LIBRARY}
+        ${spdlog_LIBRARY}
+        opengl32
+    )
+elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
+    target_link_libraries(${this_exe} PRIVATE 
+        ${spdlog_LIBRARY}
+        GL
+    )
+endif()
 
 # if(CMAKE_CXX_COMPILER_VERSION LESS 9.0)
 #     target_link_libraries(${this_exe} PRIVATE

+ 271 - 0
demo/VideoPlayerGL/GLShader/ShaderBase.cpp

@@ -0,0 +1,271 @@
+#include "ShaderBase.h"
+
+#include <QString>
+#include <QImage>
+#include <QFile>
+
+#include <QOpenGLWidget>
+#include <QOpenGLFunctions_3_3_Core>
+#include <QTextStream>
+
+#include "spdlog/spdlog.h"
+
+
+ShaderBase::ShaderBase()
+{
+}
+
+ShaderBase::~ShaderBase()
+{
+    clearTexture();
+    
+    if(m_shaderProgramObj != nullptr) {
+        delete m_shaderProgramObj;
+        m_shaderProgramObj = nullptr;
+    }
+}
+
+
+/* 初始化Shader,第一个是定点着色器,第二个是片段着色器 */
+bool ShaderBase::loadShaderCode(const QString &vertexShaderFile, const QString &fragmentShaderFile)
+{
+    if(vertexShaderFile.isEmpty() || fragmentShaderFile.isEmpty()) 
+    {
+        SPDLOG_ERROR("顶点着色器或片段着色器文件为空");
+        return false;
+    }
+    /* 打开文件 */
+    QFile vertexFile(vertexShaderFile);
+    if (!vertexFile.open(QIODevice::ReadOnly)) 
+    {
+        SPDLOG_ERROR("Failed to open vertex shader file: {}", vertexShaderFile.toStdString());
+        return false;
+    }
+    m_vertexShaderCode = vertexFile.readAll();
+    vertexFile.close();
+
+    if (m_vertexShaderCode.isEmpty()) 
+    {
+        SPDLOG_ERROR("Vertex shader file is empty: {}", vertexShaderFile.toStdString());
+        return false;
+    }
+    /* 打开文件 */
+    QFile fragmentFile(fragmentShaderFile);
+    if (!fragmentFile.open(QIODevice::ReadOnly)) 
+    {
+        SPDLOG_ERROR("Failed to open fragment shader file: {}", fragmentShaderFile.toStdString());
+        return false;
+    }
+    m_fragmentShaderCode = fragmentFile.readAll();
+    fragmentFile.close();
+    if (m_fragmentShaderCode.isEmpty()) 
+    {
+        SPDLOG_ERROR("Fragment shader file is empty: {}", fragmentShaderFile.toStdString());
+        return false;
+    }
+
+    return true;
+}
+
+
+/* 编译着色器程序 */
+bool ShaderBase::compileShader()
+{
+    if(m_vertexShaderCode.isEmpty() || m_fragmentShaderCode.isEmpty()) {
+        SPDLOG_ERROR("顶点着色器或片段着色器文件为空");
+        return false;
+    }
+    /* 编译顶点着色器 */
+    QOpenGLShader* vertexShader = new QOpenGLShader(QOpenGLShader::Vertex);
+    if(!vertexShader->compileSourceCode(m_vertexShaderCode))
+    {
+        SPDLOG_ERROR("编译顶点着色器错误: {}", vertexShader->log().toStdString());
+        return false;
+    }
+    /* 编译片段着色器 */
+    QOpenGLShader* fragmentShader = new QOpenGLShader(QOpenGLShader::Fragment);
+    if(!fragmentShader->compileSourceCode(m_fragmentShaderCode))
+    {
+        SPDLOG_ERROR("编译片段着色器错误: {}", fragmentShader->log().toStdString());
+        return false;
+    }
+
+    /* 创建着色器程序对象 */
+    m_shaderProgramObj = new QOpenGLShaderProgram();
+    /* 添加顶点着色器和片段着色器 */
+    m_shaderProgramObj->addShader(vertexShader);
+    m_shaderProgramObj->addShader(fragmentShader);
+    /* 链接着色器程序 */
+    if (!m_shaderProgramObj->link()) {
+        SPDLOG_ERROR("Failed to link shader program: {}", m_shaderProgramObj->log().toStdString());
+        return false;
+    }
+    /* 检查链接错误 */
+    if (m_shaderProgramObj->log().contains("ERROR")) {
+        SPDLOG_ERROR("Shader program link error: {}", m_shaderProgramObj->log().toStdString());
+        return false;
+    }
+    /* 删除着色器对象 */
+    delete vertexShader;
+    vertexShader = nullptr;
+    delete fragmentShader;
+    fragmentShader = nullptr;
+
+    return true;
+}
+
+/**
+ * @brief 初始化纹理,返回值是生成的纹理ID
+ * 
+ * @param imageFile 纹理图片路径
+ * @param uniformName 纹理对应的uniform变量名称
+ * @return true 
+ * @return false 
+ */
+bool ShaderBase::createTexture(const QString &imageFile, const QString uniformName)
+{
+    bool isExist = false;
+    for(auto& it : m_listTexture) 
+    {
+        if(it.first == uniformName) 
+        {
+            QOpenGLTexture* texture = it.second;
+            if (texture != nullptr) {
+                texture->destroy();
+                delete texture;
+                texture = nullptr;
+            }
+            /* 纹理已存在,删除纹理 */
+            m_listTexture.removeOne(it);
+            isExist = true;
+            break;
+        }
+    }
+    if(!isExist)
+    {
+        /* 纹理图片不存在,检查是否已经超过最大的纹理数目 */
+        int maxTextureUnits = 0;
+        // maxTextureUnits 就是当前环境支持的最大纹理单元数
+        glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
+
+        if (m_listTexture.size() >= maxTextureUnits) {
+            SPDLOG_ERROR("纹理图片数量超过最大限制");
+            return false;
+        }
+    }
+    
+    /* 加载图片 */
+    QImage image;
+    image.load(imageFile);
+    if (image.isNull()) {
+        SPDLOG_ERROR("Failed to load image: {}", imageFile.toStdString());
+        return false;
+    }
+    /* 将图片转换为RGBA格式 */
+    image = image.convertToFormat(QImage::Format_RGBA8888);
+    /* 这里要垂直翻转 */
+    // image = image.mirrored(false, true);
+
+    QOpenGLTexture* texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
+    texture->setSize(image.width(), image.height(), 1); // 设置纹理大小
+    texture->setFormat(QOpenGLTexture::RGBA8_UNorm); // 设置纹理格式
+    texture->allocateStorage(); // 分配纹理存储空间
+    texture->setData(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, image.bits()); // 设置纹理数据
+    /* 设置纹理参数 */
+    texture->setWrapMode(QOpenGLTexture::Repeat); // S轴环绕方式
+    texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear); // 纹理缩小过滤方式
+    texture->setMagnificationFilter(QOpenGLTexture::Linear); // 纹理放大过滤方式
+    
+    
+    texture->generateMipMaps(); // 生成多级渐远纹理
+
+    printGLerror("ShaderBase::createTexture");
+    /* 将纹理存储到数组中 */
+    m_listTexture.push_back(QPair<QString, QOpenGLTexture*>(uniformName, texture));
+
+    return true;
+}
+
+/* 清空纹理 */
+void ShaderBase::clearTexture()
+{
+    for(auto& it : m_listTexture) 
+    {
+        if(it.second != nullptr) 
+        {
+            it.second->destroy();
+            delete it.second;
+            it.second = nullptr;
+        }
+    }
+    m_listTexture.clear();
+}
+
+/* 使用着色器 */
+void ShaderBase::useShader()
+{
+    if (m_shaderProgramObj == nullptr) {
+        SPDLOG_ERROR("Shader program object is null");
+        return;
+    }
+    /* 使用着色器程序 */
+    m_shaderProgramObj->bind();
+    /* 使用纹理 */
+    if(m_listTexture.size() > 0) 
+    {
+        int currentTextureUnit = 0;
+        for (auto it = m_listTexture.begin(); it != m_listTexture.end(); ++it) 
+        {
+            it->second->bind(currentTextureUnit);
+            /* 绑定纹理 */
+            int location = m_shaderProgramObj->uniformLocation(it->first);
+            if (location < 0) {
+                SPDLOG_ERROR("Failed to get uniform location for texture: {}", it->first.toStdString());
+                continue;
+            }
+            /* 设置纹理单元 */
+            m_shaderProgramObj->setUniformValue(location, currentTextureUnit); 
+
+            currentTextureUnit++;
+        }
+    }
+}
+
+/* 打印OpenGL错误 */
+void ShaderBase::printGLerror(const std::string functionName)
+{
+    GLenum error = glGetError();
+    if (error == GL_NO_ERROR) {
+        return;
+    }
+
+    /* 打印出字符串 */
+    std::string errorString;
+    switch (error) 
+    {
+        case GL_INVALID_ENUM:
+            errorString = "GL_INVALID_ENUM";
+            break;
+        case GL_INVALID_VALUE:
+            errorString = "GL_INVALID_VALUE";
+            break;
+        case GL_INVALID_OPERATION:
+            errorString = "GL_INVALID_OPERATION";
+            break;
+        case GL_STACK_OVERFLOW:
+            SPDLOG_ERROR("GL_STACK_OVERFLOW");
+            break;
+        case GL_STACK_UNDERFLOW:
+            SPDLOG_ERROR("GL_STACK_UNDERFLOW");
+            break;
+        case GL_OUT_OF_MEMORY:
+            SPDLOG_ERROR("GL_OUT_OF_MEMORY");
+            break;
+        default:
+            SPDLOG_ERROR("Unknown OpenGL error: 0x{:X}", error);
+            break;
+    }
+    SPDLOG_ERROR("OpenGL error in {}: 0x{:X}, {}", functionName, error, errorString);
+    
+}
+

+ 54 - 0
demo/VideoPlayerGL/GLShader/ShaderBase.h

@@ -0,0 +1,54 @@
+#ifndef SHADERBASE_H
+#define SHADERBASE_H
+
+#include "GL/gl.h"
+#include <QList>
+#include <QOpenGLTexture>
+#include <QOpenGLShader>
+
+class QString;
+class QImage;
+
+
+
+class ShaderBase
+{
+
+public:
+    ShaderBase();
+    virtual ~ShaderBase();
+
+    /* 初始化形状 */
+    virtual GLuint initShape() = 0;
+    /* 绘制图形 */
+    virtual void drawShape() = 0;
+
+    /* 初始化Shader,第一个是定点着色器,第二个是片段着色器 */
+    virtual bool loadShaderCode(const QString &vertexShaderFile, const QString &fragmentShaderFile);
+    /* 编译着色器程序 */
+    virtual bool compileShader();
+    /* 初始化纹理,返回值是生成的纹理ID */
+    virtual bool createTexture(const QString &imageFile, const QString uniformName);
+    /* 清空纹理 */
+    virtual void clearTexture();
+
+    /* 使用着色器 */
+    virtual void useShader();
+    
+
+protected:
+    /* 打印OpenGL错误 */
+    void printGLerror(const std::string functionName);
+
+protected:
+    // QOpenGLShader* m_vertexShaderObj = nullptr;         /* 顶点着色器对象 */
+    // QOpenGLShader* m_fragmentShaderObj = nullptr;       /* 片段着色器对象 */
+    QByteArray m_vertexShaderCode;                         /* 顶点着色器文件 */
+    QByteArray m_fragmentShaderCode;                       /* 片段着色器文件 */
+    QOpenGLShaderProgram* m_shaderProgramObj = nullptr; /* 着色器程序对象 */
+    // QMap<QString, QOpenGLTexture*> m_mapTexture;        /* uniform和纹理的映射 */
+    QList<QPair<QString, QOpenGLTexture*>> m_listTexture; /* uniform和纹理的映射 */
+};
+
+
+#endif // SHADERBASE_H

+ 109 - 0
demo/VideoPlayerGL/GLShader/ShaderRect.cpp

@@ -0,0 +1,109 @@
+#include "ShaderRect.h"
+
+#include <QOpenGLWidget>
+#include <QOpenGLFunctions_3_3_Core>
+#include <GL/gl.h>
+
+#include "spdlog/spdlog.h"
+
+ShaderRect::ShaderRect()
+{
+
+}
+
+ShaderRect::~ShaderRect()
+{
+    QOpenGLFunctions_3_3_Core *gl = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_3_Core>();
+    if (m_VAO != 0) {
+        if (gl) {
+            gl->glDeleteVertexArrays(1, &m_VAO);
+            gl->glDeleteBuffers(1, &m_VBO);
+            gl->glDeleteBuffers(1, &m_EBO);
+        }
+    }
+
+}
+
+/* 初始化形状 */
+GLuint ShaderRect::initShape()
+{
+    const int vertexCount = 4;    // 顶点数量
+    const int vertexSize = 8;     // 顶点大小
+    /* 矩形位置,铺满屏幕 */
+    float vertices [vertexCount * vertexSize] {
+        /* ----- 位置 -----|-------- 颜色 ----------|----- 纹理坐标 ------ */
+        -1.0f, -1.0f, 0.0f,     1.0f, 1.0f, 0.0f,   0.0f, 0.0f,     // 左下
+        1.0f, -1.0f, 0.0f,      0.0f, 0.0f, 1.0f,   1.0f, 0.0f,     // 右下
+        1.0f,  1.0f, 0.0f,      1.0f, 0.0f, 0.0f,   1.0f, 1.0f,     // 右上
+        -1.0f, 1.0f, 0.0f,      0.0f, 1.0f, 0.0f,   0.0f, 1.0f      // 右上
+    };
+    const int indexCount = 6; // 索引数量
+    /* 顶点顺序 */
+    uint32_t indices [indexCount] {
+        0, 1, 2, // 第一个三角形
+        2, 3, 0  // 第二个三角形
+    };
+
+    QOpenGLFunctions_3_3_Core *gl = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_3_Core>();
+    if (!gl) {
+        SPDLOG_ERROR("Failed to get OpenGL functions");
+        return 0;
+    }
+    /* -------------------------------------------------------------------------------------
+     * 顶点数组对象
+     * ------------------------------------------------------------------------------------ */
+    gl->glGenVertexArrays(1, &m_VAO); // 生成一个VAO,返回的ID存储在VAO1中
+    gl->glBindVertexArray(m_VAO); // 绑定VAO
+
+    /* -------------------------------------------------------------------------------------
+     * 创建顶点数组缓冲区
+     * ------------------------------------------------------------------------------------ */
+    gl->glGenBuffers(1, &m_VBO);
+    gl->glBindBuffer(GL_ARRAY_BUFFER, m_VBO); // 绑定VBO
+    gl->glBufferData(GL_ARRAY_BUFFER, vertexCount * vertexSize * sizeof(float), vertices, GL_STATIC_DRAW);
+
+    /* -------------------------------------------------------------------------------------
+     * 创建EBO
+     * ------------------------------------------------------------------------------------ */
+    gl->glGenBuffers(1, &m_EBO);
+    gl->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBO);
+    gl->glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount * sizeof(uint32_t), indices, GL_STATIC_DRAW); // 绑定EBO
+
+
+    /* -------------------------------------------------------------------------------------
+     * 链接顶点属性
+     * ------------------------------------------------------------------------------------ */
+    // 设置顶点属性指针,告诉OpenGL如何解析顶点数据
+    gl->glVertexAttribPointer(
+                    0,                  /* 顶点属性位置,0表示第一个属性 */
+                    3,                  /* 每个顶点属性的大小,这里是3,表示x、y、z坐标 */
+                    GL_FLOAT, GL_FALSE, /* 数据类型和是否标准化 */
+                    vertexSize * sizeof(float), /* 步长,表示每个顶点属性之间的间隔 */
+                    (void*)0            /* 偏移量,表示该属性(顶点坐标)从第0个字节开始 */
+                ); 
+    /* 启用顶点属性 */
+    gl->glEnableVertexAttribArray(0);
+ 
+    // 设置颜色位置,最后一个参数表示从第3个字节开始,倒数第二个参数是两个数据之间起始位置间隔8个字节
+    gl->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, vertexSize * sizeof(float), (void*)(3*sizeof(float))); 
+    gl->glEnableVertexAttribArray(1);
+ 
+    /* 设置纹理位置 */
+    gl->glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, vertexSize * sizeof(float), (void*)(6*sizeof(float))); // 纹理坐标位置
+    gl->glEnableVertexAttribArray(2); // 启用纹理坐标属性
+
+    return m_VAO;
+}
+
+/* 绘制图形 */
+void ShaderRect::drawShape()
+{
+    QOpenGLFunctions_3_3_Core *gl = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_3_Core>();
+    /* 需要在使用时手动绑定VAO,绘制图形 */
+    gl->glBindVertexArray(m_VAO); // 绑定VAO
+
+    gl->glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); // 绘制四边形
+
+    gl->glBindVertexArray(0); // 解绑VAO
+}
+

+ 24 - 0
demo/VideoPlayerGL/GLShader/ShaderRect.h

@@ -0,0 +1,24 @@
+#ifndef SHADERRECT_H
+#define SHADERRECT_H
+
+#include "ShaderBase.h"
+
+class ShaderRect : public ShaderBase
+{
+public:
+    ShaderRect();
+    ~ShaderRect();
+
+    /* 初始化形状 */
+    GLuint initShape() override;
+    /* 绘制图形 */
+    void drawShape() override;
+
+private:
+    GLuint m_VAO = 0;              // 顶点数组对象的ID
+    GLuint m_VBO = 0;              // 顶点缓冲对象的ID
+    GLuint m_EBO = 0;              // 索引缓冲对象的ID
+};
+
+
+#endif // SHADERRECT_H

+ 27 - 179
demo/VideoPlayerGL/GLWidget/PlayerGLWidget.cpp

@@ -1,6 +1,5 @@
 #include "PlayerGLWidget.h"
 #include "spdlog/spdlog.h"
-#include <gl/gl.h>
 #include <qopenglext.h>
 #include <QVariant>
 #include <QDateTime>
@@ -25,166 +24,39 @@ void PlayerGLWidget::initializeGL()
     /* 初始化OpenGL函数,OpenGL的函数是在运行时才确定函数指针的 */
     initializeOpenGLFunctions();
 
-    /* -------------------------------------------------------------------------------------
-     * 顶点数组对象
-     * ------------------------------------------------------------------------------------ */
-    /* 生成一个VAO,返回的ID存储在VAO1中 */
-    glGenVertexArrays(1, &m_VAO1);
-    glBindVertexArray(m_VAO1);
-
-
-    /* 创建一个三角形坐标数组,这里的z坐标都是0,表示平面2维 */
-    float vertices[] = {
-        0.5f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,   // 右下
-        -0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,   // 左下
-        0.0f,  0.5f, 0.0f,  0.0f, 0.0f, 1.0f    // 顶部
-    };
-
-    /* -------------------------------------------------------------------------------------
-     * 创建顶点数组缓冲区
-     * (GLuint就是unsigned int) 
-     * ------------------------------------------------------------------------------------ */
-    /* 生成一个VBO,返回的ID存储在VBO1中 */
-    glGenBuffers(1, &m_VBO1);
-    /* OpenGL有很多缓冲对象类型,顶点缓冲对象的缓冲类型是GL_ARRAY_BUFFER
-     * 绑定VBO1到GL_ARRAY_BUFFER上,表示VBO1是一个顶点缓冲对象 */
-    glBindBuffer(GL_ARRAY_BUFFER, m_VBO1);
-    /* 将顶点数据复制到缓冲区的内存中 */
-    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
-
-    /* ---------------------------------------------------------------------------------------
-     * 创建EBO
-     * ------------------------------------------------------------------------------------ */
-    unsigned int indices[] = {
-        0, 1, 3, // 第一个三角形
-        1, 2, 3  // 第二个三角形
-    };
-    /* 创建一个EBO,返回的ID存储在EBO1中 */
-    glGenBuffers(1, &m_EBO1);
-    /* 绑定缓冲区 */
-    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBO1);
-    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
+    m_shaderObj = new ShaderRect();
 
-    
-
-
-    /* -------------------------------------------------------------------------------------
-     * 链接顶点属性
-     * ------------------------------------------------------------------------------------ */
-    /* 设置顶点属性指针,告诉OpenGL如何解析顶点数据
-     * 1、glVertexAttribPointer函数的第一个参数是顶点属性的位置值
-     * 2、第二个参数是每个顶点属性的大小,这里是3,因为我们有3个坐标分量。
-     * 3、第三个参数是数据类型,这里是GL_FLOAT,表示数据类型是float。
-     * 4、第四个参数是是否归一化,这里是GL_FALSE,表示不归一化。
-     * 5、第五个参数是步长,这里是0,表示紧凑存储。
-     * 6、第六个参数是偏移量,这里是0,表示从缓冲区的开头开始读取数据。
-     * 7、最后一个参数是启用顶点属性指针,告诉OpenGL我们要使用这个顶点属性。
-     */
-    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
-     /* 启用顶点属性指针 */
-    glEnableVertexAttribArray(0);
-
-     /* 设置颜色位置,最后一个参数表示从第3个字节开始,倒数第二个参数是两个数据之间起始位置间隔6个字节 */
-    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), (void*)(3*sizeof(float)));
-    glEnableVertexAttribArray(1);
-
-    /* -------------------------------------------------------------------------------------
-     * 顶点着色器
-     * ------------------------------------------------------------------------------------ */
-    /* 顶点着色器的源代码
-     * GLSL看起来很像C语言。每个着色器都起始于一个版本声明。OpenGL 3.3以及和更高版本中,GLSL版本号和OpenGL的版本是匹配的(比如说GLSL 420版本对应于OpenGL 4.2)。
-     * 同样明确表示我们会使用核心模式。
-     * 1、#version 330 core 设置版本号和模式
-     * 2、使用in关键字,在顶点着色器中声明所有的输入顶点属性(Input Vertex Attribute)。现在我们只关心位置(Position)数据,所以我们只需要一个顶点属性。
-     *    GLSL有一个向量数据类型,它包含1到4个float分量,包含的数量可以从它的后缀数字看出来。由于每个顶点都有一个3D坐标,我们就创建一个vec3输入变量aPos。
-     *    我们同样也通过layout (location = 0)设定了输入变量的位置值(Location)
-     * 3、gl_Position是一个内建变量,它是一个vec4类型的变量,表示顶点在裁剪空间中的位置。我们将aPos赋值给gl_Position,OpenGL会自动将vec3转换为vec4。
-     */
-    const char* vertexShaderSource = R"(
-        #version 330 core
-        layout(location = 0) in vec3 aPos;
-        layout(location = 1) in vec3 aColor;
-        uniform float offset;
-        out vec3 vertexColor;       //向片段着色器传递颜色数据
-        void main()
-        {
-            gl_Position = vec4(aPos.x + offset, aPos.y, aPos.z, 1.0);
-            vertexColor = aColor;
-        }
-    )";
-
-    /* -------------------------------------------------------------------------------------
-     * 编译着色器
-     * ------------------------------------------------------------------------------------ */
-     /* 创建一个着色器对象,参数是着色器类型
-      * GL_VERTEX_SHADER代表是顶点着色器,GL_FRAGMENT_SHADER代表是片段着色器
-      * glCreateShader函数返回一个着色器对象的ID,OpenGL会为我们分配一个唯一的ID
-      */
-     GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
-    /* 将着色器源代码附加到着色器对象上 */
-    glShaderSource(vertexShader, 1, &vertexShaderSource,nullptr);
-    /* 编译着色器 */
-    glCompileShader(vertexShader);
-    /* 检查编译错误 */
-    GLint success;
-    GLchar infoLog[512];
-    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
-    if (!success)
+    m_VAO1 = m_shaderObj->initShape(); // 初始化形状
+    if(m_VAO1 == 0) 
     {
-        glGetShaderInfoLog(vertexShader, sizeof(infoLog), nullptr, infoLog);
-        SPDLOG_ERROR("着色器编译错误: {}", infoLog);
+        SPDLOG_ERROR("初始化形状失败");
+        return;
     }
-
-    /* -------------------------------------------------------------------------------------
-     * 片段着色器
-     * ------------------------------------------------------------------------------------ */
-    /* 片段着色器的源代码 */
-    const char* fragmentShaderSource = R"(
-        #version 330 core
-        out vec4 FragColor;
-        in vec3 vertexColor;
-        void main()
-        {
-            FragColor = vec4(vertexColor, 1.0);
-        }
-    )";
-    /* 创建一个片段着色器对象 */
-    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
-    /* 将片段着色器源代码附加到片段着色器对象上 */
-    glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
-    /* 编译片段着色器 */
-    glCompileShader(fragmentShader);
-    /* 检查编译错误 */
-    success = 0;
-    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
-    if (!success)
+    /* 初始化着色器 */
+    QString vertexShaderFile = ":/shader/vertexShader.glsl";
+    QString fragmentShaderFile = ":/shader/fragmentShader.glsl";
+    if (!m_shaderObj->loadShaderCode(vertexShaderFile, fragmentShaderFile)) 
     {
-        glGetShaderInfoLog(fragmentShader, sizeof(infoLog), nullptr, infoLog);
-        SPDLOG_ERROR("片段着色器编译错误: {}", infoLog);
+        SPDLOG_ERROR("加载着色器代码失败");
+        return;
     }
+    /* 编译着色器 */
+    m_shaderObj->compileShader();
+    /* 创建纹理 */
+    QString imageFile1 = QString(":/Image/image/1.jpg");
+    QString imageFile2 = QString(":/Image/image/awesomeface.png");
 
-    /* -------------------------------------------------------------------------------------
-     * 着色器程序对象
-     * ------------------------------------------------------------------------------------ */
-    /* 创建一个着色器程序对象 */
-    m_shaderProgram = glCreateProgram();
-    /* 将顶点着色器和片段着色器附加到着色器程序对象上 */
-    glAttachShader(m_shaderProgram, vertexShader);
-    glAttachShader(m_shaderProgram, fragmentShader);
-    /* 链接着色器程序对象 */
-    glLinkProgram(m_shaderProgram);
-    /* 检查链接错误 */
-    glGetProgramiv(m_shaderProgram, GL_LINK_STATUS, &success);
-    if(!success)
+    if (!m_shaderObj->createTexture(imageFile1, "ourTexture1")) 
     {
-        glGetProgramInfoLog(m_shaderProgram, 512, nullptr, infoLog);
-        SPDLOG_ERROR("着色器程序链接错误: {}", infoLog);
+        SPDLOG_ERROR("创建纹理失败");
+        return;
     }
-    /* 删除着色器对象,因为它们已经链接到着色器程序对象上了 */
-    glDeleteShader(vertexShader);
-    glDeleteShader(fragmentShader);
-    
-    
+    if (!m_shaderObj->createTexture(imageFile2, "ourTexture2")) 
+    {
+        SPDLOG_ERROR("创建纹理失败");
+        return;
+    }
+
 }
 
 void PlayerGLWidget::resizeGL(int w, int h)
@@ -199,32 +71,8 @@ void PlayerGLWidget::paintGL()
     glClearColor(0.1f, 0.3f, 0.3f, 1.0f);
     glClear(GL_COLOR_BUFFER_BIT);
 
-    /* 激活色器程序对象 */
-    glUseProgram(m_shaderProgram);
-
-    /* 更新uniform数据 */
-    /* uniform变量是一个全局变量,它的值在着色器程序中是唯一的,不能被顶点着色器和片段着色器同时使用。
-     * uniform变量的值在每次绘制时都可以更新。 */
-    static float offset = 0;
-    offset += 0.01f;
-    if (offset > 1.0f) offset = 0.0f;
-    
-    int offsetUniform = glGetUniformLocation(m_shaderProgram, "offset");
-    glUniform1f(offsetUniform, offset);
-
-    /* 绑定VAO1到 GL_VERTEX_ARRAY 上,表示VAO1是一个顶点数组对象 */
-    glBindVertexArray(m_VAO1);
 
-    /* 绘制三角形 */
-    /* glDrawArrays函数的第一个参数是绘制模式,这里是GL_TRIANGLES,表示绘制三角形。
-     * 第二个参数是起始索引,这里是0,表示从第一个顶点开始绘制。
-     * 第三个参数是顶点数量,这里是3,表示绘制3个顶点。
-     */
-    glDrawArrays(GL_TRIANGLES, 0, 3);
-
-    
-    // glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
-    /* 解绑VAO1 */
-    glBindVertexArray(0);
+    m_shaderObj->useShader(); // 使用着色器
+    m_shaderObj->drawShape(); // 绘制图形
     
 }

+ 5 - 5
demo/VideoPlayerGL/GLWidget/PlayerGLWidget.h

@@ -4,8 +4,9 @@
 
 #include <QOpenGLWidget>
 #include <QOpenGLFunctions_3_3_Core>
-#include <gl/gl.h>
-#include <qopenglext.h>
+#include <GL/gl.h>
+
+#include "ShaderRect.h"
 
 
 /**
@@ -33,9 +34,8 @@ protected:
 private:
 
     GLuint m_VAO1 = 0; // 顶点数组对象的ID
-    GLuint m_VBO1 = 0; // 顶点缓冲对象的ID
-    GLuint m_EBO1 = 0; // 索引缓冲对象的ID
-    GLuint m_shaderProgram = 0; // 着色器程序对象的ID
+    
+    ShaderBase* m_shaderObj = nullptr; // 着色器对象
 
 };
 

+ 55 - 53
demo/VideoPlayerGL/GLWidget/PlayerGLWidget2.cpp

@@ -4,7 +4,6 @@
 #include <QTextStream>
 #include <cstdint>
 #include <cstring>
-#include <gl/gl.h>
 #include <qobject.h>
 #include <qopenglext.h>
 #include <string.h>
@@ -138,54 +137,7 @@ void PlayerGLWidget2::initializeGL()
 
     
 
-    /* -------------------------------------------------------------------------------------
-     * 纹理对象
-     * ------------------------------------------------------------------------------------ */
-    glGenTextures(1, &m_texture1); // 生成纹理对象
-    glBindTexture(GL_TEXTURE_2D, m_texture1); // 绑定纹理对象
-    /* 设置当前绑定的纹理对象的环绕、过滤方式 */
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // S轴环绕方式
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // T轴环绕方式
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // 纹理缩小过滤方式
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 纹理放大过滤方式
-
-    /* 纹理数据,最后一个参数是纹理数据的指针,0表示数据在当前上下文中 */
-    /* 纹理数据,最后一个参数是纹理数据的指针 */
-    glTexImage2D(
-        GL_TEXTURE_2D,
-        0,                          // Mipmap 级别
-        GL_RGBA,                    // 纹理内部格式
-        m_imageTexture.width(),     // 纹理宽度
-        m_imageTexture.height(),    // 纹理高度
-        0,                          // 边框(必须为 0)
-        GL_RGBA,                    // 数据格式(与 QImage 格式匹配)
-        GL_UNSIGNED_BYTE,           // 数据类型
-        m_imageTexture.bits()       // 数据指针
-    );
-    glGenerateMipmap(GL_TEXTURE_2D); // 生成多级渐远纹理
-
-    glGenTextures(1, &m_texture2); // 生成纹理对象
-    glBindTexture(GL_TEXTURE_2D, m_texture2); // 绑定纹理对象
-    /* 设置当前绑定的纹理对象的环绕、过滤方式 */
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // S轴环绕方式
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // T轴环绕方式
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // 纹理缩小过滤方式
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 纹理放大过滤方式
-
-    /* 纹理数据,最后一个参数是纹理数据的指针,0表示数据在当前上下文中 */
-    /* 纹理数据,最后一个参数是纹理数据的指针 */
-    glTexImage2D(
-        GL_TEXTURE_2D,
-        0,                          // Mipmap 级别
-        GL_RGBA,                    // 纹理内部格式
-        m_imageTexture2.width(),     // 纹理宽度
-        m_imageTexture2.height(),    // 纹理高度
-        0,                          // 边框(必须为 0)
-        GL_RGBA,                    // 数据格式(与 QImage 格式匹配)
-        GL_UNSIGNED_BYTE,           // 数据类型
-        m_imageTexture2.bits()       // 数据指针
-    );
-    glGenerateMipmap(GL_TEXTURE_2D); // 生成多级渐远纹理
+    
 
 
     /* -------------------------------------------------------------------------------------
@@ -251,8 +203,57 @@ void PlayerGLWidget2::initializeGL()
     /* 解绑着色器程序对象 */
     // glUseProgram(0); // 解绑着色器程序对象
 
-    /* 指定纹理对象名称 */
-    glUseProgram(m_shaderProgram); // 使用着色器程序对象
+    /* 使用着色器程序对象 */
+    glUseProgram(m_shaderProgram);
+
+    /* -------------------------------------------------------------------------------------
+     * 纹理对象
+     * ------------------------------------------------------------------------------------ */
+    glGenTextures(1, &m_texture1); // 生成纹理对象
+    glBindTexture(GL_TEXTURE_2D, m_texture1); // 绑定纹理对象
+    /* 设置当前绑定的纹理对象的环绕、过滤方式 */
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // S轴环绕方式
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // T轴环绕方式
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // 纹理缩小过滤方式
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 纹理放大过滤方式
+
+    /* 纹理数据,最后一个参数是纹理数据的指针,0表示数据在当前上下文中 */
+    /* 纹理数据,最后一个参数是纹理数据的指针 */
+    glTexImage2D(
+        GL_TEXTURE_2D,
+        0,                          // Mipmap 级别
+        GL_RGBA,                    // 纹理内部格式
+        m_imageTexture.width(),     // 纹理宽度
+        m_imageTexture.height(),    // 纹理高度
+        0,                          // 边框(必须为 0)
+        GL_RGBA,                    // 数据格式(与 QImage 格式匹配)
+        GL_UNSIGNED_BYTE,           // 数据类型
+        m_imageTexture.bits()       // 数据指针
+    );
+    glGenerateMipmap(GL_TEXTURE_2D); // 生成多级渐远纹理
+
+    glGenTextures(1, &m_texture2); // 生成纹理对象
+    glBindTexture(GL_TEXTURE_2D, m_texture2); // 绑定纹理对象
+    /* 设置当前绑定的纹理对象的环绕、过滤方式 */
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // S轴环绕方式
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // T轴环绕方式
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // 纹理缩小过滤方式
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 纹理放大过滤方式
+
+    /* 纹理数据,最后一个参数是纹理数据的指针,0表示数据在当前上下文中 */
+    /* 纹理数据,最后一个参数是纹理数据的指针 */
+    glTexImage2D(
+        GL_TEXTURE_2D,
+        0,                          // Mipmap 级别
+        GL_RGBA,                    // 纹理内部格式
+        m_imageTexture2.width(),     // 纹理宽度
+        m_imageTexture2.height(),    // 纹理高度
+        0,                          // 边框(必须为 0)
+        GL_RGBA,                    // 数据格式(与 QImage 格式匹配)
+        GL_UNSIGNED_BYTE,           // 数据类型
+        m_imageTexture2.bits()       // 数据指针
+    );
+    glGenerateMipmap(GL_TEXTURE_2D); // 生成多级渐远纹理
     
 }
 
@@ -275,7 +276,7 @@ void PlayerGLWidget2::paintGL()
     glActiveTexture(GL_TEXTURE0);
     glBindTexture(GL_TEXTURE_2D, m_texture1); // 绑定纹理对象
     glActiveTexture(GL_TEXTURE1);
-    glBindTexture(GL_TEXTURE_2D, m_texture1); // 绑定纹理对象
+    glBindTexture(GL_TEXTURE_2D, m_texture2); // 绑定纹理对象
     /* 绑定VAO,绘制图形 */
     glBindVertexArray(m_VAO1); // 绑定VAO
 
@@ -319,7 +320,8 @@ bool PlayerGLWidget2::loadShaderCode(const QString &fileName, char** shaderCode)
     /* 分配内存 */
     *shaderCode = new char[len];
     /* 复制字符串 */
-    strcpy_s(*shaderCode, len, ba.toStdString().c_str());
+    // strcpy_s(*shaderCode, len, ba.toStdString().c_str());
+    std::strncpy(*shaderCode, ba.constData(), len);
 
     return true;
 }

+ 1 - 1
demo/VideoPlayerGL/GLWidget/PlayerGLWidget2.h

@@ -5,7 +5,7 @@
 #include <QOpenGLWidget>
 #include <QOpenGLFunctions_3_3_Core>
 #include <cstdint>
-#include <gl/gl.h>
+#include <GL/gl.h>
 #include <qchar.h>
 #include <qimage.h>
 

+ 2 - 1
demo/VideoPlayerGL/shader/fragmentShader.glsl

@@ -10,6 +10,7 @@ uniform sampler2D ourTexture2; //纹理采样器
 
 void main()
 {
-    // FragColor = texture(ourTexture1, TexCoord) * vec4(vertexColor, 1.0); // 乘以颜色
+    //FragColor = vec4(1,0,0,1);
+    //FragColor = texture(ourTexture1, TexCoord) * vec4(vertexColor, 1.0); // 乘以颜色
     FragColor = mix(texture(ourTexture1, TexCoord), texture(ourTexture2, TexCoord), 0.2); // 混合两种纹理
 }

+ 1 - 1
demo/VideoPlayerGL/widget.cpp

@@ -12,7 +12,7 @@ Widget::Widget(QWidget *parent)
 {
     ui->setupUi(this);
 
-    m_playerGLWidget = new PlayerGLWidget2(ui->widget_display);
+    m_playerGLWidget = new PlayerGLWidget(ui->widget_display);
     m_playerGLWidget->setGeometry(0, 0, ui->widget_display->width(), ui->widget_display->height());
     m_playerGLWidget->setStyleSheet(R"(border-radius:10px;)");
 

+ 1 - 1
demo/VideoPlayerGL/widget.h

@@ -27,7 +27,7 @@ protected:
 private:
     Ui::Widget *ui;
 
-    PlayerGLWidget2 *m_playerGLWidget = nullptr;
+    PlayerGLWidget *m_playerGLWidget = nullptr;
 
     QTimer m_timer;