123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310 |
- #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;
- }
|