- 现实世界中,不同材质的物体对光的反应是不一样的,如钢材质比起木材质会更亮。在OpenGL中,想模拟不同物体的类型,我们需要给不同的渲染表面指定不同的材质(material)属性。
- 要描述渲染表面的材质,我们可以为光照的3个分量(环境光、扩散光和镜面光)分别定义一个材质颜色,并添加一个亮度值来控制这3个颜色,这就构成了我们需要的物体的材质属性。
// Material结构存储表面的材质属性
struct Material{
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
uniform Material material;
1. `ambient`材质矢量:定义表面在环境光下的反射的颜色,一般就是表面的颜色。
2. `diffuse`材质矢量:定义表面在扩散光下的反射的颜色。一般设置为表面想要的颜色。
3. `specular`材质矢量:设置镜面光斑在表面上的颜色。
4. `shininess`:影响镜面光斑的扩散/直径。
- 使用上述四个分量定义的物体材质,我们可以模拟现实世界中对应的材料。下面是一些材质属性的光照效果:(图片取自书中)
不同材质光照效果
1. 设置材质
- 根据上面定义的材质结构,我们修改片元着色器中的光源计算:
void main()
{
// ambient
vec3 ambient = lightColor * material.ambient;
// diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = lightColor * (diff * material.diffuse);
// specular
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
vec3 specular = lightColor * (spec * material.specular);
vec3 result = ambient + diffuse + specular;
FragColor = vec4(result, 1.0);
}
- 在程序中设置材质属性:
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);
- 注意:GLSL中的结构其实只是充当uniform变量的命名空间。
-
渲染效果
材质渲染效果
2. 光属性
- 上面的渲染效果,让我们感觉太明亮了,那是因为我们没对光的强度做任何处理。如果我们的光颜色是
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);
- 要对光的强度做处理,我们可以像上一章基本光照那样,给各个光分量设定一个强度值(strength value),来控制光不同分量的影响。
- 下面我们也像对待物体材质那样,创建一个结构来描述光的属性,从而实现对光不同分量的控制。下面我们在片元着色器中添加光属性的结构:
struct Light{
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform Light light;
- 根据光属性重新计算各个分量的值:
// ambient
vec3 ambient = light.ambient * material.ambient;
// diffuse
vec3 diffuse = light.diffuse * (diff * material.diffuse);
// specular
vec3 specular = light.specular * (spec * material.specular);
- 一般情况下,我们将环境光的强度设置得低一点,避免环境光对场景造成过大影响。扩散光则一般设置为我们需要的光源的颜色。而镜面光则保持为
vec3(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);
-
渲染效果
光属性渲染效果
3. 不同的光颜色
- 看看前面我们光颜色值的设置可以知道,我们只是在灰(含白)到黑之间选择光的颜色,这样的光并不会影响物体实际的颜色,只会影响物体显示的强度。下面我们让光的颜色随时间变化,看看立方体渲染出来的效果。下面是环境光和扩散光的设置代码(镜面光保持不变):
// 光的颜色
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.5f);
// 环境光分量
glm::vec3 ambientColor = diffuseColor * glm::vec3(0.2f);
// 设置到片元着色器中
objectShader.setVec3("light.ambient", ambientColor);
objectShader.setVec3("light.diffuse", diffuseColor);
-
下面是随时间变化的一些渲染效果
光颜色渲染1
网友评论