#include "PlayerGLWidget2.h" #include "spdlog.h" #include #include #include #include #include #include #include #include #include PlayerGLWidget2::PlayerGLWidget2(QWidget *parent) : QOpenGLWidget(parent) { m_vertexCount = 4; // 顶点数量 m_vertexSize = 8; // 顶点大小 // m_vertices = new float[m_vertexCount * m_vertexSize] { // /* ----- 位置 -----|-------- 颜色 ----------|----- 纹理坐标 ------ */ // -0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, // 左下 // 0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, // 右下 // 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // 右上 // -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f // 右上 // }; m_vertices = new float[m_vertexCount * m_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 // 右上 }; m_indexCount = 6; // 索引数量 m_indices = new uint32_t[m_indexCount] { 0, 1, 2, // 第一个三角形 2, 3, 0 // 第二个三角形 }; /* 打开着色器代码 */ QString vertexShaderFile = ":/shader/vertexShader.glsl"; if (!loadShader(vertexShaderFile, &m_vertexShaderCode)) { return; } // SPDLOG_INFO("顶点着色器代码: \n{}", m_vertexShaderCode); QString fragmentShaderFile = ":/shader/fragmentShader.glsl"; if (!loadShader(fragmentShaderFile, &m_fragmentShaderCode)) { return; } // SPDLOG_INFO("片段着色器代码: \n{}", m_fragmentShaderCode); /* 加载问了图片 */ QString imageFile = ":/Image/1.jpg"; if (!loadImage(imageFile, m_imageTexture)) { return; } } PlayerGLWidget2::~PlayerGLWidget2() { if(m_vertices != nullptr) { delete[] m_vertices; m_vertices = nullptr; } if(m_indices != nullptr) { delete[] m_indices; m_indices = nullptr; } if(m_vertexShaderCode != nullptr) { delete[] m_vertexShaderCode; m_vertexShaderCode = nullptr; } if(m_fragmentShaderCode != nullptr) { delete[] m_fragmentShaderCode; m_fragmentShaderCode = nullptr; } } void PlayerGLWidget2::initializeGL() { /* 初始化OpenGL函数 */ initializeOpenGLFunctions(); /* ------------------------------------------------------------------------------------- * 顶点数组对象 * ------------------------------------------------------------------------------------ */ glGenVertexArrays(1, &m_VAO1); // 生成一个VAO,返回的ID存储在VAO1中 glBindVertexArray(m_VAO1); // 绑定VAO /* ------------------------------------------------------------------------------------- * 创建顶点数组缓冲区 * ------------------------------------------------------------------------------------ */ glGenBuffers(1, &m_VBO1); glBindBuffer(GL_ARRAY_BUFFER, m_VBO1); // 绑定VBO glBufferData(GL_ARRAY_BUFFER, m_vertexCount * m_vertexSize * sizeof(float), m_vertices, GL_STATIC_DRAW); /* ------------------------------------------------------------------------------------- * 创建EBO * ------------------------------------------------------------------------------------ */ glGenBuffers(1, &m_EBO1); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBO1); glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount * sizeof(uint32_t), m_indices, GL_STATIC_DRAW); // 绑定EBO /* ------------------------------------------------------------------------------------- * 链接顶点属性 * ------------------------------------------------------------------------------------ */ // 设置顶点属性指针,告诉OpenGL如何解析顶点数据 glVertexAttribPointer( 0, /* 顶点属性位置,0表示第一个属性 */ 3, /* 每个顶点属性的大小,这里是3,表示x、y、z坐标 */ GL_FLOAT, GL_FALSE, /* 数据类型和是否标准化 */ m_vertexSize * sizeof(float), /* 步长,表示每个顶点属性之间的间隔 */ (void*)0 /* 偏移量,表示该属性(顶点坐标)从第0个字节开始 */ ); /* 启用顶点属性 */ glEnableVertexAttribArray(0); // 设置颜色位置,最后一个参数表示从第3个字节开始,倒数第二个参数是两个数据之间起始位置间隔8个字节 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, m_vertexSize * sizeof(float), (void*)(3*sizeof(float))); glEnableVertexAttribArray(1); /* 设置纹理位置 */ glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, m_vertexSize * sizeof(float), (void*)(6*sizeof(float))); // 纹理坐标位置 glEnableVertexAttribArray(2); // 启用纹理坐标属性 /* ------------------------------------------------------------------------------------- * 纹理对象 * ------------------------------------------------------------------------------------ */ glGenTextures(1, &m_textureID); // 生成纹理对象 glBindTexture(GL_TEXTURE_2D, m_textureID); // 绑定纹理对象 /* 设置当前绑定的纹理对象的环绕、过滤方式 */ 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); // 生成多级渐远纹理 /* ------------------------------------------------------------------------------------- * 着色器程序对象 * ------------------------------------------------------------------------------------ */ /* 编译顶点着色器 */ GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); /* 着色器代码 */ glShaderSource(vertexShader, 1, &m_vertexShaderCode, nullptr); glCompileShader(vertexShader); // 编译着色器 /* 检查编译错误 */ GLint success; GLchar infoLog[512]; glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); if(success == GL_FALSE) { glGetShaderInfoLog(vertexShader, sizeof(infoLog), nullptr, infoLog); SPDLOG_ERROR("顶点着色器编译错误: {}", infoLog); } /* 编译片段着色器 */ GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); /* 加载着色器代码 */ glShaderSource(fragmentShader, 1, &m_fragmentShaderCode, nullptr); glCompileShader(fragmentShader); // 编译着色器 /* 检查编译错误 */ success = 0; memset(infoLog, 0, sizeof(infoLog)); // 清空infoLog glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); if(success == GL_FALSE) { glGetShaderInfoLog(fragmentShader, sizeof(infoLog), nullptr, infoLog); SPDLOG_ERROR("片段着色器编译错误: {}", infoLog); } /* 创建一个着色器程序对象 */ m_shaderProgram = glCreateProgram(); /* 将顶点着色器和片段着色器附加到着色器程序对象上 */ glAttachShader(m_shaderProgram, vertexShader); glAttachShader(m_shaderProgram, fragmentShader); /* 链接着色器程序 */ glLinkProgram(m_shaderProgram); /* 检查链接错误 */ success = 0; memset(infoLog, 0, sizeof(infoLog)); // 清空infoLog glGetProgramiv(m_shaderProgram, GL_LINK_STATUS, &success); if(success == GL_FALSE) { glGetProgramInfoLog(m_shaderProgram, sizeof(infoLog), nullptr, infoLog); SPDLOG_ERROR("着色器链接错误: {}", infoLog); } /* 删除着色器对象 */ glDeleteShader(vertexShader); glDeleteShader(fragmentShader); /* 解绑VAO */ // glBindVertexArray(0); // 解绑VAO /* 解绑VBO */ glBindBuffer(GL_ARRAY_BUFFER, 0); // 解绑VBO /* 解绑EBO */ // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // 解绑EBO /* 解绑纹理对象 */ // glBindTexture(GL_TEXTURE_2D, 0); // 解绑纹理对象 /* 解绑着色器程序对象 */ // glUseProgram(0); // 解绑着色器程序对象 } void PlayerGLWidget2::resizeGL(int w, int h) { glViewport(0, 0, w, h); // 设置视口大小 } void PlayerGLWidget2::paintGL() { glClearColor(0.1f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); /* 激活着色器程序对象 */ glUseProgram(m_shaderProgram); glBindTexture(GL_TEXTURE_2D, m_textureID); // 绑定纹理对象 /* 绑定VAO,绘制图形 */ glBindVertexArray(m_VAO1); // 绑定VAO glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); // 绘制四边形 glBindVertexArray(0); // 解绑VAO } /** * @brief 打开GLSL文件,读取着色器代码 * * @param fileName * @param shaderCode 取出的着色器代码,函数内部会分配内存,需要在使用完后释放 * @return true * @return false */ bool PlayerGLWidget2::loadShader(const QString &fileName, char** shaderCode) { if(*shaderCode != nullptr) { delete[] *shaderCode; *shaderCode = nullptr; } /* 打开文件 */ QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { SPDLOG_ERROR("Failed to open shader file: {}", fileName.toStdString()); return false; } QByteArray ba = file.readAll(); file.close(); // SPDLOG_DEBUG("Shader file content: \n{}", ba.toStdString()); if (ba.isEmpty()) { SPDLOG_ERROR("Shader file is empty: {}", fileName.toStdString()); return false; } /* 计算字符串长度 */ int len = ba.length() + 1; // +1是为了'\0'结尾 /* 分配内存 */ *shaderCode = new char[len]; /* 复制字符串 */ strcpy_s(*shaderCode, len, ba.toStdString().c_str()); return true; } /* 加载图片 */ bool PlayerGLWidget2::loadImage(const QString &fileName, QImage &imageTexture) { imageTexture.load(fileName); if (imageTexture.isNull()) { SPDLOG_ERROR("Failed to load image: {}", fileName.toStdString()); return false; } /* 翻转图片,OpenGL的坐标系和Qt的坐标系是相反的,所以需要翻转图片 */ // imageTexture = imageTexture.mirrored(false, true); // if (imageTexture.isNull()) { // SPDLOG_ERROR("Failed to flip image: {}", fileName.toStdString()); // return false; // } /* 将图片转换为RGBA格式 */ imageTexture = imageTexture.convertToFormat(QImage::Format_RGBA8888); if (imageTexture.isNull()) { SPDLOG_ERROR("Failed to convert image to RGBA format: {}", fileName.toStdString()); return false; } return true; }