3 - 材质

每个物体会对光产生不同的反应,例如钢制物体通常会比陶土花瓶更闪闪发光。如果想要在OpenGL中模拟多种类型的物体,必须针对每种表面定义不同的 材质(Material)属性。

材质

材质的定义

当描述一个表面时,可定义的材质属性如下:

  • 环境光照:在环境光照下该表面反射什么颜色
  • 漫反射光照:定义在漫反射光照下表面的颜色
  • 镜面光照:表面上镜面高光的颜色
  • 反光度(Shininess):影响镜面高光的散射/半径

在片段着色器中,我们定义它的结构体:

// 材质结构体
struct Material 
{
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float shininess;
};
    
uniform Material material;

这里有一些材质的属性,它们模拟了现实世界的真实材质:

设置材质

接下来改一下片段着色器,让它用材质的分量进行相关计算,而不是再用objectColor,并且KaKdKs也能退役了:

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;

然后更新片段着色器,让lightPoslightColor退役:

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)