96SEO 2026-02-19 08:38 0
比如#xff0c;钢制物体看起来通常会比陶土花瓶更闪闪发光#xff0c;一个木头箱子也不会与一个钢制箱子反射同样程度的光。

比如钢制物体看起来通常会比陶土花瓶更闪闪发光一个木头箱子也不会与一个钢制箱子反射同样程度的光。
有些物体反射光的时候不会有太多的散射(Scatter)因而产生较小的高光点而有些物体则会散射很多产生一个有着更大半径的高光点。
如果我们想要在OpenGL中模拟多种类型的物体我们必须针对每种表面定义不同的材质(Material)属性。
在跟着LearnOpenGL学习10–基础光照这一篇中我们定义了一个物体和光的颜色并结合环境光与镜面强度分量来决定物体的视觉输出。
当描述一个表面时我们可以分别为三个光照分量定义一个材质颜色(Material
通过为每个分量指定一个颜色我们就能够对表面的颜色输出有细粒度的控制了。
现在我们再添加一个反光度(Shininess)分量结合上述的三个颜色我们就有了全部所需的材质属性了
//材质在片段着色器中我们创建一个结构体(Struct)来储存物体的材质属性。
我们也可以把它们储存为独立的uniform值但是作为一个结构体来储存会更有条理一些。
我们首先定义结构体的布局(Layout)然后简单地以刚创建的结构体作为类型声明一个uniform变量。
ambient材质向量定义了在环境光照下这个表面反射的是什么颜色通常与表面的颜色相同。
diffuse材质向量定义了在漫反射光照下表面的颜色。
漫反**色和环境光照一样也被设置为我们期望的物体颜色。
specular材质向量设置的是表面上镜面高光的颜色或者甚至可能反映一个特定表面的颜色。
shininess材质向量影响镜面高光的散射/半径。
有这4个元素定义一个物体的材质我们能够模拟很多现实世界中的材质。
如下表格展示了一系列材质属性它们模拟了现实世界中的真实材质。
下图展示了几组现实世界的材质参数值对我们的立方体的影响
名字环境光照漫反射光照镜面反射光照反射强度emerald(翡翠)0.0215
0.3162280.1obsidian(黑曜石)0.05375
0.8078430.21794872bronze(青铜)0.2125
可以看到通过正确地指定一个物体的材质属性我们对这个物体的感知也就不同了。
效果非常明显但是要想获得更真实的效果我们需要以更复杂的形状替换这个立方体。
搞清楚一个物体正确的材质设定是个困难的工程这主要需要实验和丰富的经验。
用了不合适的材质而毁了物体的视觉质量是件经常发生的事。
我们在片段着色器中创建了一个材质结构体的uniform所以下面我们希望修改一下光照的计算来遵从新的材质属性。
由于所有材质变量都储存在一个结构体中我们可以从uniform变量material中访问它们
}可以看到我们现在在需要的地方访问了材质结构体中的所有属性并且这次是根据材质的颜色来计算最终的输出颜色的。
物体的每个材质属性都乘上了它们各自对应的光照分量。
我们现在可以通过设置适当的uniform来设置应用中物体的材质了。
GLSL中一个结构体在设置uniform时并无任何区别结构体只是充当uniform变量们的一个命名空间。
所以如果想填充这个结构体的话我们必须设置每个单独的uniform但要以结构体名为前缀
lightingShader.setVec3(material.ambient,
lightingShader.setVec3(material.diffuse,
lightingShader.setVec3(material.specular,
lightingShader.setFloat(material.shininess,
32.0f);我们将环境光和漫反射分量设置成我们想要让物体所拥有的颜色而将镜面分量设置为一个中等亮度的颜色我们不希望镜面分量过于强烈。
我们仍将反光度保持为32。
现在我们能够轻松地在应用中影响物体的材质了。
运行程序你会得到像这样的结果
QApplication//在包含GLFW的头文件之前包含了GLAD的头文件;
//GLAD的头文件包含了正确的OpenGL头文件例如GL/gl.h;
//所以需要在其它依赖于OpenGL的头文件之前包含GLAD;
framebuffer_size_callback(GLFWwindow*
w;//w.show();//初始化GLFW//--------------------glfwInit();//配置GLFW//--------------------//告诉GLFW使用的OpenGL本是3.3glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,
3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR,
3);//告诉GLFW使用的是核心模式Core-profileglfwWindowHint(GLFW_OPENGL_PROFILE,
GLFW_OPENGL_CORE_PROFILE);//创建一个新的OpenGL环境和窗口//-----------------------------------GLFWwindow*
-1;}//设置参数window中的窗口所关联的OpenGL环境为当前环境//-----------------------------------glfwMakeContextCurrent(window);//设置窗口尺寸改变大小时的回调函数窗口尺寸发送改变时会自动调用//-----------------------------------glfwSetFramebufferSizeCallback(window,
framebuffer_size_callback);//设置鼠标事件的回调函数鼠标移动时会自动调用//-----------------------------------glfwSetCursorPosCallback(window,
mouse_callback);//设置鼠标滚轮事件的回调函数鼠标滚轮移动时会自动调用//-----------------------------------glfwSetScrollCallback(window,
scroll_callback);//告诉GLFW捕捉鼠标glfwSetInputMode(window,
GLFW_CURSOR_DISABLED);//glad加载系统相关的OpenGL函数指针//---------------------------------------if
(!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout
-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
0.0f};//物体//----------------------------------------------------------------unsigned
//创建顶点缓冲对象glBindBuffer(GL_ARRAY_BUFFER,
//将VBO与GL_ARRAY_BUFFER缓冲区绑定glBufferData(GL_ARRAY_BUFFER,
//将顶点数据复制到GL_ARRAY_BUFFER缓冲区之后可通过VBO进行操作glBindVertexArray(objectVAO);
//绑定VAO//设定顶点属性指针//位置属性glVertexAttribPointer(0,
(void*)0);glEnableVertexAttribArray(0);//法向量属性glVertexAttribPointer(1,
sizeof(float)));glEnableVertexAttribArray(1);//光源(VBO用上面的)//----------------------------------------------------------------unsigned
//创建顶点数组对象glBindVertexArray(lightVAO);
//绑定VAOglBindBuffer(GL_ARRAY_BUFFER,
//将VBO与GL_ARRAY_BUFFER缓冲区绑定glVertexAttribPointer(0,
(void*)0);glEnableVertexAttribArray(0);//渲染循环//我们可不希望只绘制一个图像之后我们的应用程序就立即退出并关闭窗口;//我们希望程序在我们主动关闭它之前不断绘制图像并能够接受用户输入;//因此我们需要在程序中添加一个while循环它能在我们让GLFW退出前一直保持运行;//------------------------------------------------------------------------------while
(!glfwWindowShouldClose(window))
//如果用户准备关闭参数window所指定的窗口那么此接口将会返回GL_TRUE否则将会返回GL_FALSE{//更新时间差float
static_castfloat(glfwGetTime());deltaTime
currentFrame;//用户输入//------------------------------------------------------------------------------processInput(window);
//检测是否有输入//渲染指令//------------------------------------------------------------------------------glClearColor(0.2f,
1.0f);glClear(GL_COLOR_BUFFER_BIT
GL_DEPTH_BUFFER_BIT);//被投光物体////激活着色器程序对象objectShader.use();objectShader.setVec3(objectColor,
0.31f);objectShader.setVec3(lightColor,
1.0f);objectShader.setVec3(lightPos,
lightPos);objectShader.setVec3(viewPos,
camera.Position);objectShader.setVec3(material.ambient,
0.31f);objectShader.setVec3(material.diffuse,
0.31f);objectShader.setVec3(material.specular,
0.5f);objectShader.setFloat(material.shininess,
glm::mat4(1.0f);objectShader.setMat4(model,
camera.GetViewMatrix();objectShader.setMat4(view,
glm::perspective(glm::radians(camera.Zoom),
100.0f);objectShader.setMat4(projection,
projection);//绘制三角形glBindVertexArray(objectVAO);
//绑定VAOglDrawArrays(GL_TRIANGLES,
36);////光源////激活着色器程序对象lightShader.use();lightShader.setMat4(view,
view);lightShader.setMat4(projection,
glm::vec3(0.2f));lightShader.setMat4(model,
model);//绘制三角形glBindVertexArray(lightVAO);
//绑定VAOglDrawArrays(GL_TRIANGLES,
36);////告诉GLFW检查所有等待处理的事件和消息包括操作系统和窗口系统中应当处理的消息。
如果有消息正在等待它会先处理这些消息再返回否则该函数会立即返回//---------------------------------------------------------------------------------------------------------------------------------glfwPollEvents();//请求窗口系统将参数window关联的后缓存画面呈现给用户(双缓冲绘图)//------------------------------------------------------------------------------glfwSwapBuffers(window);}//释放资源glDeleteVertexArrays(1,
objectVAO);glDeleteVertexArrays(1,
VBO);//glDeleteProgram(objectShader);//glDeleteProgram(lightShader);//glfw销毁窗口和OpenGL环境并释放资源之后必须再次调用glfwInit()才能使用大多数GLFW函数//------------------------------------------------------------------glfwTerminate();return
//---------------------------------------------------------------------------------------------------------
//ESC键退出glfwSetWindowShouldClose(window,
GLFW_PRESS)camera.ProcessKeyboard(FORWARD,
GLFW_PRESS)camera.ProcessKeyboard(BACKWARD,
GLFW_PRESS)camera.ProcessKeyboard(LEFT,
GLFW_PRESS)camera.ProcessKeyboard(RIGHT,
//---------------------------------------------------------------------------------------------
framebuffer_size_callback(GLFWwindow*
{//确保视口匹配新的窗口尺寸请注意宽度和高度将比视网膜显示器上指定的大得多glViewport(0,
-------------------------------------------------------
ypos;camera.ProcessMouseMovement(xoffset,
----------------------------------------------------------------------
{camera.ProcessMouseScroll(static_castfloat(yoffset));
物体过亮的原因是环境光、漫反射和镜面光这三个颜色对任何一个光源都全力反射。
光源对环境光、漫反射和镜面光分量也分别具有不同的强度。
前面的章节中我们通过使用一个强度值改变环境光和镜面光强度的方式解决了这个问题。
我们想做类似的事情但是这次是要为每个光照分量分别指定一个强度向量。
如果我们假设lightColor是vec3(1.0)代码会看起来像这样
material.specular);所以物体的每个材质属性对每一个光照分量都返回了最大的强度。
对单个光源来说这些vec3(1.0)值同样可以对每种光源分别改变而这通常就是我们想要的。
现在物体的环境光分量完全地影响了立方体的颜色可是环境光分量实际上不应该对最终的颜色有这么大的影响所以我们会将光源的环境光强度设置为一个小一点的值从而限制环境光颜色
material.ambient;我们可以用同样的方式影响光源的漫反射和镜面光强度。
这和我们在上一节中所做的极为相似你可以认为我们已经创建了一些光照属性来影响各个光照分量。
我们希望为光照属性创建类似材质结构体的东西
light;一个光源对它的ambient、diffuse和specular光照分量有着不同的强度。
环境光照通常被设置为一个比较低的强度因为我们不希望环境光颜色太过主导。
光源的漫反射分量通常被设置为我们希望光所具有的那个颜色通常是一个比较明亮的白色。
镜面光分量通常会保持为vec3(1.0)以最大强度发光。
注意我们也将光源的位置向量加入了结构体。
objectShader.setVec3(light.ambient,
objectShader.setVec3(light.diffuse,
objectShader.setVec3(light.specular,
1.0f);现在我们已经调整了光照对物体材质的影响我们得到了一个与上一节很相似的视觉效果。
但这次我们有了对光照和物体材质的完全掌控
到目前为止我们都只对光源设置了从白到灰到黑范围内的颜色这样只会改变物体各个分量的强度而不是它的真正颜色。
由于现在能够非常容易地访问光照的属性了我们可以随着时间改变它们的颜色从而获得一些非常有意思的效果。
由于所有的东西都在片段着色器中配置好了修改光源的颜色非常简单并立刻创造一些很有趣的效果
static_castfloat(sin(glfwGetTime()
static_castfloat(sin(glfwGetTime()
static_castfloat(sin(glfwGetTime()
objectShader.setVec3(light.ambient,
objectShader.setVec3(light.diffuse,
objectShader.setVec3(light.specular,
1.0f);你可以看到不同的光照颜色能够极大地影响物体的最终颜色输出。
由于光照颜色能够直接影响物体能够反射的颜色这对视觉输出有着显著的影响。
QApplication//在包含GLFW的头文件之前包含了GLAD的头文件;
//GLAD的头文件包含了正确的OpenGL头文件例如GL/gl.h;
//所以需要在其它依赖于OpenGL的头文件之前包含GLAD;
framebuffer_size_callback(GLFWwindow*
w;//w.show();//初始化GLFW//--------------------glfwInit();//配置GLFW//--------------------//告诉GLFW使用的OpenGL本是3.3glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,
3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR,
3);//告诉GLFW使用的是核心模式Core-profileglfwWindowHint(GLFW_OPENGL_PROFILE,
GLFW_OPENGL_CORE_PROFILE);//创建一个新的OpenGL环境和窗口//-----------------------------------GLFWwindow*
-1;}//设置参数window中的窗口所关联的OpenGL环境为当前环境//-----------------------------------glfwMakeContextCurrent(window);//设置窗口尺寸改变大小时的回调函数窗口尺寸发送改变时会自动调用//-----------------------------------glfwSetFramebufferSizeCallback(window,
framebuffer_size_callback);//设置鼠标事件的回调函数鼠标移动时会自动调用//-----------------------------------glfwSetCursorPosCallback(window,
mouse_callback);//设置鼠标滚轮事件的回调函数鼠标滚轮移动时会自动调用//-----------------------------------glfwSetScrollCallback(window,
scroll_callback);//告诉GLFW捕捉鼠标glfwSetInputMode(window,
GLFW_CURSOR_DISABLED);//glad加载系统相关的OpenGL函数指针//---------------------------------------if
(!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout
-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
0.0f};//物体//----------------------------------------------------------------unsigned
//创建顶点缓冲对象glBindBuffer(GL_ARRAY_BUFFER,
//将VBO与GL_ARRAY_BUFFER缓冲区绑定glBufferData(GL_ARRAY_BUFFER,
//将顶点数据复制到GL_ARRAY_BUFFER缓冲区之后可通过VBO进行操作glBindVertexArray(objectVAO);
//绑定VAO//设定顶点属性指针//位置属性glVertexAttribPointer(0,
(void*)0);glEnableVertexAttribArray(0);//法向量属性glVertexAttribPointer(1,
sizeof(float)));glEnableVertexAttribArray(1);//光源(VBO用上面的)//----------------------------------------------------------------unsigned
//创建顶点数组对象glBindVertexArray(lightVAO);
//绑定VAOglBindBuffer(GL_ARRAY_BUFFER,
//将VBO与GL_ARRAY_BUFFER缓冲区绑定glVertexAttribPointer(0,
(void*)0);glEnableVertexAttribArray(0);//渲染循环//我们可不希望只绘制一个图像之后我们的应用程序就立即退出并关闭窗口;//我们希望程序在我们主动关闭它之前不断绘制图像并能够接受用户输入;//因此我们需要在程序中添加一个while循环它能在我们让GLFW退出前一直保持运行;//------------------------------------------------------------------------------while
(!glfwWindowShouldClose(window))
//如果用户准备关闭参数window所指定的窗口那么此接口将会返回GL_TRUE否则将会返回GL_FALSE{//更新时间差float
static_castfloat(glfwGetTime());deltaTime
currentFrame;//用户输入//------------------------------------------------------------------------------processInput(window);
//检测是否有输入//渲染指令//------------------------------------------------------------------------------glClearColor(0.2f,
1.0f);glClear(GL_COLOR_BUFFER_BIT
GL_DEPTH_BUFFER_BIT);//被投光物体////激活着色器程序对象objectShader.use();objectShader.setVec3(objectColor,
0.31f);objectShader.setVec3(lightColor,
1.0f);objectShader.setVec3(lightPos,
lightPos);objectShader.setVec3(viewPos,
camera.Position);//光照强度glm::vec3
static_castfloat(sin(glfwGetTime()
static_castfloat(sin(glfwGetTime()
static_castfloat(sin(glfwGetTime()
influenceobjectShader.setVec3(light.ambient,
ambientColor);objectShader.setVec3(light.diffuse,
diffuseColor);objectShader.setVec3(light.specular,
1.0f);//材质objectShader.setVec3(material.ambient,
0.31f);objectShader.setVec3(material.diffuse,
0.31f);objectShader.setVec3(material.specular,
0.5f);objectShader.setFloat(material.shininess,
glm::mat4(1.0f);objectShader.setMat4(model,
camera.GetViewMatrix();objectShader.setMat4(view,
glm::perspective(glm::radians(camera.Zoom),
100.0f);objectShader.setMat4(projection,
projection);//绘制三角形glBindVertexArray(objectVAO);
//绑定VAOglDrawArrays(GL_TRIANGLES,
36);////光源////激活着色器程序对象lightShader.use();lightShader.setMat4(view,
view);lightShader.setMat4(projection,
glm::vec3(0.2f));lightShader.setMat4(model,
model);//绘制三角形glBindVertexArray(lightVAO);
//绑定VAOglDrawArrays(GL_TRIANGLES,
36);////告诉GLFW检查所有等待处理的事件和消息包括操作系统和窗口系统中应当处理的消息。
如果有消息正在等待它会先处理这些消息再返回否则该函数会立即返回//---------------------------------------------------------------------------------------------------------------------------------glfwPollEvents();//请求窗口系统将参数window关联的后缓存画面呈现给用户(双缓冲绘图)//------------------------------------------------------------------------------glfwSwapBuffers(window);}//释放资源glDeleteVertexArrays(1,
objectVAO);glDeleteVertexArrays(1,
VBO);//glDeleteProgram(objectShader);//glDeleteProgram(lightShader);//glfw销毁窗口和OpenGL环境并释放资源之后必须再次调用glfwInit()才能使用大多数GLFW函数//------------------------------------------------------------------glfwTerminate();return
//---------------------------------------------------------------------------------------------------------
//ESC键退出glfwSetWindowShouldClose(window,
GLFW_PRESS)camera.ProcessKeyboard(FORWARD,
GLFW_PRESS)camera.ProcessKeyboard(BACKWARD,
GLFW_PRESS)camera.ProcessKeyboard(LEFT,
GLFW_PRESS)camera.ProcessKeyboard(RIGHT,
//---------------------------------------------------------------------------------------------
framebuffer_size_callback(GLFWwindow*
{//确保视口匹配新的窗口尺寸请注意宽度和高度将比视网膜显示器上指定的大得多glViewport(0,
-------------------------------------------------------
ypos;camera.ProcessMouseMovement(xoffset,
----------------------------------------------------------------------
{camera.ProcessMouseScroll(static_castfloat(yoffset));
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback