OPENGL學習筆記之十三

#OPENGL學習筆記之十三
2019/3/8

閱讀材料來自[learnopengl.com][0]以及[learnopengl-cn.github.io][1]

這一節我們解決的主要問題是實現在同一個物體上對不同材質的表面有不同的光照效果,說得簡單一點就是如下和木箱爲例,我們對物體添加紋理貼圖,同時貼圖中央的木板和邊緣金屬邊框對光照的影響存在區別。
光照貼圖
這樣一來,我們就能對物體的漫反射分量(以及間接地對環境光分量,它們幾乎總是一樣的)和鏡面光分量有着更精確的控制。

漫反射的貼圖

我們首先要複習一下紋理是如何使用的,戳這裏
這裏我們使用下圖的漫反射貼圖(Diffuse Map)
漫反射貼圖(Diffuse Map
這是第一個紋理,我們會將紋理儲存爲Material結構體中的一個sampler2D,我們將之前定義的vec3漫反射顏色向量替換爲漫反射貼圖。當然我們也可以不這麼做,把紋理定義在外邊。

注意sampler2D是所謂的不透明類型(Opaque Type),也就是說我們不能將它實例化,只能通過uniform來定義它。如果我們使用除uniform以外的方法(比如函數的參數)實例化這個結構體,GLSL會拋出一些奇怪的錯誤。這同樣也適用於任何封裝了不透明類型的結構體。

我們也移除了環境光材質顏色向量,因爲環境光顏色在幾乎所有情況下都等於漫反射顏色,所以我們不需要將它們分開儲存:

struct Material {
    sampler2D diffuse;//注意這裏不再是向量了,ambient因爲和diffuse相同我們也去掉了
    vec3      specular;
    float     shininess;
}; 
...
in vec2 TexCoords;

如果你非常固執,仍想將環境光顏色設置爲一個(漫反射值之外)不同的值,你也可以保留這個環境光的vec3,但整個物體仍只能擁有一個環境光顏色。如果想要對不同片段有不同的環境光值,你需要對環境光值單獨使用另外一個紋理。

注意我們將在片段着色器中再次需要紋理座標,所以我們聲明一個額外的輸入變量。接下來我們只需要從紋理中採樣片段的漫反射顏色值即可,且不要忘記將環境光得材質顏色設置爲漫反射材質顏色同樣的值:

//片段着色器中
vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));

//頂點着色器中
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
...
out vec2 TexCoords;

void main()
{
    ...
    TexCoords = aTexCoords;
}

//主程序中(注意別忘了還要更新VAO的頂點屬性指針)
lightingShader.setInt("material.diffuse", 0);
...
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, diffuseMap);

鏡面光貼圖

我們使用一個專門用於鏡面高光的紋理貼圖,和之前的紋理做一個相加。下面是一個鏡面光貼圖(Specular Map)
鏡面光貼圖(Specular Map
鏡面高光的強度可以通過圖像每個像素的亮度來獲取。鏡面光貼圖上的每個像素都可以由一個顏色向量來表示,比如說黑色代表顏色向量vec3(0.0),灰色代表顏色向量vec3(0.5)。在片段着色器中,我們接下來會取樣對應的顏色值並將它乘以光源的鏡面強度。一個像素越「白」,乘積就會越大,物體的鏡面光分量就會越亮。
由於箱子大部分都由木頭所組成,而且木頭材質應該沒有鏡面高光,所以漫反射紋理的整個木頭部分全部都轉換成了黑色。箱子鋼製邊框的鏡面光強度是有細微變化的,鋼鐵本身會比較容易受到鏡面高光的影響,而裂縫則不會。

從實際角度來說,木頭其實也有鏡面高光,儘管它的反光度(Shininess)很小(更多的光被散射),影響也比較小,但是爲了教學目的,我們可以假設木頭不會對鏡面光有任何反應。

由於我們正在同一個片段着色器中使用另一個紋理採樣器,我們必須要對鏡面光貼圖使用一個不同的紋理單元

//主程序中
lightingShader.setInt("material.specular", 1);
...
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, specularMap);

//片段着色器中
struct Material {
    sampler2D diffuse;
    sampler2D specular;//更新鏡面屬性,不再是一個vec3了
    float     shininess;
};

vec3 ambient  = light.ambient  * vec3(texture(material.diffuse, TexCoords));
vec3 diffuse  = light.diffuse  * diff * vec3(texture(material.diffuse, TexCoords));  
vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
FragColor = vec4(ambient + diffuse + specular, 1.0);

通過使用鏡面光貼圖我們可以可以對物體設置大量的細節,比如物體的哪些部分需要有閃閃發光的屬性,我們甚至可以設置它們對應的強度。鏡面光貼圖能夠在漫反射貼圖之上給予我們更高一層的控制。

如果你想另闢蹊徑,你也可以在鏡面光貼圖中使用真正的顏色,不僅設置每個片段的鏡面光強度,還設置了鏡面高光的顏色。從現實角度來說,鏡面高光的顏色大部分(甚至全部)都是由光源本身所決定的,所以這樣並不能生成非常真實的視覺效果(這也是爲什麼圖像通常是黑白的,我們只關心強度)。

通過使用漫反射和鏡面光貼圖,我們可以給相對簡單的物體添加大量的細節。我們今後甚至可以使用法線/凹凸貼圖(Normal/Bump Map)或者反射貼圖(Reflection Map)給物體添加更多的細節。

如果正確的操作就能夠獲得開頭片面的效果,參考代碼和更新後的頂點數據戳這裏

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章