我们需要拓展之前的系统,引入漫反射和镜面光贴图(Map)。这允许我们对物体的漫反射分量和镜面光分量有着更精确的控制。
我们希望通过某种方式对物体的每个片段单独设置漫反射颜色。我们仅仅是对同样的原理使用了不同的名字:其实都是使用一张覆盖物体的图像,让我们能够逐片段索引其独立的颜色值。在光照场景中,它通常叫做一个漫反射贴图(Diffuse Map)(3D艺术家通常都这么叫它),它是一个表现了物体所有的漫反射颜色的纹理图像。

在着色器中使用漫反射贴图的方法和纹理教程中是完全一样的。但这次我们会将纹理储存为Material结构体中的一个sampler2D。我们将之前定义的vec3漫反射颜色向量替换为漫反射贴图。
我们也移除了环境光材质颜色向量,因为环境光颜色在几乎所有情况下都等于漫反射颜色,所以我们不需要将它们分开储存:
- struct Material {
- sampler2D diffuse;
- vec3 specular;
- float shininess;
- };
- ...
- in vec2 TexCoords;
注意我们将在片段着色器中再次需要纹理坐标,所以我们声明一个额外的输入变量。接下来我们只需要从纹理中采样片段的漫反射颜色值即可:
vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
不要忘记将环境光的材质颜色设置为漫反射材质颜色同样的值。
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
就是使用漫反射贴图的全部步骤了。你可以看到,这并不是什么新的东西,但这能够极大地提高视觉品质。为了让它正常工作,我们还需要使用纹理坐标更新顶点数据,将它们作为顶点属性传递到片段着色器,加载材质并绑定材质到合适的纹理单元。
更新后的顶点数据可以在这里找到。顶点数据现在包含了顶点位置、法向量和立方体顶点处的纹理坐标。让我们更新顶点着色器来以顶点属性的形式接受纹理坐标,并将它们传递到片段着色器中:
- #version 330 core
- layout (location = 0) in vec3 aPos;
- layout (location = 1) in vec3 aNormal;
- layout (location = 2) in vec2 aTexCoords;
- ...
- out vec2 TexCoords;
-
- void main()
- {
- ...
- TexCoords = aTexCoords;
- }
记得去更新两个VAO的顶点属性指针来匹配新的顶点数据,并加载箱子图像为一个纹理。在绘制箱子之前,我们希望将要用的纹理单元赋值到material.diffuse这个uniform采样器,并绑定箱子的纹理到这个纹理单元:
- lightingShader.setInt("material.diffuse", 0);
- ...
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, diffuseMap);
使用了漫反射贴图之后,细节再一次得到惊人的提升,这次箱子有了光照开始闪闪发光(字面意思也是)了。箱子看起来可能像这样:
4.1.lighting_maps.vs
- #version 330 core
- layout (location = 0) in vec3 aPos;
- layout (location = 1) in vec3 aNormal;
- layout (location = 2) in vec2 aTexCoords;
-
- out vec3 FragPos;
- out vec3 Normal;
- out vec2 TexCoords;
-
- uniform mat4 model;
- uniform mat4 view;
- uniform mat4 projection;
-
- void main()
- {
- FragPos = vec3(model * vec4(aPos, 1.0));
- Normal = mat3(transpose(inverse(model))) * aNormal;
- TexCoords = aTexCoords;
-
- gl_Position = projection * view * vec4(FragPos, 1.0);
- }
4.1.lighting_maps.fs
- #version 330 core
- out vec4 FragColor;
-
- struct Material {
- sampler2D diffuse;
- vec3 specular;
- float shininess;
- };
-
- struct Light {
- vec3 position;
-
- vec3 ambient;
- vec3 diffuse;
- vec3 specular;
- };
-
- in vec3 FragPos;
- in vec3 Normal;
- in vec2 TexCoords;
-
- uniform vec3 viewPos;
- uniform Material material;
- uniform Light light;
-
- void main()
- {
- // ambient
- vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb;
-
- // diffuse
- vec3 norm = normalize(Normal);
- vec3 lightDir = normalize(light.position - FragPos);
- float diff = max(dot(norm, lightDir), 0.0);
- vec3 diffuse = light.diffuse * diff * texture(material.diffuse, TexCoords).rgb;
-
- // specular
- vec3 viewDir = normalize(viewPos - FragPos);
- vec3 reflectDir = reflect(-lightDir, norm);
- float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
- vec3 specular = light.specular * (spec * material.specular);
-
- vec3 result = ambient + diffuse + specular;
- FragColor = vec4(result, 1.0);
- }
4.1.light_cube.vs
- #version 330 core
- layout (location = 0) in vec3 aPos;
-
- uniform mat4 model;
- uniform mat4 view;
- uniform mat4 projection;
-
- void main()
- {
- gl_Position = projection * view * model * vec4(aPos, 1.0);
- }
4.1.light_cube.fs
- #version 330 core
- out vec4 FragColor;
-
- void main()
- {
- FragColor = vec4(1.0); // set all 4 vector values to 1.0
- }
- #include
- #include
- #include
-
- #include
- #include
- #include
-
- #include
- #include
-
- #include
-
- void framebuffer_size_callback(GLFWwindow* window, int width, int height);
- void mouse_callback(GLFWwindow* window, double xpos, double ypos);
- void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
- void processInput(GLFWwindow *window);
- unsigned int loadTexture(const char *path);
-
- // settings
- const unsigned int SCR_WIDTH = 800;
- const unsigned int SCR_HEIGHT = 600;
-
- // camera
- Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
- float lastX = SCR_WIDTH / 2.0f;
- float lastY = SCR_HEIGHT / 2.0f;
- bool firstMouse = true;
-
- // timing
- float deltaTime = 0.0f;
- float lastFrame = 0.0f;
-
- // lighting
- glm::vec3 lightPos(1.2f, 1.0f, 2.0f);
-
- int main()
- {
- // glfw: initialize and configure
- // ------------------------------
- glfwInit();
- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
- glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
-
- #ifdef __APPLE__
- glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
- #endif
-
- // glfw window creation
- // --------------------
- GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
- if (window == NULL)
- {
- std::cout << "Failed to create GLFW window" << std::endl;
- glfwTerminate();
- return -1;
- }
- glfwMakeContextCurrent(window);
- glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
- glfwSetCursorPosCallback(window, mouse_callback);
- glfwSetScrollCallback(window, scroll_callback);
-
- // tell GLFW to capture our mouse
- glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
-
- // glad: load all OpenGL function pointers
- // ---------------------------------------
- if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
- {
- std::cout << "Failed to initialize GLAD" << std::endl;
- return -1;
- }
-
- // configure global opengl state
- // -----------------------------
- glEnable(GL_DEPTH_TEST);
-
- // build and compile our shader zprogram
- // ------------------------------------
- Shader lightingShader("4.1.lighting_maps.vs", "4.1.lighting_maps.fs");
- Shader lightCubeShader("4.1.light_cube.vs", "4.1.light_cube.fs");
-
- // set up vertex data (and buffer(s)) and configure vertex attributes
- // --------------------------------------------------