光照图(Lighting Maps)
- 现实世界的物体通常包含不止一种材质,而上一章我们为物体整体定义了一种材质属性,这可以看作是最简单的一个材质模型。本章我们引入扩散光和镜面光图(diffuse and specular maps),来允许我们更精确的控制扩散光(间接影响环境光)和镜面光的影响。
1. 扩散光图
- 我们希望通过某种方式能够设置每个物体上的每个片元扩散光的颜色,因此我们需要能够根据物体上片元的位置来检索它的颜色。这听起来与我们前面讨论过的纹理是一样的。其实扩散光图与纹理的底层原理都是:使用一张包装物体的图像,从图像中我们可以为每个片元索引一个唯一的颜色。
- 在着色器中使用扩散光图就像在着色器中使用纹理一样。我们将扩散光图定义到片元着色器的
Material
结构中,同时定义一个输入变量接受纹理坐标。(在本例中,为了简单,我们将环境光与扩散光取同样的颜色,所以也移除了结构中的环境光分量)
struct Material{
sampler2D diffuse;
vec3 specular;
float shininess;
};
...
in vec2 TexCoords;
- 从纹理中检索片元的扩散光颜色值进行计算:
// ambient
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
// diffuse
vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
- 更新顶点着色器,接收纹理坐标,并将其输出到片元着色器
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
...
out vec2 TexCoords;
int main()
{
...
TexCoords = aTexCoords;
}
- 代码中设置纹理坐标属性:
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
- 设置纹理数据(纹理数据的加载请参考纹理章节)
objectShader.use();
objectShader.setInt("material.diffuse", 0);
- 渲染循环中绑定和启用纹理
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
-
渲染效果(立方体做了一定的移动和旋转)
扩散光图效果
2. 镜面光图
-
上面渲染效果中的镜面光斑看起来有点奇怪,因为木材质不应该产生这样的光斑。这个问题我们可以使用镜面光图来解决。对于镜面光图,我们需要生成一个黑白(也可以是彩色的)纹理,该纹理定义物体每个部分的镜面光强度。上一小节扩散光图对应的镜面光图如下所示。这里,我们简单认为木材质不会对镜面光产生反应,因此中间区域全部生成黑色。而边框的钢材质则使用强度不一的灰度图。
镜面光图像 - 镜面光斑的强度取决于镜面光图中每个像素的亮度。在着色器中,我们就可以采样对应的颜色值然后与光的镜面分量强度相乘。
- 修改片元着色器,添加镜面光图。
struct Material{
sampler2D diffuse;
sampler2D specular;
float shininess;
};
- 片元颜色计算
void main()
{
// ambient
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
// diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(light.position - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords).rgb);
// specular
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
vec3 result = ambient + diffuse + specular;
FragColor = vec4(result, 1.0);
}
- 代码设置镜面光纹理
objectShader.setInt("material.specular", 1);
- 渲染循环中激活和绑定纹理(注意纹理单元的应用)。
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, specularMap);
- 镜面光图相当于在扩散光图上添加一层,让我们可以更好的控制光照下,物体各个部分的亮度。
-
渲染效果
镜面光图效果
网友评论