光源分析

按照对现实世界的模拟,我们一般可以将光源分为以下的几种:

  • 点光源,
  • 面光源,
  • 定向光,
  • 聚光灯,
  • 环境光。

1)点光源

点光源的原型是灯泡,我们将其模拟成一个点。它向他的四周发射光线。而且这个光线的强度还会随着距离的增加而减弱。因此,对于一个点光源,它有如下的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
struct Light {
vec3 position; // 光源的位置
vec3 color; // 光源的颜色
float Intensity // 光的强度

vec3 ambient;
vec3 diffuse;
vec3 specular;

float constant; // 常数衰减项
float linear; // 一次衰减项
float quadratic; // 二次衰减项
};

光线的衰减:
一般情况下,我们都用一个二次多项式来计算光线的衰减

1
2
float 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
9
struct Light {
vec3 direction; // 光源的位置
vec3 color; // 光源的颜色
float Intensity // 光的强度

vec3 ambient;
vec3 diffuse;
vec3 specular;
};

3)聚光灯

聚光灯是原型是手电筒,如下图所示。只有再这个切角范围内的物体才能被照亮,而在这个角度外的则照不到。
Light4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct 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
9
float 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则是为了平滑边缘,因为从照亮到完全黑暗是需要一个过渡的,这样才不突兀。

Light5

Light6

这个平滑过程如下:

1
2
3
float 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/