OpenGL with QtWidgets:材质、光照贴图

妖狐艹你老母 2022-09-12 04:44 301阅读 0赞

(本文是LearnOpenGL的学习笔记, 教程中文翻译地址https://learnopengl-cn.github.io/(备用地址https://learnopengl-cn.readthedocs.io/zh/latest/),写于 2021-9-20)

0.前言

上一节学习了基础光照(https://mp.csdn.net/mp_blog/creation/editor/106005766),教程接下来讲了材质和光照贴图。

1.知识点

如果我们想要在OpenGL中模拟多种类型的物体,我们必须为每个物体分别定义一个材质(Material)属性。可以用这三个分量来定义一个材质颜色(Material Color):环境光照(Ambient Lighting)、漫反射光照(Diffuse Lighting)和镜面光照(Specular Lighting)。通过为每个分量指定一个颜色,我们就能够对物体的颜色输出有着精细的控制了。再添加反光度(Shininess)这个分量到上述的三个颜色中,这就有我们需要的所有材质属性了:

  1. struct Material {
  2. vec3 ambient;
  3. vec3 diffuse;
  4. vec3 specular;
  5. float shininess;
  6. };
  7. uniform Material material;

ambient材质向量定义了在环境光照下这个物体反射得是什么颜色,通常这是和物体颜色相同的颜色。diffuse材质向量定义了在漫反射光照下物体的颜色。(和环境光照一样)漫反射颜色也要设置为我们需要的物体颜色。specular材质向量设置的是镜面光照对物体的颜色影响(或者甚至可能反射一个物体特定的镜面高光颜色)。最后,shininess影响镜面高光的散射/半径。

现实世界中的物体通常并不只包含有一种材质,而是由多种材质所组成。引入漫反射和镜面光贴图(Map)。这允许我们对物体的漫反射分量(以及间接地对环境光分量,它们几乎总是一样的)和镜面光分量有着更精确的控制。

  1. struct Material {
  2. sampler2D diffuse; //环境光和漫反射取同一个纹理
  3. sampler2D specular;
  4. float shininess;
  5. };

2.实现代码

(项目git链接:https://github.com/gongjianbo/OpenGLwithQtWidgets.git)

为了简化代码,我把 Camrea 观察矩阵给固定了不能旋转和移动

材质效果:

watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBA6b6a5bu65rOi_size_14_color_FFFFFF_t_70_g_se_x_16

光照贴图效果:

watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBA6b6a5bu65rOi_size_15_color_FFFFFF_t_70_g_se_x_16

光照贴图部分的代码(GLLightingMap类)如下:

  1. #pragma once
  2. #include <QOpenGLWidget>
  3. #include <QOpenGLFunctions_3_3_Core>
  4. #include <QOpenGLShaderProgram>
  5. #include <QOpenGLVertexArrayObject>
  6. #include <QOpenGLBuffer>
  7. #include <QOpenGLTexture>
  8. #include <QVector3D>
  9. #include <QMatrix4x4>
  10. #include <QQuaternion>
  11. #include <QTimer>
  12. //光照贴图
  13. //QOpenGLWidget窗口上下文
  14. //QOpenGLFunctions访问OpenGL接口,可以不继承作为成员变量使用
  15. class GLLightingMap
  16. : public QOpenGLWidget
  17. , protected QOpenGLFunctions_3_3_Core
  18. {
  19. Q_OBJECT
  20. public:
  21. explicit GLLightingMap(QWidget *parent = nullptr);
  22. ~GLLightingMap();
  23. protected:
  24. //【】继承QOpenGLWidget后重写这三个虚函数
  25. //设置OpenGL资源和状态。在第一次调用resizeGL或paintGL之前被调用一次
  26. void initializeGL() override;
  27. //渲染OpenGL场景,每当需要更新小部件时使用
  28. void paintGL() override;
  29. //设置OpenGL视口、投影等,每当尺寸大小改变时调用
  30. void resizeGL(int width, int height) override;
  31. private:
  32. void initShader();
  33. QOpenGLTexture *initTexture(const QString &imgpath);
  34. private:
  35. //着色器程序
  36. QOpenGLShaderProgram lightingShader,lampShader;
  37. //顶点数组对象
  38. QOpenGLVertexArrayObject lightingVao,lampVao;
  39. //顶点缓冲
  40. QOpenGLBuffer vbo;
  41. //纹理
  42. QOpenGLTexture *diffuseMap{ nullptr };
  43. QOpenGLTexture *specularMap{ nullptr };
  44. //
  45. QTimer timer;
  46. int rotate{ 0 };
  47. };
  48. #include "GLLightingMap.h"
  49. #include <cmath>
  50. #include <QtMath>
  51. #include <QDebug>
  52. GLLightingMap::GLLightingMap(QWidget *parent)
  53. : QOpenGLWidget(parent)
  54. {
  55. connect(&timer,&QTimer::timeout,this,[this](){
  56. rotate+=1;
  57. if(isVisible()){
  58. update();
  59. }
  60. });
  61. timer.setInterval(50);
  62. }
  63. GLLightingMap::~GLLightingMap()
  64. {
  65. //initializeGL在显示时才调用,释放未初始化的会异常
  66. if(!isValid())
  67. return;
  68. //QOpenGLWidget
  69. //三个虚函数不需要makeCurrent,对应的操作已由框架完成
  70. //但是释放时需要设置当前上下文
  71. makeCurrent();
  72. vbo.destroy();
  73. lightingVao.destroy();
  74. lampVao.destroy();
  75. delete diffuseMap;
  76. delete specularMap;
  77. doneCurrent();
  78. }
  79. void GLLightingMap::initializeGL()
  80. {
  81. //为当前上下文初始化OpenGL函数解析
  82. initializeOpenGLFunctions();
  83. initShader();
  84. //方块的顶点、法向量、纹理坐标
  85. float vertices[] = {
  86. // positions // normals // texture coords
  87. -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
  88. 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f,
  89. 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
  90. 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
  91. -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,
  92. -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
  93. -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
  94. 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,
  95. 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
  96. 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
  97. -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
  98. -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
  99. -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
  100. -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
  101. -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
  102. -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
  103. -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
  104. -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
  105. 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
  106. 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
  107. 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
  108. 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
  109. 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
  110. 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
  111. -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
  112. 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f,
  113. 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
  114. 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
  115. -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f,
  116. -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
  117. -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
  118. 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,
  119. 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
  120. 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
  121. -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
  122. -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f
  123. };
  124. vbo=QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
  125. vbo.create();
  126. //light vao
  127. lightingVao.create();
  128. lightingVao.bind();
  129. vbo.bind();
  130. vbo.allocate(vertices,sizeof(vertices));
  131. //setAttributeBuffer(int location, GLenum type, int offset, int tupleSize, int stride = 0)
  132. lightingShader.setAttributeBuffer(0, GL_FLOAT, sizeof(GLfloat) * 0, 3, sizeof(GLfloat) * 8);
  133. lightingShader.enableAttributeArray(0);
  134. lightingShader.setAttributeBuffer(1, GL_FLOAT, sizeof(GLfloat) * 3, 3, sizeof(GLfloat) * 8);
  135. lightingShader.enableAttributeArray(1);
  136. lightingShader.setAttributeBuffer(2, GL_FLOAT, sizeof(GLfloat) * 6, 2, sizeof(GLfloat) * 8);
  137. lightingShader.enableAttributeArray(2);
  138. vbo.release();
  139. lightingVao.release();
  140. //lamp vao
  141. lampVao.create();
  142. lampVao.bind();
  143. vbo.bind();
  144. //setAttributeBuffer(int location, GLenum type, int offset, int tupleSize, int stride = 0)
  145. lampShader.setAttributeBuffer(0, GL_FLOAT, 0, 3, sizeof(GLfloat) * 8);
  146. lampShader.enableAttributeArray(0);
  147. vbo.release();
  148. lampVao.release();
  149. //纹理
  150. diffuseMap = initTexture(":/container2.png");
  151. specularMap = initTexture(":/container2_specular.png");
  152. //shader configuration
  153. lightingShader.bind();
  154. lightingShader.setUniformValue("material.diffuse", 0);
  155. lightingShader.setUniformValue("material.specular", 1);
  156. lightingShader.release();
  157. timer.start();
  158. }
  159. void GLLightingMap::paintGL()
  160. {
  161. glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
  162. //清除深度缓冲
  163. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  164. //Z缓冲(Z-buffer),深度缓冲(Depth Buffer)。
  165. glEnable(GL_DEPTH_TEST);
  166. //draw lighting
  167. lightingShader.bind();
  168. QMatrix4x4 view; //观察矩阵
  169. view.translate(0.0f, 0.0f, -5.0f);
  170. view.rotate(45, QVector3D(1.0f, 0.8f, 0.0f));
  171. lightingShader.setUniformValue("view", view);
  172. QMatrix4x4 projection; //透视投影
  173. projection.perspective(45.0f, 1.0f * width() / height(), 0.1f, 100.0f);
  174. lightingShader.setUniformValue("projection", projection);
  175. QMatrix4x4 model;//模型矩阵
  176. lightingShader.setUniformValue("model", model);
  177. //因为要获取灯的位置,所以提前算灯的model矩阵
  178. model = QMatrix4x4();
  179. model.translate(QVector3D(1.0f, 1.0f, -1.0f));
  180. model.scale(0.3f);
  181. QVector3D light_pos = model.map(QVector3D(0.0f, 0.0f, 0.0f));
  182. QMatrix4x4 vv = view.inverted(); //逆矩阵求观察点位置
  183. QVector3D view_pos = vv.map(QVector3D(0.0f, 0.0f, 0.0f));
  184. lightingShader.setUniformValue("light.position", light_pos);
  185. lightingShader.setUniformValue("viewPos", view_pos);
  186. //光照-light properties
  187. QVector3D light_color = QVector3D(1.0f, 1.0f, 1.0f);
  188. QVector3D diffuse_color = light_color * 0.5f; // decrease the influence
  189. QVector3D ambient_color = diffuse_color * 0.2f; // low influence
  190. lightingShader.setUniformValue("light.ambient", ambient_color);
  191. lightingShader.setUniformValue("light.diffuse", diffuse_color);
  192. lightingShader.setUniformValue("light.specular", QVector3D(1.0f, 1.0f, 1.0f));
  193. //材质-material properties
  194. //shininess影响镜面高光的散射/半径
  195. lightingShader.setUniformValue("material.shininess", 64.0f);
  196. lightingVao.bind();
  197. //绑定2d纹理
  198. //bind diffuse map
  199. glActiveTexture(GL_TEXTURE0);
  200. diffuseMap->bind();
  201. //bind specular map
  202. glActiveTexture(GL_TEXTURE1);
  203. specularMap->bind();
  204. glDrawArrays(GL_TRIANGLES, 0, 36);
  205. lightingVao.release();
  206. lightingShader.release();
  207. //draw lamp
  208. lampShader.bind();
  209. lampShader.setUniformValue("view", view);
  210. lampShader.setUniformValue("projection", projection);
  211. lampShader.setUniformValue("model", model);
  212. lampVao.bind();
  213. glDrawArrays(GL_TRIANGLES, 0, 36);
  214. lampVao.release();
  215. lampShader.release();
  216. }
  217. void GLLightingMap::resizeGL(int width, int height)
  218. {
  219. glViewport(0, 0, width, height);
  220. }
  221. void GLLightingMap::initShader()
  222. {
  223. //lingting shader
  224. //in输入,out输出,uniform从cpu向gpu发送
  225. const char *lighting_vertex=R"(#version 330 core
  226. layout (location = 0) in vec3 aPos;
  227. layout (location = 1) in vec3 aNormal;
  228. layout (location = 2) in vec2 aTexCoords;
  229. out vec3 FragPos;
  230. out vec3 Normal;
  231. out vec2 TexCoords;
  232. uniform mat4 model;
  233. uniform mat4 view;
  234. uniform mat4 projection;
  235. void main()
  236. {
  237. FragPos = vec3(model * vec4(aPos, 1.0));
  238. Normal = mat3(transpose(inverse(model))) * aNormal;
  239. TexCoords = aTexCoords;
  240. gl_Position = projection * view * vec4(FragPos, 1.0);
  241. })";
  242. const char *lighting_fragment=R"(#version 330 core
  243. out vec4 FragColor;
  244. struct Material {
  245. sampler2D diffuse;
  246. sampler2D specular;
  247. float shininess;
  248. };
  249. struct Light {
  250. vec3 position;
  251. vec3 ambient;
  252. vec3 diffuse;
  253. vec3 specular;
  254. };
  255. in vec3 FragPos;
  256. in vec3 Normal;
  257. in vec2 TexCoords;
  258. uniform vec3 viewPos;
  259. uniform Material material;
  260. uniform Light light;
  261. void main()
  262. {
  263. // ambient
  264. vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb;
  265. // diffuse
  266. vec3 norm = normalize(Normal);
  267. vec3 lightDir = normalize(light.position - FragPos);
  268. float diff = max(dot(norm, lightDir), 0.0);
  269. vec3 diffuse = light.diffuse * diff * texture(material.diffuse, TexCoords).rgb;
  270. // specular
  271. vec3 viewDir = normalize(viewPos - FragPos);
  272. vec3 reflectDir = reflect(-lightDir, norm);
  273. float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
  274. vec3 specular = light.specular * spec * texture(material.specular, TexCoords).rgb;
  275. vec3 result = ambient + diffuse + specular;
  276. FragColor = vec4(result, 1.0);
  277. })";
  278. //将source编译为指定类型的着色器,并添加到此着色器程序
  279. if(!lightingShader.addCacheableShaderFromSourceCode(
  280. QOpenGLShader::Vertex,lighting_vertex)){
  281. qDebug()<<"compiler vertex error"<<lightingShader.log();
  282. }
  283. if(!lightingShader.addCacheableShaderFromSourceCode(
  284. QOpenGLShader::Fragment,lighting_fragment)){
  285. qDebug()<<"compiler fragment error"<<lightingShader.log();
  286. }
  287. //使用addShader()将添加到该程序的着色器链接在一起。
  288. if(!lightingShader.link()){
  289. qDebug()<<"link shaderprogram error"<<lightingShader.log();
  290. }
  291. //lamp shader
  292. const char *lamp_vertex=R"(#version 330 core
  293. layout (location = 0) in vec3 aPos;
  294. uniform mat4 model;
  295. uniform mat4 view;
  296. uniform mat4 projection;
  297. void main()
  298. {
  299. gl_Position = projection * view * model * vec4(aPos, 1.0f);
  300. })";
  301. const char *lamp_fragment=R"(#version 330 core
  302. out vec4 FragColor;
  303. void main()
  304. {
  305. FragColor = vec4(1.0);
  306. })"; // set alle 4 vector values to 1.0
  307. if(!lampShader.addCacheableShaderFromSourceCode(
  308. QOpenGLShader::Vertex,lamp_vertex)){
  309. qDebug()<<"compiler vertex error"<<lampShader.log();
  310. }
  311. if(!lampShader.addCacheableShaderFromSourceCode(
  312. QOpenGLShader::Fragment,lamp_fragment)){
  313. qDebug()<<"compiler fragment error"<<lampShader.log();
  314. }
  315. if(!lampShader.link()){
  316. qDebug()<<"link shaderprogram error"<<lampShader.log();
  317. }
  318. }
  319. QOpenGLTexture *GLLightingMap::initTexture(const QString &imgpath)
  320. {
  321. QOpenGLTexture *texture = new QOpenGLTexture(QImage(imgpath), QOpenGLTexture::GenerateMipMaps);
  322. if(!texture->isCreated()){
  323. qDebug() << "Failed to create texture";
  324. }
  325. //set the texture wrapping parameters
  326. //等于glTexParameteri(GLtexture_2D, GLtexture_WRAP_S, GL_REPEAT);
  327. texture->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::Repeat);
  328. texture->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::Repeat);//
  329. //set texture filtering parameters
  330. //等价于glTexParameteri(GLtexture_2D, GLtexture_MIN_FILTER, GL_LINEAR);
  331. texture->setMinificationFilter(QOpenGLTexture::Linear);
  332. texture->setMagnificationFilter(QOpenGLTexture::Linear);
  333. return texture;
  334. }

3.参考

LearnOpenGL:https://learnopengl-cn.github.io/02%20Lighting/04%20Lighting%20maps/

博客(Qt+OpenGL):https://www.jianshu.com/p/855ce8d81849

博客(Qt+OpenGL):https://blog.csdn.net/z136411501/article/details/80149125

博客(Qt+OpenGL):https://blog.csdn.net/qq_40946921/article/details/106200378

发表评论

表情:
评论列表 (有 0 条评论,301人围观)

还没有评论,来说两句吧...

相关阅读