光照基础

1)为什么物体有颜色

现实世界中,为什么我们能看到五颜六色的物体,为什么白天我们能看到五颜六色的物体,而到了夜晚,就基本都是黑的呢?这是因为光照射到物体上,物体吸收了部分波段的光,而不能被吸收的光会被反射,我们的眼睛接受到了这些反射的光,这些反射光的颜色就是我们看到的物体的颜色。所以说实际上物体本身没有颜色,是对光的吸收与反射让我们感受到了物体的颜色。
Light1
物体的红色的,是因为物体吸收了所有非红色的光,反射了红色的光导致的(这么说实际上不太准确,此处做简单的说明)

2)颜色的模拟

显示世界中,要模拟光照比较困难,这是因为光是连续的波段,但是在计算机中,我们可以用简单的三原色来模拟光,也就是rgb。
因此我们可以这样定义一个物体的颜色:物体对光源的反射程度。

1
2
3
glm::vec3 lightColor(1.0f, 1.0f, 1.0f); // 表示光,这里是白光
glm::vec3 toyColor(1.0f, 0.5f, 0.31f); // 表示物体对三原色的反射程度
glm::vec3 result = lightColor * toyColor; // 最终计算的结果就是物体的颜色

有了上面的基础,我们就可以对物体的光照效果进行模拟了。

3)Lambert光照模型

有了上面的基础,我们就可以对物体的光照效果进行模拟了。

光源照射到物体表面后,会向四面八方反射,产生的效果我们称之为漫反射,这是一种理想的漫反射光照模型。
Light2
而根据lambert定律,光照射到反射体上时,漫反射的光的强度与入射光的强度以及入射光与入射点的发现的余弦成正比,即

1
2
cosθ = L*N;
diffuse = I*cosθ

因此对于lambert光照模型,物体的颜色就是

1
diffuse = I*cosθ

4)Phong模型

上面的公式可以计算出物体的颜色(漫反射),但是这个模型太简单了,跟现实世界相差太远,我们需要更加精细的模型来模拟光照效果。

1975年,phong提出了phong light model。冯氏光照模型的主要结构由3个分量组成:环境(Ambient)、漫反射(Diffuse)和镜面(Specular)光照。

环境光照(Ambient Lighting):即使在黑暗的情况下,世界上通常也仍然有一些光亮(月亮、远处的光),所以物体几乎永远不会是完全黑暗的。为了模拟这个,我们会使用一个环境光照常量,它永远会给物体一些颜色。
漫反射光照(Diffuse Lighting):模拟光源对物体的方向性影响(Directional Impact)。它是冯氏光照模型中视觉上最显著的分量。物体的某一部分越是正对着光源,它就会越亮。
镜面光照(Specular Lighting):模拟有光泽物体上面出现的亮点。镜面光照的颜色相比于物体的颜色会更倾向于光的颜色。

  • 环境光的计算:
    由于环境光模拟的是整个环境对物体的影响,因此可以用光的颜色乘以一个很小的常量环境因子,再乘以物体的颜色来表示:

    1
    vec3 ambient = ambientStrength * lightColor * objectColor
  • 漫反射光照:
    跟lambert模型的计算方式一致,漫反射的光的强度与入射光的强度以及入射光与入射点的发现的余弦成正比,计算结果如下:

    1
    vec3 diffuse = max(dot(norm, lightDir), 0.0) * lightColor * objectColor;
  • 镜面光照:
    在现实世界中,当物体表面非常光滑,向镜子一样时,这是当光打到上面时,我们看到的是一片高光(甚至会闪的眼睛睁不开),而不只是漫反射的效果。因此相对于Lambert模型,Phong的最大贡献就是引入了镜面高光的计算.
    Light3
    镜面光照会执行镜面反射,当我们的眼睛正好对着反射的方向时,反射的光强度最大,而当我们看的方向远离反射光的方向时,感受到的光的强度越小,因此镜面高光的强度跟反射方向以及观察方向的角度余弦成正比,也就是角度越大,光照越强。

    1
    vec3 specular = specularStrength * pow(max(dot(viewDir, reflectDir), 0.0), shininess); * lightColor * objectColor;

specularStrength表示镜面的反射度,表示镜面反射多少的强度的光
viewDir 观察者的方向
reflectDir 光线反射方向
shininess 反光度,影响镜面高光的散射/半径

因此我们的phong模型的光照模型

1
result = ambient + diffuse + specular;

5) phong模型材质信息

上面我们可以看到,我们每个分量的计算都要乘以objectColor,这个就是物体的反射的颜色,这个我么你可以理解为物体的材质信息。而在实际应用中,这三个值是可以不一样的,比如我们的phong材质模型,我们需要对每一个分量定义不同的材质值。如下所示:

1
2
3
4
5
struct Material {
vec3 ambient;
vec3 diffuse;
vec3 specular;
};

而ambient和diffuse实际上他们可以统一,因为ambient实际上可以看成是diffuse的一个基础值。而镜面光照出还有个参数shininess,因此我们可以有如下的材质定义

1
2
3
4
5
struct Material {
vec3 diffuse;
vec3 specular;
float shininess;
};

6)光的属性

上面我们对objectColor做了细分,实际上对于lightColor也是可以按照上面的三因素模型做细分,这个用来表示物体对模型在三个分量上的反射的强度信息。

1
2
3
4
5
6
7
struct Light {
vec3 position;

vec3 ambient;
vec3 diffuse;
vec3 specular;
};

环境光照通常会设置为一个比较低的强度,因为我们不希望环境光颜色太过显眼。
光源的漫反射分量通常设置为光所具有的颜色,通常是一个比较明亮的白色。
镜面光分量通常会保持为vec3(1.0),以最大强度发光。

7)光照贴图

在现实中,每个物体的每个顶点的材质信息都可能是不同的,因此实际上我们是用一张类似于纹理图像的贴图来表示每个分量的材质信息,这个就被称作为光照贴图。
一般有镜面光贴图和漫反射贴图。

8)参考资料:

https://learnopengl-cn.github.io/02%20Lighting/01%20Colors/

注意:上面的逻辑是Phong光照模型,对于其他的模型,是不适用的。其他的模型有自身的属性参数,谨记。