PlayerGLWidget.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. #include "PlayerGLWidget.h"
  2. #include "spdlog/spdlog.h"
  3. #include <gl/gl.h>
  4. #include <qopenglext.h>
  5. #include <QVariant>
  6. #include <QDateTime>
  7. PlayerGLWidget::PlayerGLWidget(QWidget *parent) : QOpenGLWidget(parent)
  8. {
  9. }
  10. PlayerGLWidget::~PlayerGLWidget()
  11. {
  12. }
  13. void PlayerGLWidget::initializeGL()
  14. {
  15. /* 初始化OpenGL函数,OpenGL的函数是在运行时才确定函数指针的 */
  16. initializeOpenGLFunctions();
  17. /* -------------------------------------------------------------------------------------
  18. * 顶点数组对象
  19. * ------------------------------------------------------------------------------------ */
  20. /* 生成一个VAO,返回的ID存储在VAO1中 */
  21. glGenVertexArrays(1, &m_VAO1);
  22. glBindVertexArray(m_VAO1);
  23. /* 创建一个三角形坐标数组,这里的z坐标都是0,表示平面2维 */
  24. float vertices[] = {
  25. 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 右下
  26. -0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 左下
  27. 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // 顶部
  28. };
  29. /* -------------------------------------------------------------------------------------
  30. * 创建顶点数组缓冲区
  31. * (GLuint就是unsigned int)
  32. * ------------------------------------------------------------------------------------ */
  33. /* 生成一个VBO,返回的ID存储在VBO1中 */
  34. glGenBuffers(1, &m_VBO1);
  35. /* OpenGL有很多缓冲对象类型,顶点缓冲对象的缓冲类型是GL_ARRAY_BUFFER
  36. * 绑定VBO1到GL_ARRAY_BUFFER上,表示VBO1是一个顶点缓冲对象 */
  37. glBindBuffer(GL_ARRAY_BUFFER, m_VBO1);
  38. /* 将顶点数据复制到缓冲区的内存中 */
  39. glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
  40. /* ---------------------------------------------------------------------------------------
  41. * 创建EBO
  42. * ------------------------------------------------------------------------------------ */
  43. unsigned int indices[] = {
  44. 0, 1, 3, // 第一个三角形
  45. 1, 2, 3 // 第二个三角形
  46. };
  47. /* 创建一个EBO,返回的ID存储在EBO1中 */
  48. glGenBuffers(1, &m_EBO1);
  49. /* 绑定缓冲区 */
  50. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBO1);
  51. glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
  52. /* -------------------------------------------------------------------------------------
  53. * 链接顶点属性
  54. * ------------------------------------------------------------------------------------ */
  55. /* 设置顶点属性指针,告诉OpenGL如何解析顶点数据
  56. * 1、glVertexAttribPointer函数的第一个参数是顶点属性的位置值
  57. * 2、第二个参数是每个顶点属性的大小,这里是3,因为我们有3个坐标分量。
  58. * 3、第三个参数是数据类型,这里是GL_FLOAT,表示数据类型是float。
  59. * 4、第四个参数是是否归一化,这里是GL_FALSE,表示不归一化。
  60. * 5、第五个参数是步长,这里是0,表示紧凑存储。
  61. * 6、第六个参数是偏移量,这里是0,表示从缓冲区的开头开始读取数据。
  62. * 7、最后一个参数是启用顶点属性指针,告诉OpenGL我们要使用这个顶点属性。
  63. */
  64. glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
  65. /* 启用顶点属性指针 */
  66. glEnableVertexAttribArray(0);
  67. /* 设置颜色位置,最后一个参数表示从第3个字节开始,倒数第二个参数是两个数据之间起始位置间隔6个字节 */
  68. glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), (void*)(3*sizeof(float)));
  69. glEnableVertexAttribArray(1);
  70. /* -------------------------------------------------------------------------------------
  71. * 顶点着色器
  72. * ------------------------------------------------------------------------------------ */
  73. /* 顶点着色器的源代码
  74. * GLSL看起来很像C语言。每个着色器都起始于一个版本声明。OpenGL 3.3以及和更高版本中,GLSL版本号和OpenGL的版本是匹配的(比如说GLSL 420版本对应于OpenGL 4.2)。
  75. * 同样明确表示我们会使用核心模式。
  76. * 1、#version 330 core 设置版本号和模式
  77. * 2、使用in关键字,在顶点着色器中声明所有的输入顶点属性(Input Vertex Attribute)。现在我们只关心位置(Position)数据,所以我们只需要一个顶点属性。
  78. * GLSL有一个向量数据类型,它包含1到4个float分量,包含的数量可以从它的后缀数字看出来。由于每个顶点都有一个3D坐标,我们就创建一个vec3输入变量aPos。
  79. * 我们同样也通过layout (location = 0)设定了输入变量的位置值(Location)
  80. * 3、gl_Position是一个内建变量,它是一个vec4类型的变量,表示顶点在裁剪空间中的位置。我们将aPos赋值给gl_Position,OpenGL会自动将vec3转换为vec4。
  81. */
  82. const char* vertexShaderSource = R"(
  83. #version 330 core
  84. layout(location = 0) in vec3 aPos;
  85. layout(location = 1) in vec3 aColor;
  86. uniform float offset;
  87. out vec3 vertexColor; //向片段着色器传递颜色数据
  88. void main()
  89. {
  90. gl_Position = vec4(aPos.x + offset, aPos.y, aPos.z, 1.0);
  91. vertexColor = aColor;
  92. }
  93. )";
  94. /* -------------------------------------------------------------------------------------
  95. * 编译着色器
  96. * ------------------------------------------------------------------------------------ */
  97. /* 创建一个着色器对象,参数是着色器类型
  98. * GL_VERTEX_SHADER代表是顶点着色器,GL_FRAGMENT_SHADER代表是片段着色器
  99. * glCreateShader函数返回一个着色器对象的ID,OpenGL会为我们分配一个唯一的ID
  100. */
  101. GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
  102. /* 将着色器源代码附加到着色器对象上 */
  103. glShaderSource(vertexShader, 1, &vertexShaderSource,nullptr);
  104. /* 编译着色器 */
  105. glCompileShader(vertexShader);
  106. /* 检查编译错误 */
  107. GLint success;
  108. GLchar infoLog[512];
  109. glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
  110. if (!success)
  111. {
  112. glGetShaderInfoLog(vertexShader, sizeof(infoLog), nullptr, infoLog);
  113. SPDLOG_ERROR("着色器编译错误: {}", infoLog);
  114. }
  115. /* -------------------------------------------------------------------------------------
  116. * 片段着色器
  117. * ------------------------------------------------------------------------------------ */
  118. /* 片段着色器的源代码 */
  119. const char* fragmentShaderSource = R"(
  120. #version 330 core
  121. out vec4 FragColor;
  122. in vec3 vertexColor;
  123. void main()
  124. {
  125. FragColor = vec4(vertexColor, 1.0);
  126. }
  127. )";
  128. /* 创建一个片段着色器对象 */
  129. GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
  130. /* 将片段着色器源代码附加到片段着色器对象上 */
  131. glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
  132. /* 编译片段着色器 */
  133. glCompileShader(fragmentShader);
  134. /* 检查编译错误 */
  135. success = 0;
  136. glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
  137. if (!success)
  138. {
  139. glGetShaderInfoLog(fragmentShader, sizeof(infoLog), nullptr, infoLog);
  140. SPDLOG_ERROR("片段着色器编译错误: {}", infoLog);
  141. }
  142. /* -------------------------------------------------------------------------------------
  143. * 着色器程序对象
  144. * ------------------------------------------------------------------------------------ */
  145. /* 创建一个着色器程序对象 */
  146. m_shaderProgram = glCreateProgram();
  147. /* 将顶点着色器和片段着色器附加到着色器程序对象上 */
  148. glAttachShader(m_shaderProgram, vertexShader);
  149. glAttachShader(m_shaderProgram, fragmentShader);
  150. /* 链接着色器程序对象 */
  151. glLinkProgram(m_shaderProgram);
  152. /* 检查链接错误 */
  153. glGetProgramiv(m_shaderProgram, GL_LINK_STATUS, &success);
  154. if(!success)
  155. {
  156. glGetProgramInfoLog(m_shaderProgram, 512, nullptr, infoLog);
  157. SPDLOG_ERROR("着色器程序链接错误: {}", infoLog);
  158. }
  159. /* 删除着色器对象,因为它们已经链接到着色器程序对象上了 */
  160. glDeleteShader(vertexShader);
  161. glDeleteShader(fragmentShader);
  162. }
  163. void PlayerGLWidget::resizeGL(int w, int h)
  164. {
  165. }
  166. void PlayerGLWidget::paintGL()
  167. {
  168. /* glClearColor函数是一个状态设置函数,而glClear函数则是一个状态使用的函数,
  169. * 它使用了当前的状态来获取应该清除为的颜色。 */
  170. glClearColor(0.1f, 0.3f, 0.3f, 1.0f);
  171. glClear(GL_COLOR_BUFFER_BIT);
  172. /* 激活色器程序对象 */
  173. glUseProgram(m_shaderProgram);
  174. /* 更新uniform数据 */
  175. /* uniform变量是一个全局变量,它的值在着色器程序中是唯一的,不能被顶点着色器和片段着色器同时使用。
  176. * uniform变量的值在每次绘制时都可以更新。 */
  177. static float offset = 0;
  178. offset += 0.01f;
  179. if (offset > 1.0f) offset = 0.0f;
  180. int offsetUniform = glGetUniformLocation(m_shaderProgram, "offset");
  181. glUniform1f(offsetUniform, offset);
  182. /* 绑定VAO1到 GL_VERTEX_ARRAY 上,表示VAO1是一个顶点数组对象 */
  183. glBindVertexArray(m_VAO1);
  184. /* 绘制三角形 */
  185. /* glDrawArrays函数的第一个参数是绘制模式,这里是GL_TRIANGLES,表示绘制三角形。
  186. * 第二个参数是起始索引,这里是0,表示从第一个顶点开始绘制。
  187. * 第三个参数是顶点数量,这里是3,表示绘制3个顶点。
  188. */
  189. glDrawArrays(GL_TRIANGLES, 0, 3);
  190. // glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
  191. /* 解绑VAO1 */
  192. glBindVertexArray(0);
  193. }