现实世界的光照是 极其复杂 的,而且会受到诸多因素的影响,这是我们有限的计算能力所 无法模拟 的。因此OpenGL的光照使用的是 简化的模型,对现实的情况进行近似
冯氏光照模型的主要结构由3个分量组成:环境(Ambient)、漫反射(Diffuse)和镜面(Specular)光照
光通常都不是来自于同一个光源,而是来自于我们周围分散的很多光源,即使它们可能并不是那么显而易见。光的一个属性是,它可以向很多方向发散并反弹,从而能够到达不是非常直接临近的点。所以,光能够在其它的表面上反射,对一个物体产生间接的影响.这种算法既开销高昂又极其复杂
一个 简化 的全局照明模型,
使用一个很小的常量(光照)颜色,添加到物体片段的最终颜色中,这样子的话即便场景中没有直接的光源也能看起来存在有一些发散的光
void main()
{
float ambientStrength = 0.1;
vec3 ambient = ambientStrength * lightColor;
vec3 result = ambient * objectColor;
FragColor = vec4(result, 1.0);
}
如果光线 垂直于物体表面,这束光对物体的影响会 最大化(译注:更亮)。为了测量光线和片段的角度,我们使用一个叫做 法向量(Normal Vector) 的东西,它是垂直于片段表面的一个向量(这里以黄色箭头表示)这两个向量之间的角度很容易就能够通过点乘计算出来。
法向量是一个垂直于顶点表面的(单位)向量。由于顶点本身并没有表面(它只是空间中一个独立的点),我们利用它周围的顶点来计算出这个顶点的表面。我们能够使用一个小技巧,使用叉乘对立方体所有的顶点计算法向量
顶点着色器加入法向量
out vec3 FragPos;
out vec3 Normal;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
FragPos = vec3(model * vec4(aPos, 1.0));
Normal = aNormal;
}
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;
有了环境光分量和漫反射分量,我们把它们相加,然后把结果乘以物体的颜色,来获得片段最后的输出颜色
vec3 result = (ambient + diffuse) * objectColor;
FragColor = vec4(result, 1.0);
我们通过反射法向量周围光的方向来计算反射向量。然后我们计算反射向量和视线方向的角度差,如果夹角越小,那么镜面光的影响就会越大。它的作用效果就是,当我们去看光被物体所反射的那个方向的时候,我们会看到一个 高光
float specularStrength = 0.5;
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor;
32是高光的 反光度(Shininess) 。一个物体的反光度越高,反射光的能力越强,散射得越少,高光点就会越小
最终结果
vec3 result = (ambient + diffuse + specular) * objectColor;
FragColor = vec4(result, 1.0);