当前位置: 首页 > news >正文

跟着LearnOpenGL学习11--材质

文章目录

  • 一、材质
  • 二、设置材质
  • 三、光的属性
  • 四、不同的光源颜色

一、材质

在现实世界里,每个物体会对光产生不同的反应。

比如,钢制物体看起来通常会比陶土花瓶更闪闪发光,一个木头箱子也不会与一个钢制箱子反射同样程度的光。

有些物体反射光的时候不会有太多的散射(Scatter),因而产生较小的高光点,而有些物体则会散射很多,产生一个有着更大半径的高光点。

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

在跟着LearnOpenGL学习10–基础光照这一篇中,我们定义了一个物体和光的颜色,并结合环境光与镜面强度分量,来决定物体的视觉输出。

当描述一个表面时,我们可以分别为三个光照分量定义一个材质颜色(Material Color):

  • 环境光照(Ambient Lighting)
  • 漫反射光照(Diffuse Lighting)
  • 镜面光照(Specular Lighting)

通过为每个分量指定一个颜色,我们就能够对表面的颜色输出有细粒度的控制了。

现在,我们再添加一个反光度(Shininess)分量,结合上述的三个颜色,我们就有了全部所需的材质属性了:

片段着色器

struct Material { //材质描述结构体vec3 ambient;       //环境光照vec3 diffuse;       //漫反射光照vec3 specular;      //镜面反射光照float shininess;    //反光度
};uniform Material material;  //材质

在片段着色器中,我们创建一个结构体(Struct)来储存物体的材质属性。
我们也可以把它们储存为独立的uniform值,但是作为一个结构体来储存会更有条理一些。
我们首先定义结构体的布局(Layout),然后简单地以刚创建的结构体作为类型声明一个uniform变量。

如你所见,我们为冯氏光照模型的每个分量都定义一个颜色向量:

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

有这4个元素定义一个物体的材质,我们能够模拟很多现实世界中的材质。如下表格展示了一系列材质属性,它们模拟了现实世界中的真实材质。下图展示了几组现实世界的材质参数值对我们的立方体的影响:

材质表格

名字环境光照漫反射光照镜面反射光照反射强度
emerald(翡翠)0.0215, 0.1745, 0.02150.07568, 0.61424, 0.075680.633, 0.727811, 0.6330.6
jade(玉)0.135, 0.2225, 0.15750.54, 0.89, 0.630.316228, 0.316228, 0.3162280.1
obsidian(黑曜石)0.05375, 0.05, 0.066250.18275, 0.17, 0.225250.332741, 0.328634, 0.3464350.3
pearl(珍珠)0.25, 0.20725, 0.207251, 0.829, 0.8290.296648, 0.296648, 0.2966480.088
ruby(红宝石)0.1745, 0.01175, 0.011750.61424, 0.04136, 0.041360.727811, 0.626959, 0.6269590.6
turquoise()绿松石0.1, 0.18725, 0.17450.396, 0.74151, 0.691020.297254, 0.30829, 0.3066780.1
brass(黄铜)0.329412, 0.223529, 0.0274510.780392, 0.568627, 0.1137250.992157, 0.941176, 0.8078430.21794872
bronze(青铜)0.2125, 0.1275, 0.0540.714, 0.4284, 0.181440.393548, 0.271906, 0.1667210.2
chrome(铬)0.25, 0.25, 0.250.4, 0.4, 0.40.774597, 0.774597, 0.7745970.6
copper(铜)0.19125, 0.0735, 0.02250.7038, 0.27048, 0.08280.256777, 0.137622, 0.0860140.1
gold(黄金)0.24725, 0.1995, 0.07450.75164, 0.60648,0.226480.628281, 0.555802, 0.3660650.4
silver(银)0.19225, 0.19225, 0.192250.50754, 0.50754, 0.507540.508273, 0.508273, 0.5082730.4
black plastic(黑色塑料)0.0, 0.0, 0.00.01, 0.01, 0.010.50, 0.50, 0.500.25
cyan plastic(青色塑料)0.0, 0.1, 0.060.0, 0.50980392, 0.509803920.50196078, 0.50196078, 0.501960780.25
green plastic(绿色塑料)0.0, 0.0, 0.00.1, 0.35, 0.10.45, 0.55, 0.450.25
red plastic(红色塑料)0.0, 0.0, 0.00.5, 0.0, 0.00.7, 0.6, 0.60.25
white plastic(白色塑料)0.0, 0.0, 0.00.55, 0.55, 0.550.70, 0.70, 0.700.25
yellow plastic(黄色塑料)0.0, 0.0, 0.00.5, 0.5, 0.00.60, 0.60, 0.500.25
black rubber(黑色橡胶)0.02, 0.02, 0.020.01, 0.01, 0.010.4, 0.4, 0.40.78125
cyan rubber(青色橡胶)0.0, 0.05, 0.050.4, 0.5, 0.50.04, 0.7, 0.70.78125
green rubber(绿色橡胶)0.0, 0.05, 0.00.4, 0.5, 0.40.04, 0.7, 0.040.78125
red rubber(红色橡胶)0.05, 0.0, 0.00.5, 0.4, 0.40.7, 0.04, 0.040.78125
white rubber(白色橡胶)0.05, 0.05, 0.050.5, 0.5, 0.50.7, 0.7, 0.70.78125
yellow rubber(黄色橡胶)0.05, 0.05, 0.00.5, 0.5, 0.40.7, 0.7, 0.040.78125

在这里插入图片描述
可以看到,通过正确地指定一个物体的材质属性,我们对这个物体的感知也就不同了。效果非常明显,但是要想获得更真实的效果,我们需要以更复杂的形状替换这个立方体。

搞清楚一个物体正确的材质设定是个困难的工程,这主要需要实验和丰富的经验。用了不合适的材质而毁了物体的视觉质量是件经常发生的事。

让我们试着在着色器中实现这样的一个材质系统。


二、设置材质

我们在片段着色器中创建了一个材质结构体的uniform,所以下面我们希望修改一下光照的计算来遵从新的材质属性。由于所有材质变量都储存在一个结构体中,我们可以从uniform变量material中访问它们:

片段着色器

#version 330 corein vec3 FragPos;
in vec3 Normal;out vec4 FragColor;uniform vec3 objectColor;
uniform vec3 lightColor;
uniform vec3 lightPos;
uniform vec3 viewPos;   //观察者坐标struct Material { //材质描述结构体vec3 ambient;       //环境光照vec3 diffuse;       //漫反射光照vec3 specular;      //镜面反射光照float shininess;    //反光度
};uniform Material material;  //材质void main()
{//环境光照vec3 ambient = material.ambient * lightColor;//漫反射光照vec3 norm = normalize(Normal);vec3 lightDir = normalize(lightColor - FragPos);float diff = max(dot(norm, lightDir), 0.0);vec3 diffuse = (diff *material.diffuse) * lightColor;//镜面反射光照vec3 viewDir = normalize(viewPos - FragPos);vec3 reflectDir = reflect(-lightDir, norm);float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);vec3 specular = (material.specular * spec) * lightColor;vec3 result = ambient + diffuse + specular;FragColor = vec4(result, 1.0);
}

可以看到,我们现在在需要的地方访问了材质结构体中的所有属性,并且这次是根据材质的颜色来计算最终的输出颜色的。物体的每个材质属性都乘上了它们各自对应的光照分量。

我们现在可以通过设置适当的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);

我们将环境光和漫反射分量设置成我们想要让物体所拥有的颜色,而将镜面分量设置为一个中等亮度的颜色,我们不希望镜面分量过于强烈。我们仍将反光度保持为32。

现在我们能够轻松地在应用中影响物体的材质了。运行程序,你会得到像这样的结果:
在这里插入图片描述


全部代码

main.cpp

#include "mainwindow.h"
#include <QApplication>//在包含GLFW的头文件之前包含了GLAD的头文件;
//GLAD的头文件包含了正确的OpenGL头文件(例如GL/gl.h);
//所以需要在其它依赖于OpenGL的头文件之前包含GLAD;
#include <glad/glad.h>
#include <GLFW/glfw3.h>//GLM
//#include <glm/glm.hpp>
//#include <glm/gtc/matrix_transform.hpp>
//#include <glm/gtc/type_ptr.hpp>#include <iostream>
#include <QDebug>#include "shader.h"
#include "stb_image.h"
#include "camera.h"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 = 1920;
const unsigned int SCR_HEIGHT = 1080;//Camera
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
float lastX =  SCR_WIDTH / 2.0;
float lastY =  SCR_HEIGHT / 2.0;
bool firstMouse = true;float deltaTime = 0.0f; // 当前帧与上一帧的时间差
float lastFrame = 0.0f; // 上一帧的时间//Light
glm::vec3 lightPos(1.2f, 1.0f, 2.0f);using namespace std;int main(int argc, char *argv[])
{QApplication a(argc, argv);//MainWindow w;//w.show();//初始化GLFW//--------------------glfwInit();//配置GLFW//--------------------//告诉GLFW使用的OpenGL本是3.3glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//告诉GLFW使用的是核心模式(Core-profile)glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//创建一个新的OpenGL环境和窗口//-----------------------------------GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();    //glfw销毁窗口和OpenGL环境,并释放资源return -1;}//设置参数window中的窗口所关联的OpenGL环境为当前环境//-----------------------------------glfwMakeContextCurrent(window);//设置窗口尺寸改变大小时的回调函数(窗口尺寸发送改变时会自动调用)//-----------------------------------glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);//设置鼠标事件的回调函数(鼠标移动时会自动调用)//-----------------------------------glfwSetCursorPosCallback(window, mouse_callback);//设置鼠标滚轮事件的回调函数(鼠标滚轮移动时会自动调用)//-----------------------------------glfwSetScrollCallback(window, scroll_callback);//告诉GLFW捕捉鼠标glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);//glad加载系统相关的OpenGL函数指针//---------------------------------------if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;return -1;}//开启深度测试glEnable(GL_DEPTH_TEST);Shader objectShader("C:/Qt_Pro/OpenGL_GLFW/shader/shader.vs","C:/Qt_Pro/OpenGL_GLFW/shader/shader.fs");Shader lightShader("C:/Qt_Pro/OpenGL_GLFW/shader/light_cube.vs","C:/Qt_Pro/OpenGL_GLFW/shader/light_cube.fs");//顶点数据//---------------------------------------------------------------------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};//物体//----------------------------------------------------------------unsigned int VBO, objectVAO;glGenVertexArrays(1, &objectVAO);     //创建顶点数组对象glGenBuffers(1, &VBO);          //创建顶点缓冲对象glBindBuffer(GL_ARRAY_BUFFER, VBO);     //将VBO与GL_ARRAY_BUFFER缓冲区绑定glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);  //将顶点数据复制到GL_ARRAY_BUFFER缓冲区,之后可通过VBO进行操作glBindVertexArray(objectVAO);         //绑定VAO//设定顶点属性指针//位置属性glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);//法向量属性glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));glEnableVertexAttribArray(1);//光源(VBO用上面的)//----------------------------------------------------------------unsigned int lightVAO;glGenVertexArrays(1, &lightVAO);     //创建顶点数组对象glBindVertexArray(lightVAO);         //绑定VAOglBindBuffer(GL_ARRAY_BUFFER, VBO);     //将VBO与GL_ARRAY_BUFFER缓冲区绑定glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);//渲染循环//我们可不希望只绘制一个图像之后我们的应用程序就立即退出并关闭窗口;//我们希望程序在我们主动关闭它之前不断绘制图像并能够接受用户输入;//因此,我们需要在程序中添加一个while循环,它能在我们让GLFW退出前一直保持运行;//------------------------------------------------------------------------------while (!glfwWindowShouldClose(window))  //如果用户准备关闭参数window所指定的窗口,那么此接口将会返回GL_TRUE,否则将会返回GL_FALSE{//更新时间差float currentFrame = static_cast<float>(glfwGetTime());deltaTime = currentFrame - lastFrame;lastFrame = currentFrame;//用户输入//------------------------------------------------------------------------------processInput(window);   //检测是否有输入//渲染指令//------------------------------------------------------------------------------glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//被投光物体//============================================================//激活着色器程序对象objectShader.use();objectShader.setVec3("objectColor", 1.0f, 0.5f, 0.31f);objectShader.setVec3("lightColor", 1.0f, 1.0f, 1.0f);objectShader.setVec3("lightPos", lightPos);objectShader.setVec3("viewPos", camera.Position);objectShader.setVec3("material.ambient",  1.0f, 0.5f, 0.31f);objectShader.setVec3("material.diffuse",  1.0f, 0.5f, 0.31f);objectShader.setVec3("material.specular", 0.5f, 0.5f, 0.5f);objectShader.setFloat("material.shininess", 32.0f);//创建变换矩阵glm::mat4 model = glm::mat4(1.0f);objectShader.setMat4("model", model);glm::mat4 view = camera.GetViewMatrix();objectShader.setMat4("view", view);glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);objectShader.setMat4("projection", projection);//绘制三角形glBindVertexArray(objectVAO);             //绑定VAOglDrawArrays(GL_TRIANGLES, 0, 36);//============================================================//光源//============================================================//激活着色器程序对象lightShader.use();lightShader.setMat4("view", view);lightShader.setMat4("projection", projection);model = glm::mat4(1.0f);model = glm::translate(model, lightPos);model = glm::scale(model, glm::vec3(0.2f));lightShader.setMat4("model", model);//绘制三角形glBindVertexArray(lightVAO);             //绑定VAOglDrawArrays(GL_TRIANGLES, 0, 36);//============================================================//告诉GLFW检查所有等待处理的事件和消息,包括操作系统和窗口系统中应当处理的消息。如果有消息正在等待,它会先处理这些消息再返回;否则该函数会立即返回//---------------------------------------------------------------------------------------------------------------------------------glfwPollEvents();//请求窗口系统将参数window关联的后缓存画面呈现给用户(双缓冲绘图)//------------------------------------------------------------------------------glfwSwapBuffers(window);}//释放资源glDeleteVertexArrays(1, &objectVAO);glDeleteVertexArrays(1, &lightVAO);glDeleteBuffers(1, &VBO);//glDeleteProgram(objectShader);//glDeleteProgram(lightShader);//glfw销毁窗口和OpenGL环境,并释放资源(之后必须再次调用glfwInit()才能使用大多数GLFW函数)//------------------------------------------------------------------glfwTerminate();return a.exec();
}//检测是否有输入
//---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)   //ESC键,退出glfwSetWindowShouldClose(window, true);float cameraSpeed = static_cast<float>(2.5 * deltaTime);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);
}//给glfw窗口注册的尺寸改变回调函数
//---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{//确保视口匹配新的窗口尺寸,请注意:宽度和高度将比视网膜显示器上指定的大得多glViewport(0, 0, width, height);
}
// 鼠标移动时的回调函数
// -------------------------------------------------------
void mouse_callback(GLFWwindow* window, double xposIn, double yposIn)
{float xpos = static_cast<float>(xposIn);float ypos = static_cast<float>(yposIn);if (firstMouse){lastX = xpos;lastY = ypos;firstMouse = false;}float xoffset = lastX - xpos;float yoffset = ypos - lastY;   //翻转,因为Y轴是从下到上越来越大lastX = xpos;lastY = ypos;camera.ProcessMouseMovement(xoffset, yoffset);
}//鼠标滚轮的回调函数
// ----------------------------------------------------------------------
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{camera.ProcessMouseScroll(static_cast<float>(yoffset));
}

三、光的属性

不过看起来真的不太对劲?这个物体太亮了。

物体过亮的原因是环境光、漫反射和镜面光这三个颜色对任何一个光源都全力反射。

光源对环境光、漫反射和镜面光分量也分别具有不同的强度。前面的章节中,我们通过使用一个强度值改变环境光和镜面光强度的方式解决了这个问题。我们想做类似的事情,但是这次是要为每个光照分量分别指定一个强度向量。

如果我们假设lightColor是vec3(1.0),代码会看起来像这样:

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

所以物体的每个材质属性对每一个光照分量都返回了最大的强度。

对单个光源来说,这些vec3(1.0)值同样可以对每种光源分别改变,而这通常就是我们想要的。

现在,物体的环境光分量完全地影响了立方体的颜色,可是环境光分量实际上不应该对最终的颜色有这么大的影响,所以我们会将光源的环境光强度设置为一个小一点的值,从而限制环境光颜色:

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

我们可以用同样的方式影响光源的漫反射和镜面光强度。这和我们在上一节中所做的极为相似,你可以认为我们已经创建了一些光照属性来影响各个光照分量。我们希望为光照属性创建类似材质结构体的东西:

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

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

片段着色器

#version 330 corein vec3 FragPos;
in vec3 Normal;out vec4 FragColor;uniform vec3 objectColor;
uniform vec3 lightColor;
uniform vec3 lightPos;
uniform vec3 viewPos;   //观察者坐标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;void main()
{//环境光照vec3 ambient = material.ambient * light.ambient;//漫反射光照vec3 norm = normalize(Normal);vec3 lightDir = normalize(lightColor - FragPos);float diff = max(dot(norm, lightDir), 0.0);vec3 diffuse = (diff *material.diffuse) * light.diffuse;//镜面反射光照vec3 viewDir = normalize(viewPos - FragPos);vec3 reflectDir = reflect(-lightDir, norm);float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);vec3 specular = (material.specular * spec) * light.specular;vec3 result = ambient + diffuse + specular;FragColor = vec4(result, 1.0);
}

我们接下来在应用中设置光照强度:

objectShader.setVec3("light.ambient",  0.2f, 0.2f, 0.2f);
objectShader.setVec3("light.diffuse",  0.5f, 0.5f, 0.5f); // 将光照调暗了一些以搭配场景
objectShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f);

现在我们已经调整了光照对物体材质的影响,我们得到了一个与上一节很相似的视觉效果。但这次我们有了对光照和物体材质的完全掌控:

在这里插入图片描述
在这里插入图片描述


四、不同的光源颜色

到目前为止,我们都只对光源设置了从白到灰到黑范围内的颜色,这样只会改变物体各个分量的强度,而不是它的真正颜色。

由于现在能够非常容易地访问光照的属性了,我们可以随着时间改变它们的颜色,从而获得一些非常有意思的效果。

由于所有的东西都在片段着色器中配置好了,修改光源的颜色非常简单,并立刻创造一些很有趣的效果:

//光照强度
glm::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 influence
glm::vec3 ambientColor = diffuseColor * glm::vec3(0.2f); // low influence
objectShader.setVec3("light.ambient", ambientColor);
objectShader.setVec3("light.diffuse", diffuseColor);
objectShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f);

你可以看到,不同的光照颜色能够极大地影响物体的最终颜色输出。由于光照颜色能够直接影响物体能够反射的颜色,这对视觉输出有着显著的影响。

在这里插入图片描述

全部代码

#include "mainwindow.h"
#include <QApplication>//在包含GLFW的头文件之前包含了GLAD的头文件;
//GLAD的头文件包含了正确的OpenGL头文件(例如GL/gl.h);
//所以需要在其它依赖于OpenGL的头文件之前包含GLAD;
#include <glad/glad.h>
#include <GLFW/glfw3.h>//GLM
//#include <glm/glm.hpp>
//#include <glm/gtc/matrix_transform.hpp>
//#include <glm/gtc/type_ptr.hpp>#include <iostream>
#include <QDebug>#include "shader.h"
#include "stb_image.h"
#include "camera.h"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 = 1920;
const unsigned int SCR_HEIGHT = 1080;//Camera
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
float lastX =  SCR_WIDTH / 2.0;
float lastY =  SCR_HEIGHT / 2.0;
bool firstMouse = true;float deltaTime = 0.0f; // 当前帧与上一帧的时间差
float lastFrame = 0.0f; // 上一帧的时间//Light
glm::vec3 lightPos(1.2f, 1.0f, 2.0f);using namespace std;int main(int argc, char *argv[])
{QApplication a(argc, argv);//MainWindow w;//w.show();//初始化GLFW//--------------------glfwInit();//配置GLFW//--------------------//告诉GLFW使用的OpenGL本是3.3glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//告诉GLFW使用的是核心模式(Core-profile)glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//创建一个新的OpenGL环境和窗口//-----------------------------------GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();    //glfw销毁窗口和OpenGL环境,并释放资源return -1;}//设置参数window中的窗口所关联的OpenGL环境为当前环境//-----------------------------------glfwMakeContextCurrent(window);//设置窗口尺寸改变大小时的回调函数(窗口尺寸发送改变时会自动调用)//-----------------------------------glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);//设置鼠标事件的回调函数(鼠标移动时会自动调用)//-----------------------------------glfwSetCursorPosCallback(window, mouse_callback);//设置鼠标滚轮事件的回调函数(鼠标滚轮移动时会自动调用)//-----------------------------------glfwSetScrollCallback(window, scroll_callback);//告诉GLFW捕捉鼠标glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);//glad加载系统相关的OpenGL函数指针//---------------------------------------if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;return -1;}//开启深度测试glEnable(GL_DEPTH_TEST);Shader objectShader("C:/Qt_Pro/OpenGL_GLFW/shader/shader.vs","C:/Qt_Pro/OpenGL_GLFW/shader/shader.fs");Shader lightShader("C:/Qt_Pro/OpenGL_GLFW/shader/light_cube.vs","C:/Qt_Pro/OpenGL_GLFW/shader/light_cube.fs");//顶点数据//---------------------------------------------------------------------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};//物体//----------------------------------------------------------------unsigned int VBO, objectVAO;glGenVertexArrays(1, &objectVAO);     //创建顶点数组对象glGenBuffers(1, &VBO);          //创建顶点缓冲对象glBindBuffer(GL_ARRAY_BUFFER, VBO);     //将VBO与GL_ARRAY_BUFFER缓冲区绑定glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);  //将顶点数据复制到GL_ARRAY_BUFFER缓冲区,之后可通过VBO进行操作glBindVertexArray(objectVAO);         //绑定VAO//设定顶点属性指针//位置属性glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);//法向量属性glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));glEnableVertexAttribArray(1);//光源(VBO用上面的)//----------------------------------------------------------------unsigned int lightVAO;glGenVertexArrays(1, &lightVAO);     //创建顶点数组对象glBindVertexArray(lightVAO);         //绑定VAOglBindBuffer(GL_ARRAY_BUFFER, VBO);     //将VBO与GL_ARRAY_BUFFER缓冲区绑定glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);//渲染循环//我们可不希望只绘制一个图像之后我们的应用程序就立即退出并关闭窗口;//我们希望程序在我们主动关闭它之前不断绘制图像并能够接受用户输入;//因此,我们需要在程序中添加一个while循环,它能在我们让GLFW退出前一直保持运行;//------------------------------------------------------------------------------while (!glfwWindowShouldClose(window))  //如果用户准备关闭参数window所指定的窗口,那么此接口将会返回GL_TRUE,否则将会返回GL_FALSE{//更新时间差float currentFrame = static_cast<float>(glfwGetTime());deltaTime = currentFrame - lastFrame;lastFrame = currentFrame;//用户输入//------------------------------------------------------------------------------processInput(window);   //检测是否有输入//渲染指令//------------------------------------------------------------------------------glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//被投光物体//============================================================//激活着色器程序对象objectShader.use();objectShader.setVec3("objectColor", 1.0f, 0.5f, 0.31f);objectShader.setVec3("lightColor", 1.0f, 1.0f, 1.0f);objectShader.setVec3("lightPos", lightPos);objectShader.setVec3("viewPos", camera.Position);//光照强度glm::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 influenceobjectShader.setVec3("light.ambient", ambientColor);objectShader.setVec3("light.diffuse", diffuseColor);objectShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f);//材质objectShader.setVec3("material.ambient",  1.0f, 0.5f, 0.31f);objectShader.setVec3("material.diffuse",  1.0f, 0.5f, 0.31f);objectShader.setVec3("material.specular", 0.5f, 0.5f, 0.5f);objectShader.setFloat("material.shininess", 32.0f);//创建变换矩阵glm::mat4 model = glm::mat4(1.0f);objectShader.setMat4("model", model);glm::mat4 view = camera.GetViewMatrix();objectShader.setMat4("view", view);glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);objectShader.setMat4("projection", projection);//绘制三角形glBindVertexArray(objectVAO);             //绑定VAOglDrawArrays(GL_TRIANGLES, 0, 36);//============================================================//光源//============================================================//激活着色器程序对象lightShader.use();lightShader.setMat4("view", view);lightShader.setMat4("projection", projection);model = glm::mat4(1.0f);model = glm::translate(model, lightPos);model = glm::scale(model, glm::vec3(0.2f));lightShader.setMat4("model", model);//绘制三角形glBindVertexArray(lightVAO);             //绑定VAOglDrawArrays(GL_TRIANGLES, 0, 36);//============================================================//告诉GLFW检查所有等待处理的事件和消息,包括操作系统和窗口系统中应当处理的消息。如果有消息正在等待,它会先处理这些消息再返回;否则该函数会立即返回//---------------------------------------------------------------------------------------------------------------------------------glfwPollEvents();//请求窗口系统将参数window关联的后缓存画面呈现给用户(双缓冲绘图)//------------------------------------------------------------------------------glfwSwapBuffers(window);}//释放资源glDeleteVertexArrays(1, &objectVAO);glDeleteVertexArrays(1, &lightVAO);glDeleteBuffers(1, &VBO);//glDeleteProgram(objectShader);//glDeleteProgram(lightShader);//glfw销毁窗口和OpenGL环境,并释放资源(之后必须再次调用glfwInit()才能使用大多数GLFW函数)//------------------------------------------------------------------glfwTerminate();return a.exec();
}//检测是否有输入
//---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)   //ESC键,退出glfwSetWindowShouldClose(window, true);float cameraSpeed = static_cast<float>(2.5 * deltaTime);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);
}//给glfw窗口注册的尺寸改变回调函数
//---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{//确保视口匹配新的窗口尺寸,请注意:宽度和高度将比视网膜显示器上指定的大得多glViewport(0, 0, width, height);
}
// 鼠标移动时的回调函数
// -------------------------------------------------------
void mouse_callback(GLFWwindow* window, double xposIn, double yposIn)
{float xpos = static_cast<float>(xposIn);float ypos = static_cast<float>(yposIn);if (firstMouse){lastX = xpos;lastY = ypos;firstMouse = false;}float xoffset = lastX - xpos;float yoffset = ypos - lastY;   //翻转,因为Y轴是从下到上越来越大lastX = xpos;lastY = ypos;camera.ProcessMouseMovement(xoffset, yoffset);
}//鼠标滚轮的回调函数
// ----------------------------------------------------------------------
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{camera.ProcessMouseScroll(static_cast<float>(yoffset));
}

相关文章:

跟着LearnOpenGL学习11--材质

文章目录 一、材质二、设置材质三、光的属性四、不同的光源颜色 一、材质 在现实世界里&#xff0c;每个物体会对光产生不同的反应。 比如&#xff0c;钢制物体看起来通常会比陶土花瓶更闪闪发光&#xff0c;一个木头箱子也不会与一个钢制箱子反射同样程度的光。 有些物体反…...

Java guava partition方法拆分集合自定义集合拆分方法

日常开发中&#xff0c;经常遇到拆分集合处理的场景&#xff0c;现在记录2中拆分集合的方法。 1. 使用Guava包提供的集合操作工具栏 Lists.partition()方法拆分 首先&#xff0c;引入maven依赖 <dependency><groupId>com.google.guava</groupId><artifa…...

GLTF编辑器-位移贴图实现破碎的路面

在线工具推荐&#xff1a; 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 位移贴图是一种可以用于增加模型细节和形状的贴图。它能够在渲染时针…...

多维时序 | MATLAB实现SSA-BiLSTM麻雀算法优化双向长短期记忆神经网络多变量时间序列预测

多维时序 | MATLAB实现SSA-BiLSTM麻雀算法优化双向长短期记忆神经网络多变量时间序列预测 目录 多维时序 | MATLAB实现SSA-BiLSTM麻雀算法优化双向长短期记忆神经网络多变量时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.MATLAB实现SSA-BiLSTM麻雀算法优化…...

docker安装Nacos和Rabbitmq

一、安装Nacos 首先需要拉取对应的镜像文件&#xff1a;&#xff08;切换版本加上对应版本号即可&#xff0c;默认最新版&#xff09; docker pull nacos/nacos-server 接着挂载目录&#xff1a; mkdir -p /mydata/nacos/logs/ #新建logs目录 mkdir -p …...

Android MVC 写法

前言 Model&#xff1a;负责数据逻辑 View&#xff1a;负责视图逻辑 Controller&#xff1a;负责业务逻辑 持有关系&#xff1a; 1、View 持有 Controller 2、Controller 持有 Model 3、Model 持有 View 辅助工具&#xff1a;ViewBinding 执行流程&#xff1a;View >…...

网络层解读

基本介绍 概述 当两台主机之间的距离较远(如相隔几十或几百公里&#xff0c;甚至几千公里)时&#xff0c;就需要另一种结构的网络&#xff0c;即广域网。广域网尚无严格的定义。通常是指覆盖范围很广(远超过一个城市的范围)的长距离的单个网络。它由一些结点交换机以及连接这些…...

js for和forEach 跳出循环 替代方案

1 for循环跳出 for(let i0;i<10;i){if(i5){break;}console.log(i) }在函数中也可以return跳出循环 function fn(){for(let i0;i<10;i){if(i5){return;}console.log(i)} } fn()for ... of效果同上 2 forEach循环跳出 break会报错 [1,2,3,4,5,6,7,8,9,10].forEach(i>…...

如何使用ArcGIS Pro自动矢量化建筑

相信你在使用ArcGIS Pro的时候已经发现了一个问题&#xff0c;那就是ArcGIS Pro没有ArcScan&#xff0c;在ArcGIS Pro中&#xff0c;Esri确实已经移除了ArcScan&#xff0c;没有了ArcScan我们如何自动矢量化地图&#xff0c;从地图中提取建筑等要素呢&#xff0c;这里为大家介绍…...

交互式笔记Jupyter Notebook本地部署并实现公网远程访问内网服务器

最近&#xff0c;我发现了一个超级强大的人工智能学习网站。它以通俗易懂的方式呈现复杂的概念&#xff0c;而且内容风趣幽默。我觉得它对大家可能会有所帮助&#xff0c;所以我在此分享。点击这里跳转到网站。 文章目录 1.前言2.Jupyter Notebook的安装2.1 Jupyter Notebook下…...

41.坑王驾到第七期:uniapp开发微信小程序引用组件时报错!

一、错误再现 页面login引用了一个组件register&#xff0c;运行至小程序开发工具报错。 xxx.js 已被代码依赖分析忽略&#xff0c;无法被其他模块引用。 二、解决办法 在微信小程序的配置文件中找到setting节点&#xff0c;增加两个配置项。 “ignoreDevUnusedFiles”: fa…...

挂载与解挂载

一. 挂载 1.什么是挂载 将系统中的文件夹和磁盘做上关联&#xff0c;使用文件夹等于使用磁盘 2.mount 2.1 格式 mount [ -t 类型 ] 存储设备 挂载点目录 mount -o loop ISO镜像文件 挂载点目录 注意&#xff1a;指明要挂载的设备 设备文件&#xff1a;例如:/dev/sda5 卷…...

UGUI Panel的显示和隐藏优化

unity UI如何开启&#xff08;显示&#xff09;或者关闭&#xff08;隐藏&#xff09;Panel界面&#xff0c;相信大家都是知道的&#xff0c;但是如何做最好呢&#xff1f; 可能大家一般开启/关闭界面的方法就是直接SetActive吧。这样做通常是可以的&#xff0c;简答快速地解决…...

Linux:多文件编辑

多文件编辑 1.使用vim编辑多个文件 编辑多个文件有两种形式&#xff0c;一种是在进入vim前使用的参数就是多个文件。另一种就是进入vim后再编辑其他的文件。 同时创建两个新文件并编辑 $ vim 1.txt 2.txt默认进入1.txt文件的编辑界面 命令行模式下输入:n编辑2.txt文件&…...

模式识别与机器学习-概率图模型

模式识别与机器学习-概率图模型 概率图模型三大基本问题表示推断学习 有向概率图模型例子三种经典的图 HMMViterbi 算法 谨以此博客作为复习期间的记录 概率图模型三大基本问题 概率图模型通常涉及三个基本问题&#xff0c;即表示&#xff08;Representation&#xff09;、推…...

RK3566 ANDROID 11 平台上适配移远EC200A

适配前理清楚一下调试的流程: 1.该模块为LGA封装,需要控制上电时序模块才能正常上电工作: 2.模块供电正常后,读取模组的PID 和VID 并将其ID添加到内核里面,确保USB转Serial端口能够正常生成: 3.生成ttyUSB0~ttyUSB2端口后,确保rild进程正常启动,能够正常加载ril库; …...

存算分离降本增效,StarRocks 助力聚水潭 SaaS 业务服务化升级

作者&#xff1a;聚水潭数据研发负责人 溪竹 聚水潭是中国领先的 SaaS 软件服务商&#xff0c;核心产品是电商 ERP&#xff0c;协同350余家电商平台&#xff0c;为商家提供综合的信息化、数字化解决方案。公司是偏线下商家侧的 toB 服务商&#xff0c;员工人数超过3500&#xf…...

Linux 内核学习笔记: hlist 的理解

前言 最近阅读 Linux 内核时&#xff0c;遇到了 hlist&#xff0c;这个 hlist 用起来像是普通的链表&#xff0c;但是为何使用 hlist&#xff0c;hlist 是怎么工作的&#xff1f; 相关代码 hlist_add_head(&clk->clks_node, &core->clks); /*** clk_core_link_…...

几种设计模式介绍

前言 设计模式是一种用于解决软件开发中常见问题的通用解决方案&#xff0c;它可以提高代码的可读性、可维护性和可复用性。前端开发中也有很多应用设计模式的场景&#xff0c;比如处理异步操作、优化性能、封装复杂逻辑等。 前端开发中常见的设计模式有以下几种&#xff1a; …...

拓展操作(三) jenkins迁移到另一个机器

让清单成为一种习惯 互联网时代的变革,不再是简单的开发部署上线,持续,正确,安全地把事情做好尤其重要;把事情做好的前提是做一个可量化可执行的清单,让工程师就可以操作的清单而不是专家才能操作: 设定检查点 根据节点执行检查程序操作确认或边读边做 二者选其一不要太…...

重定向和转发的区别

重定向 1、定义 用户通过浏览器发送一个请求&#xff0c;Tomcat服务器接收这个请求&#xff0c;会给浏览器发送一个状态码302&#xff0c;并设置一个重定向的路径&#xff0c;浏览器如果接收到了这个302的状态码以后&#xff0c;就会去自动加载服务器设置的路径 一个页面跳转…...

基于ElementUI二次封装弹窗组件

效果&#xff1a; 一、自定义内容类型弹窗 <!-- title&#xff1a;对话框的标题confirmLoading&#xff1a;当前是否处于提交中titleCenter&#xff1a;对话框标题居中方式footerCenter&#xff1a;底部按钮的对其方式visible&#xff1a;是否显示弹窗width&#xff1a;设置…...

linux cat命令改变功能显示当前文件行号

linux的cat命令使用-n显示多个文件行号时&#xff0c;行号是累加的&#xff0c;不是到了新文件就重新计数。这样满足不了我的需求。如果到了新文件能够重新计数&#xff0c;就能使用-nf&#xff08;在上一篇-f显示文件名功能的基础上&#xff09;加| grep xxx&#xff0c;既能直…...

Django-REST-Framework 如何快速生成Swagger, ReDoc格式的 REST API 文档

1、API 接口文档的几种规范格式 前后端分离项目中&#xff0c;使用规范、便捷的API接口文档工具&#xff0c;可以有效提高团队工作效率。 标准化的API文档的益处&#xff1a; 允许开发人员以交互式的方式查看、测试API接口&#xff0c;以方便使用将所有可暴露的API接口进行分…...

SpringBoot当中的Singleton和Prototype详解

在Spring Boot中&#xff0c;Singleton和Prototype是两种Bean的作用域。这两种作用域决定了Spring容器如何创建和管理Bean的实例。 Singleton&#xff08;单例&#xff09;&#xff1a; 当一个Bean被配置为Singleton作用域时&#xff0c;Spring容器在启动时只会创建该Bean的一个…...

LeetCode第1题 - 两数之和

题目 给定一个整数数组 nums 和一个目标值 target&#xff0c;请你在该数组中找出和为目标值的那 两个 整数&#xff0c;并返回他们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;你不能重复利用这个数组中同样的元素。 示例 给定 nums [2, 7, 11, 15], …...

(14)Linux 地址空间的理解

前言&#xff1a;本章核心主题为 "进程地址空间"。 一、Linux 进程地址空间 程序地址空间是内存吗&#xff1f;不是&#xff01;程序地址空间不是内存&#xff01; 其实&#xff0c;我们称之为程序地址空间都不准确&#xff0c;应该叫 进程地址空间&#xff0c;这…...

Java中的设计模式

设计模式是软件开发中常见问题的可重用解决方案。在Java中&#xff0c;设计模式有助于提高代码的可维护性、可读性和可扩展性。以下是一篇关于Java中设计模式的文章&#xff0c;以帮助您更好地理解这些模式。 一、设计模式简介 设计模式是经过验证的解决方案&#xff0c;用于…...

Hadoop(2):常见的MapReduce[在Ubuntu中运行!]

1 以词频统计为例子介绍 mapreduce怎么写出来的 弄清楚MapReduce的各个过程&#xff1a; 将文件输入后&#xff0c;返回的<k1,v1>代表的含义是&#xff1a;k1表示偏移量&#xff0c;即v1的第一个字母在文件中的索引&#xff08;从0开始数的&#xff09;&#xff1b;v1表…...

Unity | 快速修复Animation missing错误

目录 一、背景 二、效果 三、解决办法 一、背景 最近在做2D 骨骼动画相关的Demo&#xff0c;我自己使用Unity引擎进行骨骼绑定并创建了anim后&#xff0c;一切正常&#xff0c;anim也能播放。但是昨天我修改Obj及子物体的名称&#xff08;由中文改为英文&#xff0c;如&…...

ssm基于web的志愿者管理系统的设计与实现+vue论文

摘 要 使用旧方法对志愿者管理系统的信息进行系统化管理已经不再让人们信赖了&#xff0c;把现在的网络信息技术运用在志愿者管理系统的管理上面可以解决许多信息管理上面的难题&#xff0c;比如处理数据时间很长&#xff0c;数据存在错误不能及时纠正等问题。这次开发的志愿者…...

C++运算符重载(插入and提取)

介绍 本文主要介绍 插入(>>) and 提取(<<)的运算符重载 1.插入(>>) 提取(<<)只能是友元函数 2.插入关键词istream 例子&#xff1a;istream& operator>>(istream& in, sumber&Left) 3.提取关键词ostream 例子&#xff1a;ostream&a…...

C#高级 08Json操作

1.概念 Json是存储和交换文本信息的语法。类似于XML。Json比XML更小、更快、更易解析。Json与XML一样是一种数据格式。Json是一种轻量级的数据交换格式。它基于ECMAScript的一个子集。Json采取完全独立于语言的文本格式&#xff0c; 但是也使用了类似于C语言的习惯。这些特性使…...

封装uniapp签字板

新开发的业务涉及到签字功能&#xff0c;由于是动态的表单&#xff0c;无法确定它会出现在哪里&#xff0c;不得已封装模块。 其中涉及到一个难点就是this的指向性问题&#xff0c; 第二个是微信小程序写法&#xff0c; 我这个写法里用了u-view的写法&#xff0c;可以自己修改组…...

Mybatis行为配置之Ⅳ—日志

专栏精选 引入Mybatis Mybatis的快速入门 Mybatis的增删改查扩展功能说明 mapper映射的参数和结果 Mybatis复杂类型的结果映射 Mybatis基于注解的结果映射 Mybatis枚举类型处理和类型处理器 再谈动态SQL Mybatis配置入门 Mybatis行为配置之Ⅰ—缓存 Mybatis行为配置…...

Java设计模式-外观模式

目录 一、影院管理项目 二、外观模式 &#xff08;一&#xff09;基本介绍 &#xff08;二&#xff09;原理类图 &#xff08;三&#xff09;解决影院管理 &#xff08;四&#xff09;注意事项和细节 &#xff08;五&#xff09;外观模式在MyBatis框架应用的源码分析 一…...

js+css实现颜色选择器

<!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>颜色选择器</title><style>.color-box {width: 50px;height: 50px;border: 1px solid #000;cursor: pointer;}</style> </head> <body><…...

Go语言中的包管理工具之Go Modules的使用

GoLang 中常用的包管理的方式 常用的有三种 Go PathGo VendorGo Modules 关于 Go Modules 1 ) 概述 Go的包管理&#xff0c;经过社区和官方的共同努力下&#xff0c;最终在百家争鸣后Go官方在 2018.8 推出了go 1.11版本中的Go Modules&#xff0c;并且很快成为一统江湖的包…...

【c/c++】指针例图基础详解

文章目录 指针变量内存指针详解例1例2练习&答案解析 指针变量内存 int main(){// 各类型变量占字节数printf("char: %d\n",sizeof(char)); // 1printf("short: %d\n",sizeof(short)); // 2printf("int: %d\n",sizeof(int)); // 4pri…...

TCP/IP的网络层(即IP层)之IP地址和网络掩码,在视频监控系统中的配置和应用

在给客户讲解我们的AS-V1000视频监控平台的时候&#xff0c;有的客户经常会配置错误IP地址的掩码和网关&#xff0c;导致出现一些网路问题。而在视频监控系统中&#xff0c;IP地址和子网掩码是用于标识网络中设备的重要标识符。IP地址被用来唯一地标识一个网络设备&#xff0c;…...

代码随想录刷题 | Day1

今日学习目标 一、基础 数组 array类 模板类vector 数组是存放在连续内存空间上的相同类型数据的集合。 数组可以方便的通过下标索引的方式获取到下标下对应的数据。 需要两点注意的是 数组下标都是从0开始的。 数组内存空间的地址是连续的 而且大家如果使用C的话&…...

查看IOS游戏FPS

摘要 本篇技术博客将介绍如何使用克魔助手工具来查看iOS游戏的帧率&#xff08;FPS&#xff09;。通过克魔助手&#xff0c;开发者可以轻松监测游戏性能&#xff0c;以提升用户体验和游戏质量。 引言 在iOS游戏开发过程中&#xff0c;了解游戏的帧率对于优化游戏性能至关重要…...

挑战Python100题(7)

100+ Python challenging programming exercises 7 Question 61 Print a unicode string "hello world". Hints: Use ustrings format to define unicode string. 打印一个unicode字符串“helloworld”。 提示:使用u“字符串”格式定义unicode字符串。 Solution…...

HarmonyOS自学-Day4(TodoList案例)

目录 文章声明⭐⭐⭐让我们开始今天的学习吧&#xff01;TodoList小案例 文章声明⭐⭐⭐ 该文章为我&#xff08;有编程语言基础&#xff0c;非编程小白&#xff09;的 HarmonyOS自学笔记&#xff0c;此类文章笔记我会默认大家都学过前端相关的知识知识来源为 HarmonyOS官方文…...

LTPI协议的理解——2、LTPI实现的底层架构

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 LTPI协议的理解——2、LTPI实现的底层架构 前言一、体系结构三、实现细节四、物理接口信号传输方法总结 前言 前面讲了LTPI的定义和大概结构&#xff0c;接下来继续理解LTPI…...

CentOS 8.2 安装 Mysql 5.7.26(单机)

Mysql二进制包: mysql-5.7.26-linux-glibc2.12-x86_64.tar.gz 1、卸载旧环境 rpm -qa|grep mysql rpm -qa|grep mariadb rpm -e XXX.rpm --nodeps # 强制卸载rpm包 rm -rf /etc/my.cnf rm -rf /etc/mysql rm -rf /usr/local/mysql 2、安装依赖包 yum -y install libaio yum…...

Vue Tinymce富文本组件自定义带下拉框的操作按钮

想实现如下效果 首先在init方法中的props&#xff0c;toolbar属性增加一个自定义按钮 增加一个setup方法 代码 setup: function(editor) { editor.ui.registry.addSplitButton(myDateButton, {text: 日期时间,onAction: (_) > editor.insertContent(getJsMonthDay(getNowDat…...

YOLOv5算法进阶改进(10)— 更换主干网络之MobileViTv3 | 轻量化Backbone

前言:Hello大家好,我是小哥谈。MobileViTv3是一种改进的模型架构,用于图像分类任务。它是在MobileViTv1和MobileViTv2的基础上进行改进的,通过引入新的模块和优化网络结构来提高性能。本节课就给大家介绍一下如何在主干网络中引入MobileViTv3网络结构,希望大家学习之后能够…...

Java UDP

接收方 创建DatagramSocket实例并指定端口。创建DatagramPacket实例接收信息。调用DatagramSocket的receive()方法将接收信息并传递给DatagramPacket。通过DatagramPacket的getData()方法获取信息内容&#xff0c;getLength()方法获取长度。 package io.github.jast90.udp;im…...

Halcon阈值处理的几种分割方法threshold/auto_threshold/binary_threshold/dyn_threshold

Halcon阈值处理的几种分割方法 文章目录 Halcon阈值处理的几种分割方法1. 全局阈值2. 基于直方图的自动阈值分割方法3. 自动全局阈值分割方法4. 局部阈值分割方法5. var_threshold算子6 . char_threshold 算子7. dual_threshold算子 在场景中选择物体或特征是图像测量或识别的重…...