OpenGL学习笔记——纹理

Dear 丶 2023-01-13 10:52 267阅读 0赞

文章目录

  • 一、简介
  • 二、代码实现
    • 2.1单个纹理对象与单元
    • 2.2多个纹理单元与对象

一、简介

如果我们的程序单纯的只是一些颜色什么的,未免不够真实和有趣,但是如果让我们为每一个像素都手动的分配更为真实的颜色值,又过于繁杂而不切实际,因此在OpenGL中也就有了纹理映射这一过程(或者说是为我们的图形进行贴图)。

纹理映射的过程其实非常类似于缓冲区分配的过程,都是在设置OpenGL的状态:
1、创建一个纹理对象。
2、激活纹理单元,并绑定相应的纹理对象。
3、为纹理对象设置相应的参数并将数据传递给纹理对象。
4、在着色器程序中利用纹理采样器(simple2D或者是其他类型),根据纹理坐标以及纹理对象实现对颜色的采样。这一工作主要集中在片元着色器中。
5、绘制出相应的纹理图案。

二、代码实现

2.1单个纹理对象与单元

shader.h

  1. #ifndef SHADER_H
  2. #define SHADER_H
  3. #include <glad/glad.h> // include glad to get all the required OpenGL headers
  4. #include"glm\glm.hpp" //矩阵部分头文件
  5. #include"glm\gtc\matrix_transform.hpp"
  6. #include"glm\gtc\type_ptr.hpp"
  7. #include <string>
  8. #include <fstream>
  9. #include <sstream>
  10. #include <iostream>
  11. class Shader
  12. {
  13. public:
  14. // the program ID
  15. unsigned int ID;
  16. // constructor reads and builds the shader
  17. Shader(const char* vertexPath, const char* fragmentPath);
  18. // use/activate the shader
  19. void use();
  20. // utility uniform functions
  21. void setBool(const std::string &name, bool value) const;
  22. void setInt(const std::string &name, int value) const;
  23. void setFloat(const std::string &name, float value) const;
  24. void setVec2(const std::string &name, const glm::vec2 &value) const;
  25. void setVec2(const std::string &name, float x, float y) const;
  26. void setVec3(const std::string &name, const glm::vec3 &value) const;
  27. void setVec3(const std::string &name, float x, float y, float z) const;
  28. void setVec4(const std::string &name, const glm::vec4 &value) const;
  29. void setVec4(const std::string &name, float x, float y, float z, float w) const;
  30. void setMat2(const std::string &name, const glm::mat2 &mat) const;
  31. void setMat3(const std::string &name, const glm::mat3 &mat) const;
  32. void setMat4(const std::string &name, const glm::mat4 &mat) const;
  33. private:
  34. void checkCompileErrors(GLuint shader, std::string type);
  35. };
  36. Shader::Shader(const char * vertexPath, const char * fragmentPath)
  37. {
  38. // 1. retrieve the vertex/fragment source code from filePath
  39. std::string vertexCode;
  40. std::string fragmentCode;
  41. std::ifstream vShaderFile;
  42. std::ifstream fShaderFile;
  43. // ensure ifstream objects can throw exceptions:
  44. vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
  45. fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
  46. try
  47. {
  48. // open files
  49. vShaderFile.open(vertexPath);
  50. fShaderFile.open(fragmentPath);
  51. std::stringstream vShaderStream, fShaderStream;
  52. // read file's buffer contents into streams
  53. vShaderStream << vShaderFile.rdbuf(); //重定向方法,读取文件数据
  54. fShaderStream << fShaderFile.rdbuf(); //将文件内容通过缓冲区传递给stringstream对象
  55. // close file handlers
  56. vShaderFile.close();
  57. fShaderFile.close();
  58. // convert stream into string,将stringstream对象转换为string对象
  59. vertexCode = vShaderStream.str();
  60. fragmentCode = fShaderStream.str();
  61. }
  62. catch (std::ifstream::failure e)
  63. {
  64. std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
  65. }
  66. const char* vShaderCode = vertexCode.c_str();
  67. const char* fShaderCode = fragmentCode.c_str();
  68. // 2. compile shaders
  69. unsigned int vertex, fragment;
  70. int success;
  71. char infoLog[512];
  72. // vertex Shader
  73. vertex = glCreateShader(GL_VERTEX_SHADER);
  74. glShaderSource(vertex, 1, &vShaderCode, NULL); //将源码传递给顶点着色器对象
  75. glCompileShader(vertex); //运行时动态编译着色器源码,并生成着色器对象
  76. // print compile errors if any
  77. glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
  78. if (!success)
  79. {
  80. glGetShaderInfoLog(vertex, 512, NULL, infoLog);
  81. std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
  82. };
  83. // similiar for Fragment Shader
  84. // fragment Shader
  85. fragment = glCreateShader(GL_FRAGMENT_SHADER);
  86. glShaderSource(fragment, 1, &fShaderCode, NULL);
  87. glCompileShader(fragment);
  88. //checkCompileErrors(fragment, "FRAGMENT");
  89. // shader Program
  90. ID = glCreateProgram(); //创建一个程序对象
  91. glAttachShader(ID, vertex); //将着色器对象添加到程序对象之中
  92. glAttachShader(ID, fragment);
  93. glLinkProgram(ID); //链接为真正的着色器程序
  94. // print linking errors if any
  95. glGetProgramiv(ID, GL_LINK_STATUS, &success);
  96. if (!success)
  97. {
  98. glGetProgramInfoLog(ID, 512, NULL, infoLog);
  99. std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
  100. }
  101. // delete the shaders as they're linked into our program now and no longer necessary
  102. glDeleteShader(vertex); //在生成真正的着色器程序之后,则着色器对象将没有用处
  103. glDeleteShader(fragment);
  104. }
  105. void Shader::use() {
  106. glUseProgram(ID);
  107. }
  108. void Shader::setBool(const std::string & name, bool value) const
  109. {
  110. glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
  111. }
  112. void Shader::setInt(const std::string & name, int value) const
  113. {
  114. glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
  115. }
  116. void Shader::setFloat(const std::string & name, float value) const
  117. {
  118. glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
  119. }
  120. void Shader::setVec2(const std::string & name, const glm::vec2 & value) const
  121. {
  122. glUniform2fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
  123. }
  124. void Shader::setVec2(const std::string & name, float x, float y) const
  125. {
  126. glUniform2f(glGetUniformLocation(ID, name.c_str()), x, y);
  127. }
  128. void Shader::setVec3(const std::string & name, const glm::vec3 & value) const
  129. {
  130. glUniform3fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
  131. }
  132. void Shader::setVec3(const std::string & name, float x, float y, float z) const
  133. {
  134. glUniform3f(glGetUniformLocation(ID, name.c_str()), x, y, z);
  135. }
  136. void Shader::setVec4(const std::string & name, const glm::vec4 & value) const
  137. {
  138. glUniform4fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
  139. }
  140. void Shader::setVec4(const std::string & name, float x, float y, float z, float w) const
  141. {
  142. glUniform4f(glGetUniformLocation(ID, name.c_str()), x, y, z, w);
  143. }
  144. void Shader::setMat2(const std::string & name, const glm::mat2 & mat) const
  145. {
  146. glUniformMatrix2fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
  147. }
  148. void Shader::setMat3(const std::string & name, const glm::mat3 & mat) const
  149. {
  150. glUniformMatrix3fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
  151. }
  152. void Shader::setMat4(const std::string & name, const glm::mat4 & mat) const
  153. {
  154. glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
  155. }
  156. void Shader::checkCompileErrors(GLuint shader, std::string type)
  157. {
  158. GLint success;
  159. GLchar infoLog[1024];
  160. if (type != "PROGRAM")
  161. {
  162. glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
  163. if (!success)
  164. {
  165. glGetShaderInfoLog(shader, 1024, NULL, infoLog);
  166. std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
  167. }
  168. }
  169. else
  170. {
  171. glGetProgramiv(shader, GL_LINK_STATUS, &success);
  172. if (!success)
  173. {
  174. glGetProgramInfoLog(shader, 1024, NULL, infoLog);
  175. std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
  176. }
  177. }
  178. }
  179. #endif

main.cpp

  1. #include<glad\glad.h>
  2. #include<GLFW\glfw3.h>
  3. #include"shader.h"
  4. #define STB_IMAGE_IMPLEMENTATION
  5. #include "stb_image.h"
  6. #include<iostream>
  7. int main() {
  8. glfwInit();
  9. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  10. glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  11. glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  12. GLFWwindow *window = glfwCreateWindow(800, 600, "Texture", 0, 0);
  13. if (window == NULL) {
  14. std::cout << "创建窗口失败" << std::endl;
  15. glfwTerminate(); //释放glfw相关资源
  16. return -1;
  17. }
  18. glfwMakeContextCurrent(window); //为特定的窗口创建一个上下文
  19. if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { //加载相关的函数地址
  20. std::cout << "GLAD初始化失败,函数地址无法被加载" << std::endl;
  21. return -1;
  22. }
  23. //设置视口,类似于现实生活中的窗户
  24. /*int viewWidth, viewHeight; glfwGetFramebufferSize(window, &viewWidth, &viewHeight); glViewport(0, 0, 800, 600);*/
  25. //初始化我们的着色器
  26. Shader ourShader("../Shader/shader_texture.vs", "../Shader/shader_texture.fs");
  27. //数据部分
  28. static float vertices[] = {
  29. // positions // colors // texture coords
  30. 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top right
  31. 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom right
  32. -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom left
  33. -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // top left
  34. };
  35. unsigned int indices[] = { //由于OpenGL的基础图元只有三种:点、线以及三角形,所以我们这里使用两个三角形来构建一个矩形
  36. 0, 1, 3, // first triangle
  37. 1, 2, 3 // second triangle
  38. };
  39. //GPU部分
  40. unsigned int VBO, VAO, EBO; //这些变量有点类似于我们经常使用的文件描述符,也可以说是“句柄”,主要是用来管理GPU分配的内存
  41. glGenVertexArrays(1, &VAO); //创建一个顶点数组对象名字,类似于声明
  42. glGenBuffers(1, &VBO); //创建一个缓冲区对象名字
  43. glGenBuffers(1, &EBO);
  44. glBindVertexArray(VAO); //绑定顶点数组对象,并为其分配内存空间
  45. glBindBuffer(GL_ARRAY_BUFFER, VBO); //将VBO绑定到一个目标上,不同的目标代表这不同的用途
  46. glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STATIC_DRAW); //将数据传给GL_ARRAY_BUFFER目标上的VBO缓冲对象
  47. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
  48. glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
  49. //指定顶点数据属性不同部分的意义
  50. //位置,第一部分
  51. glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
  52. glEnableVertexAttribArray(0); //启用这一部分属性数组
  53. //颜色,第二部分
  54. glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
  55. glEnableVertexAttribArray(1);
  56. //纹理,第三部分
  57. glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
  58. glEnableVertexAttribArray(2);
  59. //设置纹理,类似于上面缓冲区的设置过程
  60. unsigned int texture;
  61. glGenTextures(1, &texture);
  62. //glActiveTexture(GL_TEXTURE0); //默认是激活的,所以可以不写
  63. glBindTexture(GL_TEXTURE_2D, texture);
  64. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); //设置我们想要的坐标轴S、T
  65. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  66. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //图像分辨率大但是窗口小时,所采用的策略
  67. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  68. //加载图像
  69. int width, height, nrChannels;
  70. unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);
  71. if (data)
  72. {
  73. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); //将图像传递给纹理对象
  74. glGenerateMipmap(GL_TEXTURE_2D); //为当前绑定的纹理自动生成所有需要的多级渐远纹理
  75. }
  76. else
  77. {
  78. std::cout << "Failed to load texture" << std::endl;
  79. }
  80. stbi_image_free(data); //释放加载进来的图像数据内存
  81. //glBindTexture(GL_TEXTURE_2D, 0); //解绑纹理对象
  82. ourShader.use();
  83. while (!glfwWindowShouldClose(window))
  84. {
  85. //渲染
  86. glClearColor(0.2f, 0.3f, 0.3f, 1.0f); //指定了清除缓冲区所用的颜色
  87. glClear(GL_COLOR_BUFFER_BIT);
  88. glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); //使用GL_ELEMENT_ARRAY_BUFFER目标上的对象绘制两个三角形以达到四边形的效果
  89. glfwSwapBuffers(window);
  90. glfwPollEvents(); //检测所有的事件(键盘、鼠标或者一些窗口的状态等)是否被触发
  91. }
  92. glDeleteVertexArrays(1, &VAO);
  93. glDeleteBuffers(1, &VBO);
  94. glDeleteBuffers(1, &EBO);
  95. glfwTerminate();
  96. return 0;
  97. }

着色器部分:

shader_texture.vs

  1. #version 330 core
  2. layout (location = 0) in vec3 aPos;
  3. layout (location = 1) in vec3 aColor;
  4. layout (location = 2) in vec2 aTexCoord;
  5. out vec3 ourColor;
  6. out vec2 TexCoord;
  7. void main()
  8. {
  9. gl_Position = vec4(aPos, 1.0);
  10. ourColor = aColor;
  11. TexCoord = aTexCoord;
  12. }

shader_texture.fs

  1. #version 330 core
  2. out vec4 FragColor;
  3. in vec3 ourColor;
  4. in vec2 TexCoord;
  5. uniform sampler2D ourTexture;
  6. //uniform sampler2D texture1;
  7. //uniform sampler2D texture2;
  8. void main()
  9. {
  10. FragColor = texture(ourTexture, TexCoord);
  11. //FragColor = texture(ourTexture, TexCoord)*vec4(ourColor, 1.0);
  12. //FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2);
  13. }

实现效果:
在这里插入图片描述

在这里要特别注意着色器中ourTexture这个uniform变量,它并没有在CPU端被修改,但是它仍然正常工作了,这是因为OpenGL总是会默认激活一个纹理单元GL_TEXTURE0,这个纹理单元已经与texture纹理对象进行了关联,所以变量ourTexture会默认利用纹理坐标来对texture纹理对象进行颜色的采样。

2.2多个纹理单元与对象

OpenGL中存在大约16个纹理单元,因此其可以同时利用多个纹理对象来绘制一些比较有趣图案。

修改上述纹理部分的代码,如下所示:

  1. //设置纹理,类似于上面缓冲区的设置过程
  2. unsigned int texture1, texture2;
  3. glGenTextures(1, &texture1);
  4. //glActiveTexture(GL_TEXTURE0); //默认是激活的,所以可以不写
  5. glBindTexture(GL_TEXTURE_2D, texture1);
  6. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); //设置我们想要的坐标轴S、T
  7. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  8. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //图像分辨率大但是窗口小时,所采用的策略
  9. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  10. //加载图像
  11. int width, height, nrChannels;
  12. unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);
  13. if (data)
  14. {
  15. std::cout << "container:" << nrChannels << std::endl;
  16. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); //将图像传递给纹理对象
  17. glGenerateMipmap(GL_TEXTURE_2D); //为当前绑定的纹理自动生成所有需要的多级渐远纹理
  18. }
  19. else
  20. {
  21. std::cout << "Failed to load texture" << std::endl;
  22. }
  23. stbi_image_free(data); //释放加载进来的图像数据内存
  24. //glBindTexture(GL_TEXTURE_2D, 0); //解绑纹理对象
  25. //设置第二个纹理对象
  26. glGenTextures(1, &texture2);
  27. glBindTexture(GL_TEXTURE_2D, texture2);
  28. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); //设置我们想要的坐标轴S、T
  29. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  30. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //图像分辨率大但是窗口小时,所采用的策略
  31. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  32. data = stbi_load("awesomeface.png", &width, &height, &nrChannels, 0);
  33. if (data)
  34. {
  35. std::cout << "awesomeface:" << nrChannels<<std::endl;
  36. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); //将图像传递给纹理对象
  37. glGenerateMipmap(GL_TEXTURE_2D); //为当前绑定的纹理自动生成所有需要的多级渐远纹理
  38. }
  39. else
  40. {
  41. std::cout << "Failed to load texture" << std::endl;
  42. }
  43. stbi_image_free(data); //释放加载进来的图像数据内存
  44. //glBindTexture(GL_TEXTURE_2D, 0); //解绑纹理对象
  45. ourShader.use();
  46. glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 0);
  47. ourShader.setInt("texture2", 1);
  48. //绑定相应的纹理对象
  49. glActiveTexture(GL_TEXTURE0);
  50. glBindTexture(GL_TEXTURE_2D, texture1);
  51. glActiveTexture(GL_TEXTURE1);
  52. glBindTexture(GL_TEXTURE_2D, texture2);

运行效果如下所示:
在这里插入图片描述

不过因为纹理坐标的坐标系统与图片的坐标系统有点不一样(y轴不同),导致图片相对于窗口是倒置的,要想解决这个问题我们可以用书中提到的两个小方法:
1、翻转y值,也就是用1减去y值。
2、可以经着色器中的aTexCoord替换为vec2(aTexCoord.x, 1.0f - aTexCoord.y)。
我这里是使用了第一种方法,将顶点数据中的纹理坐标修改为下面的这种形式就正常了:

  1. static float vertices[] = {
  2. // positions // colors // texture coords
  3. 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top right
  4. 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, // bottom right
  5. -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // bottom left
  6. -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f // top left
  7. };

在这里插入图片描述

参考资料:《Learning OpenGL》 《OpenGL编程指南》

发表评论

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

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

相关阅读

    相关 OpenGL纹理置换编程

    在OpenGL中,纹理贴图是一种常用的技术,用于将图像或图案应用到3D模型表面上。然而,有时仅仅使用静态纹理贴图可能会显得单调,无法达到想要的效果。在这种情况下,使用纹理置换技

    相关 openGL纹理

    纹理压缩技术已经广泛应用在各种3D游戏之中,它们包括:DXTC(Direct X Texture Compress,DirectX纹理压缩,以S3TC为基础)、S3TC(S3

    相关 OpenGL纹理贴图

    1.纹理贴图的步骤 1)创建纹理对象,并为它指定一个纹理。 glGenTextures() glGenerateMipmap() 2)确定纹理如何应用到每个像素上 g