材質
在圖形學中,材質表示了光線如何和物體進行互動,有了解過 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