材质
不同的物体对光的反射效果也不同,这里用材质来描述这种性质。
struct Material {
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
材质的定义是与光照模型的定义相对应的,按照之前使用的冯氏光照模型,物体颜色的组成主要有四个部分:环境光照、漫反射光照,镜面光照以及反光度。根据上述规则可以定义出对应该模型的材质的结构体。
实际实践中,环境光照一般都取与反射光照相同的颜色,因此可以不用添加,如果你希望的话,也可以保留。而为了更加精细的对物体的每个片段单独设置颜色,可以使用漫反射纹理贴图与镜面反射纹理贴图来进行控制,替代直接使用分量全局执行。
镜面反射纹理贴图是一个专门用于镜面高光的纹理贴图。它是一个黑白的(如果你想得话也可以是彩色的)纹理,来定义物体每部分的镜面光强度。在镜面反射纹理贴图中,一个像素越「白」,物体的镜面光分量就会越亮。如图所示是一个镜面反射纹理贴图:
使用Photoshop或Gimp之类的工具,将漫反射纹理转换为镜面光纹理还是比较容易的,只需要剪切掉一些部分,将图像转换为黑白的,并增加亮度/对比度就好了。
因此,进一步可以将材料结构体定义如下:
struct Material {
sampler2D diffuse;
sampler2D specular;
float shininess;
};
in vec2 TexCoords;
uniform Material material;
这里使用纹理的方式与之前基本完全相同。而由于这里还需要纹理坐标,因此和之前的类似,还需要传入纹理坐标。
接着,从CPU程序中通过uniform将材质传入。注意,从外面传结构体时,需要对结构体的每个变量单独传值。具体实现如下:
shader.SetUniform1i("material.specular", 1);
shader.SetUniform1f("material.shininess", 25.0f);
shader.SetUniform1i("material.diffuse", 0);
光源
影响物体表现的除了物体本身,当然还有光源的情况。同样的,对应的光源属性也有四个,用于控制这四种光照对物体的影响。
struct Light {
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform Light light;
这里将其定义为向量形式即可,因为这里光源本身可以视作是一个整体。
同样,将对应值通过uniform传入即可。
那么,根据上面定义好的物理材质和光源属性,就可以得到对应的光照情况下的物体了。基本上就是在之前的光照模型的基础上稍加改动,将固定值改为了传入的材料和光源属性。
void main()
{
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(LightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
vec3 viewDir = normalize(-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);
}
加入了上述结构后,可以使得程序显示出一个更加真实的光照情况。最终效果如下:
参考资料
[1] https://learnopengl-cn.readthedocs.io/zh/latest/01%20Getting%20started/03%20Hello%20Window/