光照贴图
在现实世界中,一个物体都是由很多部分组成的,而组成物体的各个部分也拥有着完全不同的材质,而在之前我们所定义的物体为一个均匀的材质,这是远远不够的。
这里就引入漫反射贴图以及镜面反射贴图,允许我们能够更精确的控制漫反射分量和镜面分量。
漫反射贴图
漫反射贴图说白了就是之前所运用到的纹理,两者所用到的原理完全相同。
漫反射贴图表示的是物体所有漫反射颜色的纹理图像。例如下图:
用法与纹理一样,只不过在此处之前的漫反射颜色向量vec3 ambient替换为漫反射贴图。将纹理存储为结构体material中的一个sampler2D。
同时也移除了环境光颜色向量,因为环境光颜色与漫反射颜色基本一致,所以不需要分开存储。
struct Material {
sampler2D diffuse;
vec3 reflection;
float shininess;
};
...
in vec2 TexCoords;
由于纹理采样需要纹理坐标,所以需要从顶点着色器中接收一个纹理坐标,然后进行采样。
vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoord s));
其中light.diffuse表示的是环境光强度,diff表示的是光线与法线夹角的余弦值,用来衡量漫反射的多少,vec(texture(...))是对纹理的采样。
对环境光做同样操作。
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
顶点的数据也需要更新,形式为:vec3(位置)vec3(法线)vec2(纹理),例如立方体其中一面:
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
同时更新顶点着色器layout,并向片段着色器输出纹理坐标。
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
...
out vec2 TexCoords;
void main()
{
...
TexCoords = aTexCoords;
}
接着要更新两个VAO的顶点属性指针
glBindVertexArray(lightingVAO);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
glBindVertexArray(lightCubeVAO);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
镜面光贴图
在上面生成的你可能会觉得有些奇怪,木头不应该有这样强的高光,我们可以设置镜面光材质为vec3(0.0)来解决,但是又会出现一个新的问题,箱子一圈的钢圈本应该有高光的,设置完后会丢失高光,所以我们想让木头与钢圈显示不用的高光强度。
因此我们可以使用一个镜面光贴图来专门用于生成高光,我们需要一个黑白纹理来表示高光强度,如下:
镜面高光的强度可以通过每个像素的亮度来获取。镜面光贴图上的每一个像素都可以用一个颜色向量来表示,vec3(0.0)表示黑色,vec3(1.0)表示白色,这样就可以在片段着色器中将对应的颜色向量乘以光源的镜面强度,得到物体的镜面光分量,颜色越量,所得值越大。(这里假设木头没有高光,其实还是有微小高光)。
绑定纹理单元:
lightingShader.setInt("material.reflection", 1);
...
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, specularMap);
更新片段着色器的材质属性:
struct Material {
sampler2D diffuse;
sampler2D reflection;
float shininess;
};
采样镜面光贴图,计算镜面光强度
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
vec3 ref = light.ref * ref * vec3(texture(material.reflection, TexCoords));
FragColor = vec4(ambient + diffuse + ref, 1.0);
可以看到高光只在钢圈上出现。