光照&材质

什么是颜色

在搞清楚什么是光照之前,我们应该线弄清楚什么是颜色。人之所以可以看见颜色是因为这种波段的光进入了人眼,而实际上物体的颜色和光的颜色恰好是互补的,例如一个蓝色的物体恰恰是因为他吸收了除了蓝色以外的所有波段的光,从而使得蓝光反射进入到了人眼。

什么是光照

之前我们之所以没有考虑光照,实际上是对光照做了一个简化,我们在片段着色器计算出像素点的颜色后便直接将其应用到了物体上,也就是说这个点反射了100%的光线。然后对于实际而言这是不可能的。现实的物理场景是十分复杂的,例如光打在物体上会反射,会形成阴影,还会形成高光等等,同时物体可能不止接受来自一个光源的光线,甚至不止接受光源的光线,可能还有其它物体的反射,来自环境本身的光等等。如果将这些条件都考虑进去,我们渲染出来的画面当然会无比接近真实,但是同样的算力要求也会大大提升。所以我们有必要从最简单的光照模型开始学习。

Phong模型

Phong模型是最简单的一种光照模型,可以将其简单拆分成三部分:环境光、漫反射光和镜面反射光。

  • 环境光:在日常生活中,物体不可能显示出绝对的黑色,也即是无论在什么条件都可以看清物体的一点轮廓,而勾勒出这个轮廓的光我们便称之为环境光。环境光可以用环境光系数乘以光源得到。
1
2
float ambienStrength=0.1f;
vec3 ambienLight=ambienStrength*lightColor;
  • 漫反射光:漫反射我们可以理解为,光线通过漫反射会被等强度地反射到四面八方,因此,漫反射的光强只和光线的入射角有关。因此我们只要求出光线和法线之间夹角的余弦值,然后将此作为漫反射的强度即可。
1
2
3
4
vec3 norm=normalize(Normal);
vec3 lightDir=normalize(lightPos-Pos);
float diff=max(dot(norm,lightDir),0.f);
vec3 diffuse=diff*lightColor;

注意这里计算diff的时候用到了max函数,这是为了防止当光线和法线夹角出现钝角时变为负值。

  • 镜面反射:如果漫反射只是和入射角有关,那么镜面反射则是和反射光与观察者之间的夹角有关。这其实也很好理解,镜面反射就是物体身上的“高光”,这种高光只有在特定角度才可以被观察到。而最终还会将结果求N次幂,这是因为高光往往只有在很小的范围才可以被看见,所以当N越大,最后形成的高光区也越小,同时亮度也越大,从而表现出一定的“金属性”。
1
2
3
4
5
float mirrorStrenght=0.5f;
vec3 viewDir=normalize(viewPos-Pos);
vec3 reflectDir=normalize(reflect(-lightDir,norm));
float mir=pow(max(dot(viewDir,reflectDir),0.f),32);
vec3 mirror=mirrorStrenght*mir*lightColor;

这里会发现,还多加了一个mirrorStrenght的参数,这是镜面反射强度,这是为了防止镜面反射过亮造成不真实感。最后将三种光照叠加在一起,再结合上物体本来的颜色,就会得到该点最终渲染出来的颜色vec3 result=(ambienLight+diffuse+mirror)*objectColor

材质

如果我们对物体的颜色针对不同的光照分出不同的颜色,那么只要参数调整的得当,我们是不是可以模拟世界上任何一种材质了。因此材质的概念也就呼之欲出了。

1
2
3
4
5
6
7
8
struct Material{

vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;

};

设定完了材质,我们对光也可以做出对应的修改,因为对于不同的光,物体反射出来的效果也应该是不同的,所以可以这样定义光源。

1
2
3
4
5
6
7
8
struct Light{

vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;

};

这样,我们再根据之前的phong模型进行计算就可以得出不同的光照条件下物体所呈现的颜色了。

1
2
3
4
5
6
7
8
9
10
11
12
vec3 ambientLight=light.ambient*material.ambient;
vec3 lightDir=normalize(light.position-Pos);
vec3 norm=normalize(Normal);
float diff=max(dot(lightDir,norm),0.f);
vec3 diffLight=light.diffuse*diff*material.ambient;
vec3 viewDir=normalize(ViewPos-Pos);
vec3 reflectLight=normalize(reflect(-lightDir,norm));
float specular=pow(max(dot(reflectLight,viewDir),0.f),material.shininess);
vec3 specularLight=light.specular*specular*material.specular;
vec3 result=ambientLight+diffLight+specularLight;

FragColor=vec4(result,1.f);

我们可以定义不同的ambiendiffusespecular来模仿不同的材质,这些分别是铬、金和珍珠的材质