材质
在图形学中,材质表示了光线如何和物体进行交互,有了解过 BRDF 的话,实际上 Material == BRDF,有关材质的讲述,可以在这篇文章中了解:计算机图形学(六)——材质 - 知乎 (zhihu.com)。
针对不同的物体,我们可以定义不同的材质属性来进行区分,以此来获得更加真实的效果。
一、添加材质属性
当描述一个表面时,我们可以分别为三个光照分量定义一个材质颜色(Material Color):环境光照(Ambient Lighting)、漫反射光照(Diffuse Lighting)和镜面光照(Specular Lighting)。另外再添加一个反光度(shininess)来控制物体的高光半径,一般来说金属具有更高的反光度。
struct Material
{
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
uniform Material u_material;
ambient材质向量定义了在环境光照下这个表面反射的是什么颜色,通常与表面的颜色相同。diffuse材质向量定义了在漫反射光照下表面的颜色。漫反射颜色(和环境光照一样)也被设置为我们期望的物体颜色。specular材质向量设置的是表面上镜面高光的颜色(或者甚至可能反映一个特定表面的颜色)。最后,shininess影响镜面高光的散射/半径。
二、设置材质
在片元着色器中创建了材质结构体并且创建了全局变量material 之后,需要对其属性进行指定:
box_shader.set_vec3("material.ambient", glm::vec3(1.0f, 0.5f, 0.31f));
box_shader.set_vec3("material.diffuse", glm::vec3(1.0f, 0.5f, 0.31f));
box_shader.set_vec3("material.specular", glm::vec3(0.5f, 0.5f, 0.5f));
box_shader.set_float("material.shininess", 35.0f);
然后在 shader 中进行计算:
vec3 ambient = light_color * material.ambient;
vec3 normal = normalize(v_normal);
vec3 light_dir = normalize(light_pos - v_world_pos);
vec3 diffuse_color = light_color * max(0.0, dot(normal, light_dir)) * material.diffuse;
vec3 view_dir = normalize(view_pos - v_world_pos);
vec3 reflect_dir = reflect(-light_dir, normal);
vec3 specular_color = light_color * pow(max(dot(view_dir, reflect_dir), 0.0), material.shininess) * material.specular;
vec3 color = ambient + diffuse_color + specular_color;
运行查看结果:
可以看到得到的立方体的亮度太亮了,这是因为环境光、漫反射和镜面光这三个颜色都设置为 vec3(1.0f),显然是有问题的,正确的做法是为每个光照分量分别指定一个强度向量。
三、添加光源属性
和物体的材质一样,我们同样在shader中创建一个 Light 结构体来指定光源的属性:
struct Light
{
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
一个光源对它的ambient、diffuse和specular光照分量有着不同的强度。环境光照通常被设置为一个比较低的强度,因为我们不希望环境光颜色太过主导。光源的漫反射分量通常被设置为我们希望光所具有的那个颜色,通常是一个比较明亮的白色。镜面光分量通常会保持为vec3(1.0)
,以最大强度发光。
在循环中进行 uniform 变量指定:
box_shader.set_vec3("light.position", light_pos);
box_shader.set_vec3("light.ambient", ambient_color);
box_shader.set_vec3("light.diffuse", diffuse_color);
box_shader.set_vec3("light.specular", glm::vec3(1.0f, 1.0f, 1.0f));
之后重新进行计算:
vec3 ambient = light.ambient * material.ambient;
vec3 normal = normalize(v_normal);
vec3 light_dir = normalize(light.position - v_world_pos);
vec3 diffuse_color = light.diffuse * max(0.0, dot(normal, light_dir)) * material.diffuse;
vec3 view_dir = normalize(view_pos - v_world_pos);
vec3 reflect_dir = reflect(-light_dir, normal);
vec3 specular_color = light.specular * pow(max(dot(view_dir, reflect_dir), 0.0), material.shininess) * material.specular;
vec3 color = ambient + diffuse_color + specular_color;
得到如下结果:
如果需要查阅代码, GitHub 地址:OpenGL