材质
一: 啥叫材质:
- 在现实世界里,每个物体会对光产生不同的反应。每个物体对镜面高光也有不同的反应。有些物体反射光的时候不会有太多的散射(Scatter),因而产生一个较小的高光点,而有些物体则会散射很多,产生一个有着更大半径的高光点。
- 如果我们想要在OpenGL中模拟多种类型的物体,我们必须为每个物体分别定义一个材质(Material)属性。
二:材质属性
- 当描述一个物体的时候,我们可以用这三个分量来定义一个材质颜色(Material Color):环境光照(Ambient Lighting)、漫反射光照(Diffuse Lighting)和镜面光照(Specular Lighting)。
- 通过为每个分量指定一个颜色,我们就能够对物体的颜色输出有着精细的控制了。现在,我们再添加反光度(Shininess)这个分量到上述的三个颜色中,这就有我们需要的所有材质属性了。
三: Material结构体
- 在片段着色器中,我们创建一个结构体(Struct)来储存物体的材质属性。我们也可以把它们储存为独立的uniform值,但是作为一个结构体来储存会更有条理一些。我们首先定义结构体的布局(Layout),然后使用刚创建的结构体为类型,简单地声明一个uniform变量。
- 我们为每个冯氏光照模型的分量都定义一个颜色向量。
2.1 ambient材质向量定义了在环境光照下这个物体反射的是什么颜色,通常这是和物体颜色相同的颜色。
2.2 diffuse材质向量定义了在漫反射光照下物体的颜色。(和环境光照一样)漫反射颜色也要设置为我们需要的物体颜色。
2.3 specular材质向量设置的是镜面光照对物体的颜色影响(或者甚至可能反射一个物体特定的镜面高光颜色)。
2.4 最后,shininess影响镜面高光的散射/半径。 - 这四个元素定义了一个物体的材质,通过它们我们能够模拟很多现实世界中的材质。
四:设置材质
- 在片段着色器中创建了一个材质结构体的uniform,所以下面我们希望修改一下光照的计算来顺应新的材质属性。由于所有材质变量都储存在结构体中,我们可以从uniform变量material中访问它们:
void main() { // 环境光 vec3 ambient = lightColor * material.ambient; // 漫反射 vec3 norm = normalize(Normal); vec3 lightDir = normalize(lightPos - FragPos); float diff = max(dot(norm, lightDir), 0.0); vec3 diffuse = lightColor * (diff * material.diffuse); // 镜面光 vec3 viewDir = normalize(viewPos - FragPos); vec3 reflectDir = reflect(-lightDir, norm); float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); vec3 specular = lightColor * (spec * material.specular); vec3 result = ambient + diffuse + specular; FragColor = vec4(result, 1.0); }
- 现在在需要的地方访问了材质结构体中的所有属性,并且这次是根据材质的颜色来计算最终的输出颜色的。物体的每个材质属性都乘上了它们对应的光照分量。
- 注意:以上的代码和上一节的代码有所不同,上一节的代码的result还需要乘以物体的颜色,但是因为这里设置了物体的材质,在每个光照分量中都乘以材质属性,所以结果result直接加起来就好了。还有一个不同就是这里没有设置环境光和镜面反射光的强度值。
- 现在可以在程序中设置适当的uniform,对物体设置材质了。
五: 光的属性
- 物体过亮的原因是环境光、漫反射和镜面光这三个颜色对任何一个光源都会去全力反射。光源对环境光、漫反射和镜面光分量也具有着不同的强度。
- 前面的教程,我们通过使用一个强度值改变环境光和镜面光强度的方式解决了这个问题。我们想做一个类似的系统,但是这次是要为每个光照分量都指定一个强度向量。
- 创建了一些光照属性来影响每个单独的光照分量。我们希望为光照属性创建一个与材质结构体类似的结构体:
- Light结构体:
4.1 一个光源对它的ambient、diffuse和specular光照有着不同的强度。环境光照通常会设置为一个比较低的强度,因为我们不希望环境光颜色太过显眼。光源的漫反射分量通常设置为光所具有的颜色,通常是一个比较明亮的白色。镜面光分量通常会保持为vec3(1.0),以最大强度发光。注意我们也将光源的位置添加到了结构体中。
4.2 和材质uniform一样,我们需要更新片段着色器:
4.3 接下来在程序中设置光照强度.setVec3vec3 ambient = light.ambient * material.ambient; vec3 diffuse = light.diffuse * (diff * material.diffuse); vec3 specular = light.specular * (spec * material.specular);
Either Excellent or Rusty