|
@@ -0,0 +1,310 @@
|
|
|
+#include "PlayerGLWidget2.h"
|
|
|
+#include "spdlog.h"
|
|
|
+#include <QFile>
|
|
|
+#include <QTextStream>
|
|
|
+#include <cstdint>
|
|
|
+#include <cstring>
|
|
|
+#include <gl/gl.h>
|
|
|
+#include <qobject.h>
|
|
|
+#include <qopenglext.h>
|
|
|
+#include <string.h>
|
|
|
+#include <string>
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+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;
|
|
|
+}
|