按照对现实世界的模拟,我们一般可以将光源分为以下的几种:
- 点光源,
- 面光源,
- 定向光,
- 聚光灯,
- 环境光。
1)点光源
点光源的原型是灯泡,我们将其模拟成一个点。它向他的四周发射光线。而且这个光线的强度还会随着距离的增加而减弱。因此,对于一个点光源,它有如下的属性1
2
3
4
5
6
7
8
9
10
11
12
13struct Light {
vec3 position; // 光源的位置
vec3 color; // 光源的颜色
float Intensity // 光的强度
vec3 ambient;
vec3 diffuse;
vec3 specular;
float constant; // 常数衰减项
float linear; // 一次衰减项
float quadratic; // 二次衰减项
};
光线的衰减:
一般情况下,我们都用一个二次多项式来计算光线的衰减1
2float distance = length(light.position - FragPos);
float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
2)定向光
定向光模拟的是太阳光,因为光源的位置足够远,因此所有的光线均是平行光,因此他不需要定义位置信息,只需要给定方向。同时定向光不存在衰减问题。1
2
3
4
5
6
7
8
9struct Light {
vec3 direction; // 光源的位置
vec3 color; // 光源的颜色
float Intensity // 光的强度
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
3)聚光灯
聚光灯是原型是手电筒,如下图所示。只有再这个切角范围内的物体才能被照亮,而在这个角度外的则照不到。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17struct Light {
vec3 position; // 光源的位置
vec3 direction; // 光源的方向
vec3 color; // 光源的颜色
float Intensity // 光的强度
float cutOff; // 内切光角
float outerCutOff; // 外切光角
float constant; // 常数衰减项
float linear; // 一次衰减项
float quadratic; // 二次衰减项
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
聚光灯的光照计算如下所示:1
2
3
4
5
6
7
8
9float theta = dot(lightDir, normalize(-light.direction));
if(theta > light.cutOff)
{
// 执行光照计算
}
else // 否则,使用环境光,让场景在聚光之外时不至于完全黑暗
color = vec4(light.ambient * vec3(texture(material.diffuse, TexCoords)), 1.0);
}
聚光灯还有两个特殊的属性,即cutOff,outerCutOff。cutOff表示的是上面图中的切角,而outerCutOff则是为了平滑边缘,因为从照亮到完全黑暗是需要一个过渡的,这样才不突兀。
这个平滑过程如下:1
2
3float theta = dot(lightDir, normalize(-light.direction));
float epsilon = light.cutOff - light.outerCutOff;
float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);
4)面光源
面光源大多数用在光线追踪的场景里面。实际上面光源定义的是一个面积,这个面积的每个点都是一个点光源。在实际应用中,实际上是在面光源中采样使用。
5)环境光
实际上外部环境也要对物体产生光照影响,尤其是当我们使用天空盒的时候,天空盒的颜色也可以看做是环境光,这对物体也是由影响的。
参考资料:
https://learnopengl-cn.github.io/02%20Lighting/01%20Colors/