3 - 材质
每个物体会对光产生不同的反应,例如钢制物体通常会比陶土花瓶更闪闪发光。如果想要在OpenGL中模拟多种类型的物体,必须针对每种表面定义不同的 材质(Material)属性。
材质
材质的定义
当描述一个表面时,可定义的材质属性如下:
- 环境光照:在环境光照下该表面反射什么颜色
- 漫反射光照:定义在漫反射光照下表面的颜色
- 镜面光照:表面上镜面高光的颜色
- 反光度(Shininess):影响镜面高光的散射/半径
在片段着色器中,我们定义它的结构体:
// 材质结构体
struct Material
{
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
uniform Material material;
这里有一些材质的属性,它们模拟了现实世界的真实材质:
设置材质
接下来改一下片段着色器,让它用材质的分量进行相关计算,而不是再用objectColor
,并且Ka
,Kd
,Ks
也能退役了:
void main()
{
// 加入环境光照
vec3 ambient = lightColor * material.ambient;
// 加入漫反射项
float r = distance(lightPos, FragPos);
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
vec3 diffuse = (lightColor * material.diffuse / (r * r)) * max(0.0, dot(norm, lightDir));
// 加入高光项
vec3 viewDir = normalize(viewPos - FragPos);
vec3 half = normalize(viewDir + lightDir);
vec3 specular = (lightColor * material.specular / (r * r)) * pow(max(0.0, dot(norm, half)), material.shininess);
vec3 result = ambient + diffuse + specular;
FragColor = vec4(result, 1.0);
}
然后加入一个物体材质康康:
// 玉石材质(练习二)
cubeShader.setVec3("material.ambient", 0.135f, 0.2225f, 0.1575f);
cubeShader.setVec3("material.diffuse", 0.54f, 0.89f, 0.63f);
cubeShader.setVec3("material.specular", 0.316228f, 0.316228f, 0.316228f);
cubeShader.setFloat("material.shininess", 0.1f * 128);
很值钱有没有。
光的属性
同理,光也能用几个分量来描述:
struct Light
{
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform Light light;
然后更新片段着色器,让lightPos
和lightColor
退役:
void main()
{
// 加入环境光照
vec3 ambient = material.ambient * light.ambient;
// 加入漫反射项
float r = distance(light.position, FragPos);
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(light.position - FragPos);
vec3 diffuse = material.diffuse * (light.diffuse / (r * r) ) * max(0.0, dot(norm, lightDir));
// 加入高光项
vec3 viewDir = normalize(viewPos - FragPos);
vec3 half = normalize(viewDir + lightDir);
vec3 specular = material.specular * (light.specular / (r * r) ) * pow(max(0.0, dot(norm, half)), material.shininess);
vec3 result = ambient + diffuse + specular;
FragColor = vec4(result, 1.0);
}
最后设置相关uniform变量即可:
// Light
cubeShader.setVec3("light.position", lightPos);
cubeShader.setVec3("light.ambient", 0.2f, 0.2f, 0.2f);
cubeShader.setVec3("light.diffuse", 2.0f, 2.0f, 2.0f);
cubeShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f);
结果如图:
不同的光源颜色
接下来让光源颜色随时间动起来,康康会发生什么:
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.9f); // 降低影响
glm::vec3 ambientColor = diffuseColor * glm::vec3(0.7f); // 很低的影响
cubeShader.setVec3("light.position", lightPos);
cubeShader.setVec3("light.ambient", ambientColor);
cubeShader.setVec3("light.diffuse", diffuseColor);
cubeShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f);
可以看见物体随着光源颜色的改变而改变。
练习
练习一
你能做到这件事吗,改变光照颜色导致改变光源立方体的颜色?
只需在光源专属的片段着色器上新增一个uniform变量即可:
uniform vec3 lightColor;
void main()
{
FragColor = vec4(lightColor, 1.0);
}
lightShader.setVec3("lightColor", lightColor);
本文代码详见这里。
参考资料
- 材质 - LearnOpenGL CN (learnopengl-cn.github.io)