OpenGL learning tutorial material

material

 In the real world, each object reacts differently to light. Steel will look shinier than a ceramic vase, and a wooden case will not reflect light as strongly as a steel case. Each object also reacts differently to specular highlights. Some objects do not scatter (Scatter) a lot of light but reflect (Reflect) a lot of light, the result looks like a smaller highlight (Highlight), some objects scatter a lot, they will produce a highlight with a larger radius . If we want to simulate multiple types of objects in OpenGL, we must define Material properties for each object separately.
 In the previous tutorials, we specified an object and a light color to define the object's image output, and combined it with ambient and specular intensity elements. When describing an object, we can use three lighting elements: Ambient Lighting, Diffuse Lighting, and Specular Lighting to define a material color. By assigning a color to each element, we have fine-grained control over the color output of the object. Now add a specular element to these three colors, here are all the material properties we need:

#version 330 core
struct Material
{
    
    
	vec3 ambient;
	vec3 diffuse;
	vec3 specular;
	float shininess;
}
uniform Material material;

 In the fragment shader, we create a structure (Struct) to store the material properties of the object. We could also store them as individual uniform values, but storing them as a struct is more organized. We first define the layout of the struct, then simply declare a uniform variable with the newly created struct as its type.
 As you can see, we define a color vector for each element of the Phong lighting model. The ambient material vector defines what color the object reflects in ambient lighting; usually this is the same color as the object. The diffuse material vector defines the color of the object under diffuse lighting. The diffuse color is set (like the ambient light) to the object color we want. The specular material vector sets the color that the object is affected by specular lighting (or may reflect an object-specific specular highlight color). Finally, shininess affects the scattering/radius of the specular highlight.
 These four elements define the material of an object, through which we can simulate many real-world materials. Here is a list devernay.free.fr showing several material properties that simulate real materials in the outside world. The image below shows the effect of several real-world materials on our cube:
insert image description hereinsert image description here
 As you can see, correctly assigning an object's material properties seems to change the scale of the relevant properties of our object. The effect is obviously eye-catching, but for most realistic effects we end up needing more complex shapes than just a cube. In later tutorials, we'll discuss more complex shapes.
 It is very difficult to give an object the right material, which requires a lot of experimentation and rich experience, so it is a common occurrence that the picture quality of the object is ruined by setting the wrong material.
 Let's try implementing such a material system in a shader.

set material

 We created a uniform material struct in the fragment shader, so next we want to change the lighting calculations to accommodate the new material properties. Since all material elements are stored in the struct, we can get them from the uniform variable material:

void main()
{
    
    
	// 环境光
	vec3 ambient = lightColor * material.ambient;
	
	// 漫反射光
	vec3 norm = normalize(Normal);
	vec3 lightDir = normalize(lightPos - FragPos);
	flaot diff = max(dot(norm, lightDir), 0.0);
	vec3 diffuse = lightColor * (diff * material.diffuse);

	// 镜面高光
	vec3 viewDir = normalize(viewPos - FragPos);
	vec3 reflectDir = reflece(-lightDir, norm);
	float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
	vec3 specular = lightColor * (spec * material.specular);

	vec3 result = ambient + diffuse + specular;
	color = vec4(result, 1.0f);
}

 As you can see, we now get all the properties of the material struct wherever we need them, this time with the help of the material color, to calculate the color of the resulting output. Each material property of the object is multiplied by their corresponding light element.
 By setting the appropriate uniform, we can set the material of the object in the application. A struct in GLSL is not considered special when setting uniform. A struct value acts as a wrapper for uniform variables, so if we wish to populate the struct, we still have to set the uniform value for each element in the struct, but this time prefixed with the struct name:

GLint matAmbientLoc = glGetUniformLocation(lightingShader.Program, "material.ambient");
GLint matDiffuseLoc = glGetUniformLocation(lightingShader.Program, "material.diffuse");
GLint matSpecularLoc = glGetUniformLocation(lightingShader.Program, "material.specular");
GLint matShineLoc = glGetUniformLocation(lightingShader.Program, "material.shininess");

glUniform3f(matAmbientLoc, 1.0f, 0.5f, 0.31f);
glUniform3f(matDiffuseLoc, 1.0f, 0.5f, 0.31f);
glUniform3f(matSpecularLoc, 0.5f, 0.5f, 0.5f);
glUniform1f(matShineLoc, 32.0f);

 l We set the ambient and diffuse elements to the color we want the object to appear, and set the specular element of the object to a medium brightness color; we don't want the specular element to have too strong an impact on the specified object. We also set the shininess to 32. We can now easily affect the material of objects in the application.
 Run the program, you will get the following result:
insert image description here)
 Seems weird, doesn't it?

complete code

 Vertex shader:

#version 330 core
layout( location = 0 ) in vec3 position;
layout( location = 1 ) in vec3 normal;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform mat3 normatr;

out vec3 FragPos;
out vec3 Normal;

void main()
{
    
    
	gl_Position = projection * view * model * vec4(position, 1.0f);
	FragPos = vec3(model * vec4(position, 1.0f));
	Normal  = normatr * normal;
}

 Fragment shader:

#version 330 core
struct Material
{
    
    
	vec3 ambient;
	vec3 diffuse;
	vec3 specular;
	float shininess;
};
uniform Material material;

// 输入顶点位置和法向量
in vec3 FragPos;
in vec3 Normal;

// 输出顶点的颜色
out vec4 color;

// 顶点本身的颜色
uniform vec3 objectColor;
// 光源的颜色和位置
uniform vec3 lightColor;
uniform vec3 lightPos;
// 观察者的位置(世界坐标)
uniform vec3 viewPos;

void main()
{
    
    
    // 环境光
    vec3 ambient = lightColor * material.ambient;

    // 漫反射光
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(lightPos - FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = lightColor * (diff * material.diffuse);

    // 镜面高光
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);  
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    vec3 specular = lightColor * (spec * material.specular);  

    vec3 result = ambient + diffuse + specular;
    color = vec4(result, 1.0f);
}

 Main program:

// 标准输出
#include <string>

// GLEW
#define GLEW_STATIC
#include <GL/glew.h>

// GLFW
#include <GLFW/glfw3.h>

// 着色器类和摄像机类
#include "Shader.h"
#include "Camera.h"

// GLM 数学库
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

// 图片数据读取库
#include <SOIL.h>

// 定义窗口大小
GLuint screenWidth = 800, screenHeight = 600;

// GLFW窗口需要注册的函数
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
// 处理摄像机位置移动的函数
void Do_Movement();

// 定义摄像机
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
// 定义数组存储按键信息
bool keys[1024];
// 
GLfloat lastX = 400, lastY = 300;
bool firstMouse = true;

GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;

glm::vec3 lightPos(1.2f, 1.0f, -0.5f);

// The MAIN function, from here we start our application and run our Game loop
int main()
{
    
    
    // 初始化GLFW 
    glfwInit();
    // 设置glfw使用的OpenGL版本
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    // 设置使用OpenGL的核心模式
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    // 设置窗口大小可改变性 为不可变
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
    // GLFW会自动创建一个每像素4个子采样点的深度和样本缓冲。这也意味着所有缓冲的大小都增长了4倍。
    glfwWindowHint(GLFW_SAMPLES, 4);

    // 创建GLFW的窗口
    GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", nullptr, nullptr);
    // 设置window窗口线程为主线程
    glfwMakeContextCurrent(window);

    // 注册窗口的事件
    glfwSetKeyCallback(window, key_callback);         // 按键检测
    glfwSetCursorPosCallback(window, mouse_callback); // 鼠标移动检测 
    glfwSetScrollCallback(window, scroll_callback);   // 滚轮滑动检测

    // 设置隐藏光标模式
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    // 设置GLEW为更现代的模式
    glewExperimental = GL_TRUE;
    // 初始化GLEW
    glewInit();

    // 设置视口的位置和大小(位置是相对于窗口左下角的)
    glViewport(0, 0, screenWidth, screenHeight);

    // 开启深度测试功能
    glEnable(GL_DEPTH_TEST);

    // 根据顶点着色器和片段着色器位置创建着色器程序
    Shader lightingShader("C:\\Users\\32156\\source\\repos\\LearnOpenGL\\Shader\\vertexShader.txt", "C:\\Users\\32156\\source\\repos\\LearnOpenGL\\Shader\\fragmentShader.txt");
    Shader lampShader("C:\\Users\\32156\\source\\repos\\LearnOpenGL\\Shader\\lightVertexShader.txt", "C:\\Users\\32156\\source\\repos\\LearnOpenGL\\Shader\\lightFragmentShader.txt");

    // 顶点数据(位置+纹理坐标)
    GLfloat vertices[] = {
    
    
    -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
     0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
     0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
     0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
    -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
    -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,

    -0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
     0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
     0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
     0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
    -0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
    -0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,

    -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
    -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
    -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
    -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
    -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
    -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,

     0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
     0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
     0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
     0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
     0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
     0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,

    -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
     0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
     0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
     0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
    -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,

    -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
     0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
     0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
     0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
    -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
    -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f
    };

    // 设置VBO和VAO
    GLuint VBO, VAO;
    // 先申请VAO和VBO的显存
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    // 绑定VAO
    glBindVertexArray(VAO);
    // 绑定VBO
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    // 将顶点数据传输到显存中
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // 位置数据解析
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);
    glBindVertexArray(0); // 解绑VAO

    GLuint lightVAO;
    glGenVertexArrays(1, &lightVAO);
    glBindVertexArray(lightVAO);
    // 只需要绑定VBO不用再次设置VBO的数据,因为容器(物体)的VBO数据中已经包含了正确的立方体顶点数据
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    // 设置灯立方体的顶点属性指针(仅设置灯的顶点数据)
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    glBindVertexArray(0);

    // 游戏循环
    while (!glfwWindowShouldClose(window))
    {
    
    
        // 计算帧相差时间
        GLfloat currentFrame = glfwGetTime();
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        // 窗口的事件检测
        glfwPollEvents();
        // 根据事件移动摄像机
        Do_Movement();

        // 设置清空屏幕颜色缓存所使用颜色
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        // 清空 屏幕颜色缓存、深度缓存
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // 被光源照亮的普通物体的着色器
        lightingShader.Use();

        GLint objectColorLoc = glGetUniformLocation(lightingShader.Program, "objectColor");
        GLint lightColorLoc = glGetUniformLocation(lightingShader.Program, "lightColor");
        GLint lightPosLoc = glGetUniformLocation(lightingShader.Program, "lightPos");
        GLint viewPosLoc = glGetUniformLocation(lightingShader.Program, "viewPos");
        glUniform3f(objectColorLoc, 1.0f, 0.5f, 0.31f);// 我们所熟悉的珊瑚红
        glUniform3f(lightColorLoc, 1.0f, 1.0f, 1.0f); // 依旧把光源设置为白色
        glUniform3f(lightPosLoc, lightPos.x, lightPos.y, lightPos.z);
        glUniform3f(viewPosLoc, camera.Position.x, camera.Position.y, camera.Position.z);

        // 创建观察矩阵( 只要创建矩阵记得初始化= glm::mat4(1.0f) )
        glm::mat4 view = glm::mat4(1.0f);
        // 获取观察矩阵(根据摄像机的状态)
        view = camera.GetViewMatrix();
        // 创建投影矩阵
        glm::mat4 projection = glm::mat4(1.0f);
        // 计算投影矩阵(fov视野为摄像机的属性camera.Zoom)
        projection = glm::perspective(camera.Zoom, (float)screenWidth / (float)screenHeight, 0.1f, 1000.0f);
        // 计算顶点着色器中矩阵的位置值
        GLint modelLoc = glGetUniformLocation(lightingShader.Program, "model");
        GLint viewLoc = glGetUniformLocation(lightingShader.Program, "view");
        GLint projLoc = glGetUniformLocation(lightingShader.Program, "projection");

        // 将观察矩阵和投影矩阵传入对应的位置(记得转换)
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

        // 绑定VAO
        glBindVertexArray(VAO);   
        // 计算模型矩阵
        glm::mat4 model = glm::mat4(1.0f);
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
        // 根据模型矩阵model计算正规矩阵normatr。inverse取逆和transpose转置 函数使用glm对应函数
        GLuint normalLoc = glGetUniformLocation(lightingShader.Program, "normatr");
        glm::mat3 normatr = (glm::mat3)(glm::transpose(glm::inverse(model)));
        // 向物体的顶点着色器传入正规矩阵normatr
        glUniformMatrix3fv(normalLoc, 1, GL_FALSE, glm::value_ptr(normatr));

        glDrawArrays(GL_TRIANGLES, 0, 36);
        glBindVertexArray(0);

		// 设置材质
        GLint matAmbientLoc = glGetUniformLocation(lightingShader.Program, "material.ambient");
        GLint matDiffuseLoc = glGetUniformLocation(lightingShader.Program, "material.diffuse");
        GLint matSpecularLoc = glGetUniformLocation(lightingShader.Program, "material.specular");
        GLint matShineLoc = glGetUniformLocation(lightingShader.Program, "material.shininess");

        glUniform3f(matAmbientLoc, 1.0f, 0.5f, 0.31f);
        glUniform3f(matDiffuseLoc, 1.0f, 0.5f, 0.31f);
        glUniform3f(matSpecularLoc, 0.5f, 0.5f, 0.5f);
        glUniform1f(matShineLoc, 32.0f);

        // 使用光源着色器
        lampShader.Use();
        modelLoc = glGetUniformLocation(lampShader.Program, "model");
        viewLoc = glGetUniformLocation(lampShader.Program, "view");
        projLoc = glGetUniformLocation(lampShader.Program, "projection");

        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

        model = glm::mat4(1.0f);
        GLfloat lightX = sin(glfwGetTime());
        GLfloat lightZ = cos(glfwGetTime());
        GLfloat lightY = 1;
        lightPos = glm::vec3(lightX, lightY, lightZ);
        model = glm::translate(model, lightPos);
        model = glm::scale(model, glm::vec3(0.1f)); // Make it a smaller cube
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
        glBindVertexArray(lightVAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);
        glBindVertexArray(0);

        // 交换前后缓冲区
        glfwSwapBuffers(window);
    }
    // 释放VAO和VBO的显存
    glDeleteVertexArrays(1, &VAO);
    glDeleteVertexArrays(1, &lightVAO);
    glDeleteBuffers(1, &VBO);
    // 释放glfw的内存
    glfwTerminate();
    return 0;
}

// 根据按键信息移动摄像机
void Do_Movement()
{
    
    
    // 如果某个键按下,就执行摄像机对应的方法(更新摄像机的位置)
    if (keys[GLFW_KEY_W])
        camera.ProcessKeyboard(FORWARD, deltaTime);
    if (keys[GLFW_KEY_S])
        camera.ProcessKeyboard(BACKWARD, deltaTime);
    if (keys[GLFW_KEY_A])
        camera.ProcessKeyboard(LEFT, deltaTime);
    if (keys[GLFW_KEY_D])
        camera.ProcessKeyboard(RIGHT, deltaTime);
}

// 按键回调函数
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    
    
    // 如果按下ESE则设置窗口应该关闭
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
    if (key >= 0 && key < 1024)
    {
    
    
        // 如果按下某一个键,则设置其keys为true,如果松开则设置回false
        if (action == GLFW_PRESS)
            keys[key] = true;
        else if (action == GLFW_RELEASE)
            keys[key] = false;
    }
}

// 鼠标移动的回调函数
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
    
    
    // 第一次进窗口鼠标坐标很大,所以第一次调用函数我们需要把它设置为一个正常的值
    if (firstMouse)
    {
    
    
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    // 计算鼠标在竖直方向和水平方向的偏移(屏幕坐标系往右x大,但往下是y大,所以运算顺序不同)
    GLfloat xoffset = xpos - lastX;
    GLfloat yoffset = lastY - ypos;  // Reversed since y-coordinates go from bottom to left

    // 更新鼠标的坐标
    lastX = xpos;
    lastY = ypos;

    // 调用摄像机的鼠标移动函数(根据位置偏移更新摄像机方式)
    camera.ProcessMouseMovement(xoffset, yoffset);
}

// 鼠标滚动的回调函数
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    
    
    // 调用摄像机的鼠标滚动函数(根据位置偏移更新摄像机fov视野大小)
    camera.ProcessMouseScroll(yoffset);
}

properties of light

 This object is too bright. The reason why the object is too bright is that any light source of the three colors of environment, diffuse reflection and specular surface will fully reflect. Light sources have different intensities for ambient, diffuse, and specular elements at the same time. In the previous tutorial, we solved this problem by changing the environment and specular intensity with one intensity value. We want to do the same system, but this time specify an intensity vector for each light element. If we imagine lightColor is vec3(1.0), the code looks like this:

vec3 ambient = vec3(1.0f) * material.ambient;
vec3 diffuse = vec3(1.0f) * (diff * material.diffuse);
vec3 specular = vec3(1.0f) * (spec * material.specular);

 So each material property of the object returns the full intensity of each light element. These vec3(1.0) values ​​can affect each light source independently, which is usually what we want. Now the ambient element of the object fully shows the color of the cube, but the ambient element should not have such a big impact on the final color, so we need to set the ambient brightness of the light to a smaller value to limit the ambient color:

vec3 result = vec3(0.1f) * material.ambient;

 We can affect the intensity of diffuse and specular light sources in the same way. This is very similar to what we did in the previous tutorial; you can say that we have created some light properties to affect each light element independently. We want to create something similar to a material struct for the light's properties:

struct Light
{
    
    
    vec3 position;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
uniform Light light;

 A light source's ambient, diffuse, and specular light all have different brightness. Ambient light is usually set to a relatively low brightness, because we don't want the ambient color to be too conspicuous. The diffuse element of a light source is usually set to the color we want the light to be; often a bright white. Specular elements are usually set to a full intensity glow of type vec3(1.0f). The thing to keep in mind is that we also add the light's position to the struct.
 Just like the material uniform, the fragment shader needs to be updated:

vec3 ambient = light.ambient * material.ambient;
vec3 diffuse = light.diffuse * (diff * material.diffuse);
vec3 specular = light.specular * (spec * material.specular);

 Then we need to set the brightness of the light in the application:

GLint lightAmbientLoc = glGetUniformLocation(lightingShader.Program, "light.ambient");
GLint lightDiffuseLoc = glGetUniformLocation(lightingShader.Program, "light.diffuse");
GLint lightSpecularLoc = glGetUniformLocation(lightingShader.Program, "light.specular");

glUniform3f(lightAmbientLoc, 0.2f, 0.2f, 0.2f);
glUniform3f(lightDiffuseLoc, 0.5f, 0.5f, 0.5f);// 让我们把这个光调暗一点,这样会看起来更自然
glUniform3f(lightSpecularLoc, 1.0f, 1.0f, 1.0f);

 Now that we've adjusted how the light affects all the materials of the object, we get a more visual output like the previous tutorial. This time we have full control over the object lighting and materials :)
insert image description hereNow
 changing the appearance of the object is relatively simple. Let's do something more interesting!

full code

 Fragment shader:

#version 330 core
struct Material
{
    
    
	vec3 ambient;
	vec3 diffuse;
	vec3 specular;
	float shininess;
};
uniform Material material;

struct Light
{
    
    
    vec3 position;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
uniform Light light;

// 输入顶点位置和法向量
in vec3 FragPos;
in vec3 Normal;

// 输出顶点的颜色
out vec4 color;

// 顶点本身的颜色
uniform vec3 objectColor;
// 光源的颜色和位置
uniform vec3 lightColor;
uniform vec3 lightPos;
// 观察者的位置(世界坐标)
uniform vec3 viewPos;

void main()
{
    
    
    // 环境光
    vec3 ambient = light.ambient * material.ambient;

    // 漫反射光
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(lightPos - FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = light.diffuse * (diff * material.diffuse);

    // 镜面高光
    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;
    color = vec4(result, 1.0f);
}

different light source colors

 So far, we have used the color of the light source only to change the intensity of each element of the object (by choosing a color ranging from white to gray to black), and has not affected the actual color of the object (just the intensity). Since the properties of lights are now very easy to access, we can change their color over time to achieve some interesting effects. Since everything is already done in the fragment shader, changing the color of the light is simple and we can immediately create some interesting effects :)
insert image description hereAs
 you can see, the color of the different lights greatly affects the color output of the object. Since the color of light directly affects the color reflected by objects (you may recall this was discussed in the color tutorial), it has a significant effect on the visual output.
 Using sin and glfwGetTime to change the light's ambient and diffuse colors, we can simply change the color of the light source over time:

glm::vec3 lightColor; lightColor.x = sin(glfwGetTime() * 2.0f);
lightColor.y = sin(glfwGetTime() * 0.7f);
lightColor.z = sin(glfwGetTime() * 1.3f);

glm::vec3 diffuseColor = lightColor * glm::vec3(0.5f);
glm::vec3 ambientColor = diffuseColor * glm::vec3(0.2f);

glUniform3f(lightAmbientLoc, ambientColor.x, ambientColor.y, ambientColor.z);
glUniform3f(lightDiffuseLoc, diffuseColor.x, diffuseColor.y, diffuseColor.z);```

practise

 Can you simulate a real world object based on some material properties like we did at the beginning of the tutorial? Note that the color of the ambient light in the material table may be different from the color of the diffuse light, because they do not take the light intensity into account for simulation, you need to change the intensity of the light color to vec(1.0f) to output the correct result:
insert image description here
 Core code:

// 设置材质
        GLint matAmbientLoc = glGetUniformLocation(lightingShader.Program, "material.ambient");
        GLint matDiffuseLoc = glGetUniformLocation(lightingShader.Program, "material.diffuse");
        GLint matSpecularLoc = glGetUniformLocation(lightingShader.Program, "material.specular");
        GLint matShineLoc = glGetUniformLocation(lightingShader.Program, "material.shininess");

        glUniform3f(matAmbientLoc, 0.135f, 0.2225f, 0.1575f);
        glUniform3f(matDiffuseLoc, 0.54f, 0.89f, 0.63f);
        glUniform3f(matSpecularLoc, 0.316228f, 0.316228f, 0.316228f);
        glUniform1f(matShineLoc, 32.0f);

        // 设置光源属性
        GLint lightAmbientLoc = glGetUniformLocation(lightingShader.Program, "light.ambient");
        GLint lightDiffuseLoc = glGetUniformLocation(lightingShader.Program, "light.diffuse");
        GLint lightSpecularLoc = glGetUniformLocation(lightingShader.Program, "light.specular");

        glUniform3f(lightAmbientLoc, 1.0f, 1.0f, 1.0f);
        glUniform3f(lightDiffuseLoc, 1.0f, 1.0f, 1.0f);// 让我们把这个光调暗一点,这样会看起来更自然
        glUniform3f(lightSpecularLoc, 1.0f, 1.0f, 1.0f);

Guess you like

Origin blog.csdn.net/qq_51563654/article/details/130497956