OpenGL 入门(九)—Material(材质)和 光照贴图

文章目录

  • 材质
    • 设置材质
    • 光的属性
    • 脚本实现
  • 光照贴图
    • 漫反射贴图
    • 高光反射贴图

材质

材质本质是一个数据集,主要功能就是给渲染器提供数据和光照算法。

如果我们想要在OpenGL中模拟多种类型的物体,我们必须针对每种表面定义不同的材质(Material)属性。

我们可以分别为三个光照分量定义一个材质颜色(Material Color):环境光照(Ambient Lighting)漫反射光照(Diffuse Lighting)镜面光照(Specular Lighting)。通过为每个分量指定一个颜色,我们就能够对表面的颜色输出有细粒度的控制了。

再添加一个反光度(Shininess)分量,设置材质属性:

#version 330 core
struct Material {vec3 ambient;vec3 diffuse;vec3 specular;float shininess;
}; 
//在片段着色器中,我们创建一个结构体(Struct)来储存物体的材质属性。
uniform Material material;

设置材质

可以通过设置适当的uniform来设置应用中物体。

GLSL中一个结构体在设置uniform时并无任何区别,结构体只是充当uniform变量们的一个命名空间。所以如果想填充这个结构体的话,我们必须设置每个单独的uniform,但要以结构体名为前缀:

lightingShader.setVec3("material.ambient",  1.0f, 0.5f, 0.31f);
lightingShader.setVec3("material.diffuse",  1.0f, 0.5f, 0.31f);
lightingShader.setVec3("material.specular", 0.5f, 0.5f, 0.5f);
lightingShader.setFloat("material.shininess", 32.0f);

光的属性

一个光源对它的ambient、diffuse和specular光照分量有着不同的强度。

  • 环境光照通常被设置为一个比较低的强度,因为我们不希望环境光颜色太过主导。
  • 光源的漫反射分量通常被设置为我们希望光所具有的那个颜色,通常是一个比较明亮的白色。
  • 镜面光分量通常会保持为vec3(1.0),以最大强度发光。注意我们也将光源的位置向量加入了结构体。

为光照属性创建类似材质结构体:

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

和材质uniform一样,我们需要更新片段着色器:

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

脚本实现

创建材质的顶点着色器和片元着色器

materials.vs

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;out vec3 FragPos;
out vec3 Normal;uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;void main()
{FragPos = vec3(model * vec4(aPos, 1.0));//在顶点着色器中,我们可以使用inverse和transpose函数自己生成这个法线矩阵,这两个函数对所有类型矩阵都有效Normal = mat3(transpose(inverse(model))) * aNormal;  gl_Position = projection * view * vec4(FragPos, 1.0);
}

materials.fs

#version 330 core
out vec4 FragColor;struct Material {vec3 ambient;vec3 diffuse;vec3 specular;    float shininess;
}; struct Light {vec3 position;vec3 ambient;vec3 diffuse;vec3 specular;
};in vec3 FragPos;  
in vec3 Normal;  uniform vec3 viewPos;
uniform Material material;
uniform Light light;void main()
{// ambientvec3 ambient = light.ambient * material.ambient;// diffuse vec3 norm = normalize(Normal);vec3 lightDir = normalize(light.position - FragPos);float diff = max(dot(norm, lightDir), 0.0);vec3 diffuse = light.diffuse * (diff * material.diffuse);// specularvec3 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);
} 

创建灯光的顶点着色器和片元着色器

light_mrt_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);
}

light_mrt_cube.fs

#version 330 core
out vec4 FragColor;void main()
{FragColor = vec4(1.0); // set all 4 vector values to 1.0
}

在这里插入图片描述

完整源码

#include <glad/glad.h>
#include <GLFW/glfw3.h>#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>// https://learnopengl.com/code_viewer_gh.php?code=includes/learnopengl/shader_m.h
#include <shader_s.h>
// https://learnopengl.com/code_viewer_gh.php?code=includes/learnopengl/camera.h
#include <camera.h>void InitGLFW();
bool CreateWindow();
bool InitGLAD();// 窗口大小改变时调用
void framebuffer_size_callback(GLFWwindow *window, int width, int height);
void mouse_callback(GLFWwindow *window, double xposIn, double yposIn);
void scroll_callback(GLFWwindow *window, double xoffset, double yoffset);
void processInput(GLFWwindow *window);// settings 窗口宽高
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;// 相机
Camera camera(glm::vec3(0.0f, 0.0f, 6.0f));
float lastX = static_cast<float>(SCR_WIDTH) / 2.0;
float lastY = static_cast<float>(SCR_HEIGHT) / 2.0;
bool firstMouse = true;// timing
float deltaTime = 0.0f; // time between current frame and last frame
float lastFrame = 0.0f;// lighting
glm::vec3 lightPos(0.5f, 0.5f, 1.0f);GLFWwindow *window;int main()
{InitGLFW(); // 初始化GLFWbool isCreated = CreateWindow(); // 创建一个窗口对象if (!isCreated)return -1;bool isGLAD = InitGLAD(); // 初始化GLAD,传入加载系统相关opengl函数指针的函数if (!isGLAD)return -1;// 启用深度测试glEnable(GL_DEPTH_TEST);// 构建和编译着色程序Shader lightingShader("shader/P1_Basic/08_LightModel/materials.vs", "shader/P1_Basic/08_LightModel/materials.fs");Shader lightCubeShader("shader/P1_Basic/08_LightModel/light_mrt_cube.vs", "shader/P1_Basic/08_LightModel/light_mrt_cube.fs");// 设置顶点数据(和缓冲区)并配置顶点属性// 1.设置立方体顶点输入  一共需要36个顶点float 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};// 2.设置索引缓冲对象unsigned int VBO, cubeVAO;glGenVertexArrays(1, &cubeVAO);glGenBuffers(1, &VBO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glBindVertexArray(cubeVAO);// position attributeglVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);// normal attributeglVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));glEnableVertexAttribArray(1);// second, configure the light's VAO (VBO stays the same; the vertices are the same for the light object which is also a 3D cube)unsigned int lightCubeVAO;glGenVertexArrays(1, &lightCubeVAO);glBindVertexArray(lightCubeVAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);// note that we update the lamp's position attribute's stride to reflect the updated buffer dataglVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);// 循环渲染while (!glfwWindowShouldClose(window)){// 计算帧间隔时间float currentFrame = static_cast<float>(glfwGetTime());deltaTime = currentFrame - lastFrame;lastFrame = currentFrame;// 输入processInput(window);// 渲染// 清除颜色缓冲glClearColor(0.1f, 0.1f, 0.1f, 1.0f);// 清除深度缓冲glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// be sure to activate shader when setting uniforms/drawing objectslightingShader.use();lightingShader.setVec3("light.position", lightPos);lightingShader.setVec3("viewPos", camera.Position);// light propertiesglm::vec3 lightColor;lightColor.x = static_cast<float>(sin(glfwGetTime() * 2.0));lightColor.y = static_cast<float>(sin(glfwGetTime() * 0.7));lightColor.z = static_cast<float>(sin(glfwGetTime() * 1.3));glm::vec3 diffuseColor = lightColor * glm::vec3(0.5f);   // decrease the influenceglm::vec3 ambientColor = diffuseColor * glm::vec3(0.2f); // low influencelightingShader.setVec3("light.ambient", ambientColor);lightingShader.setVec3("light.diffuse", diffuseColor);lightingShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f);// material propertieslightingShader.setVec3("material.ambient", 1.0f, 0.5f, 0.31f);lightingShader.setVec3("material.diffuse", 1.0f, 0.5f, 0.31f);lightingShader.setVec3("material.specular", 0.5f, 0.5f, 0.5f); // specular lighting doesn't have full effect on this object's materiallightingShader.setFloat("material.shininess", 32.0f);// view/projection transformationsglm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);glm::mat4 view = camera.GetViewMatrix();lightingShader.setMat4("projection", projection);lightingShader.setMat4("view", view);// world transformationglm::mat4 model = glm::mat4(1.2f);lightingShader.setMat4("model", model);// render the cubeglBindVertexArray(cubeVAO);glDrawArrays(GL_TRIANGLES, 0, 36);// also draw the lamp objectlightCubeShader.use();lightCubeShader.setMat4("projection", projection);lightCubeShader.setMat4("view", view);model = glm::mat4(0.6f);model = glm::translate(model, lightPos);model = glm::scale(model, glm::vec3(0.5f)); // a smaller cubelightCubeShader.setMat4("model", model);glBindVertexArray(lightCubeVAO);glDrawArrays(GL_TRIANGLES, 0, 36);// 检查并调用事件,交换缓冲glfwSwapBuffers(window);glfwPollEvents();}// 可选:一旦资源超出其用途,就取消分配所有资源:glDeleteVertexArrays(1, &cubeVAO);glDeleteVertexArrays(1, &lightCubeVAO);glDeleteBuffers(1, &VBO);// 释放/删除之前的分配的所有资源glfwTerminate();return 0;
}void InitGLFW()
{// 初始化GLFWglfwInit();// 配置GLFW  第一个参数代表选项的名称,我们可以从很多以GLFW_开头的枚举值中选择;// 第二个参数接受一个整型,用来设置这个选项的值。glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);// glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
}
bool CreateWindow()
{// 创建一个窗口对象window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;// 创建失败,终止程序glfwTerminate();return false;}// 将我们窗口的上下文设置为当前线程的主上下文glfwMakeContextCurrent(window);// 设置窗口大小改变时的回调函数glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);// 设置鼠标移动回调函数glfwSetCursorPosCallback(window, mouse_callback);// 设置滚轮滚动回调函数glfwSetScrollCallback(window, scroll_callback);// 隐藏并捕捉鼠标// glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);return true;
}
bool InitGLAD()
{// 初始化GLAD,传入加载系统相关opengl函数指针的函数if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;// 初始化失败,终止程序return false;}return true;
}// 窗口大小改变时调用
void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{// 设置窗口的维度glViewport(0, 0, width, height);
}// 输入
void processInput(GLFWwindow *window)
{// 当用户按下esc键,我们设置window窗口的windowShouldClose属性为true// 关闭应用程序if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)camera.ProcessKeyboard(FORWARD, deltaTime);if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)camera.ProcessKeyboard(BACKWARD, deltaTime);if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)camera.ProcessKeyboard(LEFT, deltaTime);if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)camera.ProcessKeyboard(RIGHT, deltaTime);
}// 鼠标移动回调函数
void mouse_callback(GLFWwindow *window, double xposIn, double yposIn)
{// 长按T键,鼠标才能控制相机if (glfwGetKey(window, GLFW_KEY_1) != GLFW_PRESS)return;float xpos = static_cast<float>(xposIn);float ypos = static_cast<float>(yposIn);if (firstMouse){lastX = xpos;lastY = ypos;firstMouse = false;}float xoffset = xpos - lastX;float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to toplastX = xpos;lastY = ypos;camera.ProcessMouseMovement(xoffset, yoffset);
}// 鼠标滚轮滚动回调函数
void scroll_callback(GLFWwindow *window, double xoffset, double yoffset)
{camera.ProcessMouseScroll(static_cast<float>(yoffset));
}

运行效果:
请添加图片描述

光照贴图

纹理与贴图关系

事实上,纹理与贴图原理是一样的,贴图也叫纹理贴图,其实都是使用一张覆盖物体的图像,让我们能够逐片段索引其独立的颜色值。

漫反射贴图

只是在光照场景中,它通常叫做一个漫反射贴图(Diffuse Map)(3D艺术家通常都这么叫它),它是一个表现了物体所有的漫反射颜色的纹理图像。

在着色器中使用漫反射贴图的方法和纹理教程中是完全一样的。

但这次我们会将纹理储存为Material结构体中的一个sampler2D。我们将之前定义的vec3漫反射颜色向量替换为漫反射贴图:

struct Material {sampler2D diffuse;vec3      specular;float     shininess;
}; 
...
in vec2 TexCoords;

注意sampler2D是所谓的不透明类型(Opaque Type),也就是说我们不能将它实例化,只能通过uniform来定义它。如果我们使用除uniform以外的方法(比如函数的参数)实例化这个结构体,GLSL会抛出一些奇怪的错误。这同样也适用于任何封装了不透明类型的结构体。

然后,我们将在片段着色器中再次需要纹理坐标,所以我们声明一个额外的输入变量。接下来我们只需要从纹理中采样片段的漫反射颜色值即可:

vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));

将环境光的材质颜色设置为漫反射材质颜色同样的值:

vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));

脚本实现

创建顶点着色器和片元着色器

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);
}

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()
{// ambientvec3 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;  // specularvec3 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);
} 

完整源码

#include <glad/glad.h>
#include <GLFW/glfw3.h>#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>// https://learnopengl.com/code_viewer_gh.php?code=includes/learnopengl/shader_m.h
#include <shader_s.h>
// https://learnopengl.com/code_viewer_gh.php?code=includes/learnopengl/camera.h
#include <camera.h>
#include <iostream>
void InitGLFW();
bool CreateWindow();
bool InitGLAD();// 窗口大小改变时调用
void framebuffer_size_callback(GLFWwindow *window, int width, int height);
void mouse_callback(GLFWwindow *window, double xposIn, double yposIn);
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(glm::vec3(0.0f, 0.0f, 6.0f));
float lastX = static_cast<float>(SCR_WIDTH) / 2.0;
float lastY = static_cast<float>(SCR_HEIGHT) / 2.0;
bool firstMouse = true;// timing
float deltaTime = 0.0f; // time between current frame and last frame
float lastFrame = 0.0f;// lighting
glm::vec3 lightPos(0.5f, 0.5f, 1.0f);GLFWwindow *window;int main()
{InitGLFW(); // 初始化GLFWbool isCreated = CreateWindow(); // 创建一个窗口对象if (!isCreated)return -1;bool isGLAD = InitGLAD(); // 初始化GLAD,传入加载系统相关opengl函数指针的函数if (!isGLAD)return -1;// 启用深度测试glEnable(GL_DEPTH_TEST);// 构建和编译着色程序Shader lightingShader("shader/P1_Basic/08_LightModel/lighting_maps.vs", "shader/P1_Basic/08_LightModel/lighting_maps.fs");Shader lightCubeShader("shader/P1_Basic/08_LightModel/light_mrt_cube.vs", "shader/P1_Basic/08_LightModel/light_mrt_cube.fs");// 设置顶点数据(和缓冲区)并配置顶点属性// 1.设置立方体顶点输入  一共需要36个顶点float vertices[] = {// positions          // normals           // texture coords-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f,0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,-0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f,-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,-0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f,-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f,0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,-0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f,-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f};// 2.设置索引缓冲对象unsigned int VBO, cubeVAO;glGenVertexArrays(1, &cubeVAO);glGenBuffers(1, &VBO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glBindVertexArray(cubeVAO);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)0);glEnableVertexAttribArray(0);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)(3 * sizeof(float)));glEnableVertexAttribArray(1);glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)(6 * sizeof(float)));glEnableVertexAttribArray(2);// second, configure the light's VAO (VBO stays the same; the vertices are the same for the light object which is also a 3D cube)unsigned int lightCubeVAO;glGenVertexArrays(1, &lightCubeVAO);glBindVertexArray(lightCubeVAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);// note that we update the lamp's position attribute's stride to reflect the updated buffer dataglVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)0);glEnableVertexAttribArray(0);// 加载纹理https://learnopengl-cn.github.io/img/02/04/container2.pngunsigned int diffuseMap = loadTexture("image/04_Textures/container2.png");// shader configuration// --------------------lightingShader.use();lightingShader.setInt("material.diffuse", 0);// 循环渲染while (!glfwWindowShouldClose(window)){// 计算帧间隔时间float currentFrame = static_cast<float>(glfwGetTime());deltaTime = currentFrame - lastFrame;lastFrame = currentFrame;// 输入processInput(window);// 渲染// 清除颜色缓冲glClearColor(0.1f, 0.1f, 0.1f, 1.0f);// 清除深度缓冲glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// be sure to activate shader when setting uniforms/drawing objectslightingShader.use();lightingShader.setVec3("light.position", lightPos);lightingShader.setVec3("viewPos", camera.Position);// light propertieslightingShader.setVec3("light.ambient", 0.2f, 0.2f, 0.2f);lightingShader.setVec3("light.diffuse", 0.5f, 0.5f, 0.5f);lightingShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f);// material propertieslightingShader.setVec3("material.specular", 0.5f, 0.5f, 0.5f);lightingShader.setFloat("material.shininess", 64.0f);// view/projection transformationsglm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);glm::mat4 view = camera.GetViewMatrix();lightingShader.setMat4("projection", projection);lightingShader.setMat4("view", view);// world transformationglm::mat4 model = glm::mat4(1.0f);lightingShader.setMat4("model", model);// bind diffuse mapglActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, diffuseMap);// render the cubeglBindVertexArray(cubeVAO);glDrawArrays(GL_TRIANGLES, 0, 36);// also draw the lamp objectlightCubeShader.use();lightCubeShader.setMat4("projection", projection);lightCubeShader.setMat4("view", view);model = glm::mat4(1.0f);model = glm::translate(model, lightPos);model = glm::scale(model, glm::vec3(0.4f)); // a smaller cubelightCubeShader.setMat4("model", model);glBindVertexArray(lightCubeVAO);glDrawArrays(GL_TRIANGLES, 0, 36);// 检查并调用事件,交换缓冲glfwSwapBuffers(window);glfwPollEvents();}// 可选:一旦资源超出其用途,就取消分配所有资源:glDeleteVertexArrays(1, &cubeVAO);glDeleteVertexArrays(1, &lightCubeVAO);glDeleteBuffers(1, &VBO);// 释放/删除之前的分配的所有资源glfwTerminate();return 0;
}void InitGLFW()
{// 初始化GLFWglfwInit();// 配置GLFW  第一个参数代表选项的名称,我们可以从很多以GLFW_开头的枚举值中选择;// 第二个参数接受一个整型,用来设置这个选项的值。glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);// glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
}
bool CreateWindow()
{// 创建一个窗口对象window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;// 创建失败,终止程序glfwTerminate();return false;}// 将我们窗口的上下文设置为当前线程的主上下文glfwMakeContextCurrent(window);// 设置窗口大小改变时的回调函数glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);// 设置鼠标移动回调函数glfwSetCursorPosCallback(window, mouse_callback);// 设置滚轮滚动回调函数glfwSetScrollCallback(window, scroll_callback);// 隐藏并捕捉鼠标// glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);return true;
}
bool InitGLAD()
{// 初始化GLAD,传入加载系统相关opengl函数指针的函数if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;// 初始化失败,终止程序return false;}return true;
}// 窗口大小改变时调用
void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{// 设置窗口的维度glViewport(0, 0, width, height);
}// 输入
void processInput(GLFWwindow *window)
{// 当用户按下esc键,我们设置window窗口的windowShouldClose属性为true// 关闭应用程序if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)camera.ProcessKeyboard(FORWARD, deltaTime);if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)camera.ProcessKeyboard(BACKWARD, deltaTime);if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)camera.ProcessKeyboard(LEFT, deltaTime);if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)camera.ProcessKeyboard(RIGHT, deltaTime);
}// 鼠标移动回调函数
void mouse_callback(GLFWwindow *window, double xposIn, double yposIn)
{// 长按T键,鼠标才能控制相机if (glfwGetKey(window, GLFW_KEY_1) != GLFW_PRESS)return;float xpos = static_cast<float>(xposIn);float ypos = static_cast<float>(yposIn);if (firstMouse){lastX = xpos;lastY = ypos;firstMouse = false;}float xoffset = xpos - lastX;float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to toplastX = xpos;lastY = ypos;camera.ProcessMouseMovement(xoffset, yoffset);
}// 鼠标滚轮滚动回调函数
void scroll_callback(GLFWwindow *window, double xoffset, double yoffset)
{camera.ProcessMouseScroll(static_cast<float>(yoffset));
}
unsigned int loadTexture(char const *path)
{unsigned int textureID;glGenTextures(1, &textureID);int width, height, nrComponents;unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0);if (data){GLenum format;if (nrComponents == 1)format = GL_RED;else if (nrComponents == 3)format = GL_RGB;else if (nrComponents == 4)format = GL_RGBA;glBindTexture(GL_TEXTURE_2D, textureID);glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);glGenerateMipmap(GL_TEXTURE_2D);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);stbi_image_free(data);}else{std::cout << "Texture failed to load at path: " << path << std::endl;stbi_image_free(data);}return textureID;
}

运行效果 :

在这里插入图片描述

高光反射贴图

我们同样可以使用一个专门用于镜面高光的纹理贴图。这也就意味着我们需要生成一个黑白的(如果你想得话也可以是彩色的)纹理,来定义物体每部分的镜面光强度。

镜面高光的强度可以通过图像每个像素的亮度来获取。镜面光贴图上的每个像素都可以由一个颜色向量来表示,比如说黑色代表颜色向量vec3(0.0),灰色代表颜色向量vec3(0.5)。

在片段着色器中,我们接下来会取样对应的颜色值并将它乘以光源的镜面强度。一个像素越「白」,乘积就会越大,物体的镜面光分量就会越亮。

从实际角度来说,木头其实也有镜面高光,尽管它的反光度(Shininess)很小(更多的光被散射),影响也比较小,但是为了教学目的,我们可以假设木头不会对镜面光有任何反应。

采样镜面光贴图

创建片段着色器的材质属性,让其接受一个sampler2D而不是vec3作为镜面光分量:

struct Material {sampler2D diffuse;sampler2D specular;float     shininess;
};

然后,通过采样镜面光贴图,来获取片段所对应的镜面光强度:

vec3 ambient  = light.ambient  * vec3(texture(material.diffuse, TexCoords));
vec3 diffuse  = light.diffuse  * diff * vec3(texture(material.diffuse, TexCoords));  
vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
FragColor = vec4(ambient + diffuse + specular, 1.0);

脚本实现

我们只创建片元着色器,顶点着色器和上面漫反射一样的,源码也类似

源码

 ...// 加载纹理// https://learnopengl-cn.github.io/img/02/04/container2.pngunsigned int diffuseMap = loadTexture("image/04_Textures/container2.png");// https://learnopengl-cn.github.io/img/02/04/container2_specular.pngunsigned int specularMap = loadTexture("image/04_Textures/container2_specular.png");...// 循环渲染while (!glfwWindowShouldClose(window)){...// bind diffuse mapglActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, diffuseMap);// bind specular mapglActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D, specularMap);...
}

lighting_specular_maps.fs

#version 330 core
out vec4 FragColor;struct Material {sampler2D diffuse;sampler2D 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()
{// ambientvec3 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;  // specularvec3 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 * texture(material.specular, TexCoords).rgb;  vec3 result = ambient + diffuse + specular;FragColor = vec4(result, 1.0);
} 

运行效果:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/669500.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【实训】自动运维ansible实训(网络管理与维护综合实训)

来自即将退役学长的分享&#xff0c;祝学弟学妹以后发大财&#xff01; 一 实训目的及意义 1.1 实训目的 1、熟悉自动化运维工具&#xff1a;实训旨在让学员熟悉 Ansible 这一自动化运维工具。通过实际操作&#xff0c;学员可以了解 Ansible 的基本概念、工作原理和使用方法…

Obsidian使用ddnsto穿透nas的webdav功能实现跨平台同步

之前一直用坚果云的webdav功能做obsidian的跨平台同步&#xff08;Windows&#xff0c;Ubuntu&#xff0c;iOS&#xff09;&#xff0c;但是今天在新的工作机上部署obsidian时&#xff0c;发现一次同步的文件数量超过了坚果云的限制&#xff08;付费用户好像是500次&#xff09…

springboot154基于Spring Boot智能无人仓库管理

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…

2,cdc放缩位图

类似地&#xff0c;用pDC->StretchBlt来缩放&#xff0c;只是加上了两个参数&#xff0c;原始位图的宽高。 void CMy1_showbitmapView::StretchBitMap(CDC * pDC) { //CBitmap对象 CBitmap bitmap; //CDC对象 CDC dcMemory; //加载资源 bitmap.LoadBitmapW(IDB_BITMAP1); /…

基于单片机的智能寻光小车设计

摘 要&#xff1a;随着物联网技术的飞速发展和逐渐成熟&#xff0c;以单片机为主的智能小车在巡查、仓储、探险及国防等领域得到广泛应用。本文设计了一种基于单片机的智能寻光小车&#xff0c;该小车以STC89C52RC 芯片为设计核心&#xff0c;结合光敏传感器和超声波传感器等多…

leetcode(双指针)283.移动零(C++)DAY3

文章目录 1.题目示例提示 2.解答思路3.实现代码结果 4.总结 1.题目 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 示例 示例 1: 输入…

浅析软件测试中的一些常见理论:杀虫剂效应、金字塔模型、缺陷集群性原则、软件测试活动依赖于软件测试背景、软件测试的7大基本原则

这篇文章我主要想记录学习一下在软件测试行业中的一些常见理论效应以做基本了解。 一、杀虫剂效应 1、杀虫剂效应介绍 杀虫剂效应原本指农业中随着农药的普及使用&#xff0c;害虫对农药的抗药性就越来越强&#xff0c;农药就越来越难杀死害虫。在农场里为了对付破坏农作物的…

《爬虫职海录》卷二 • 爬在广州

HI&#xff0c;朋友们好&#xff0c;「爬虫职海录」第二期更新啦&#xff01; 本栏目的内容方向会以爬虫相关的“岗位分析”和“职场访谈”为主&#xff0c;方便大家了解一下当下的市场行情。 本栏目持续更新&#xff0c;暂定收集国内主要城市的爬虫岗位相关招聘信息&#xf…

SpringBoot注解--02---常用注解汇总

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 1.SpringBoot 配置启动注解SpringBootApplication 2.Bean处理注解2.1 依赖注入AutoWired、Qualifier、Resource 2.2 类被 Spring 容器创建&#xff0c;管理 iocComp…

我在代码随想录|写代码Day26 |回溯算法|332. 重新安排行程 , 51. N皇后 , 37. 解数独

学习目标&#xff1a; 博主介绍: 27dCnc 专题 : 数据结构帮助小白快速入门 &#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d; ☆*: .&#xff61;. o(≧▽≦)…

C++杂选

#include <iostream> #include <regex>using namespace std;int main() { //它声明了一个 string 类型的变量 input&#xff0c;用于存储输入的字符串。然后使用 getline() 函数从标准输入中读取一行输入&#xff0c;并将其存储在 input 变量中。string input;getl…

蓝桥杯Web应用开发-盒模型

盒模型 专栏持续更新中 盒子模型就是用来装 HTML 元素的盒子&#xff0c;它用于描述一个装有 HTML 元素的矩形盒子。该模型包括边框&#xff08;border&#xff09;、内边距&#xff08;padding&#xff09;、内容&#xff08;content&#xff09;、外边距&#xff08;margin…

《PCI Express体系结构导读》随记 —— 第II篇 第4章 PCIe总线概述(7)

接前一篇文章&#xff1a;《PCI Express体系结构导读》随记 —— 第II篇 第4章 PCIe总线概述&#xff08;6&#xff09; 4.1 PCIe总线的基础知识 与PCI总线不同&#xff0c;PCIe总线使用端到端的连接方式&#xff0c;在一条PCIe链路的两端只能各连接一个设备&#xff0c;这两个…

Springboot多种方法处理静态资源:设置并访问静态资源目录

&#xff5e;目录嗷&#xff5e; 静态文件application设置方法 配置详解编写配置优缺点 设置配置类方法 配置详解编写配置优缺点 总结 作者&#xff1a;Mintimate 博客&#xff1a;https://www.mintimate.cn Mintimate’s Blog&#xff0c;只为与你分享 静态文件 静态资源&…

DevOps落地笔记-15|混沌工程:通过问题注入提高系统可靠性

上一课时介绍了通过搭建一套部署流水线&#xff0c;高效、可靠的将软件部署到测试环境以及生产环境。到目前为止&#xff0c;我们学习了从用户需求到软件部署到生产环境交付给用户的全过程。随着软件工程不断发展&#xff0c;近几年&#xff0c;出现了一种新的实践&#xff0c;…

day35 柠檬水找零 根据身高重建队列 用最少数量的箭引爆气球

题目1&#xff1a;860 柠檬水找零 题目链接&#xff1a;860 柠檬水找零 题意 一杯柠檬水5美元&#xff0c;每位顾客只买一杯柠檬水&#xff0c;支付5美玉&#xff0c;10美元&#xff0c;20美元&#xff0c;必须正确找零 开始时并没有零钱 若可以正确找零&#xff0c;则返回…

C#使用RabbitMQ-5_主题模式(主题交换机)

简介 主题模式允许发送者根据主题发布消息&#xff0c;而订阅者可以订阅特定的主题。 在主题模式中&#xff0c;生产者发送的消息被发送到一个交换机&#xff08;Exchange&#xff09;&#xff0c;该交换机根据消息的路由键&#xff08;Routing Key&#xff09;和绑定&#x…

Linux系统安全①iptables防火墙

目录 一.iptables防火墙概述 1.netfilter与iptables &#xff08;1&#xff09;netfilter &#xff08;2&#xff09;iptables 2.iptables防火墙默认规则表、链结构 二.iptables四表五链 1.四表 2.五链 3.总结 三.iptables的配置 1.安装 2.配置方法 &#xff08;1…

介绍一个关于 JSON 可视化的网站

最近在看到一个比较好玩的网站&#xff0c;可以将 JSON以可视化的方式展现出现&#xff0c;比如存在一下JSON数据&#xff1a; {"id": "f3bbc3bc-9f34-4bf7-8a0f-7e6f6e6fbb9a","isActive": false,"age": 25,"name": "…

【Qt5小项目】接金币小游戏

代码量在250行左右&#xff0c; 需要源码的可以私信我。