Vulkan_Shader_Day04—光照(光照貼圖_Lighting maps)

光照貼圖

在上一節中,我們討論了讓每個物體都擁有自己獨特的材質從而對光照做出不同的反應的方法。這樣子能夠很容易在一個光照的場景中給每個物體一個獨特的外觀,但是這仍不能對一個物體的視覺輸出提供足夠多的靈活性。

在上一節中,我們將整個物體的材質定義爲一個整體,但現實世界中的物體通常並不只包含有一種材質,而是由多種材質所組成。想想一輛汽車:它的外殼非常有光澤,車窗會部分反射周圍的環境,輪胎不會那麼有光澤,所以它沒有鏡面高光,輪轂非常閃亮(如果你洗車了的話)。汽車同樣會有漫反射和環境光顏色,它們在整個物體上也不會是一樣的,汽車有着許多種不同的環境光/漫反射顏色。總之,這樣的物體在不同的部件上都有不同的材質屬性。

所以,上一節中的那個材質系統是肯定不夠的,它只是一個最簡單的模型,所以我們需要拓展之前的系統,引入漫反射和鏡面光貼圖(Map)。這允許我們對物體的漫反射分量(以及間接地對環境光分量,它們幾乎總是一樣的)和鏡面光分量有着更精確的控制。

我們希望通過某種方式對物體的每個片段單獨設置漫反射顏色。有能夠讓我們根據片段在物體上的位置來獲取顏色值得系統嗎?

這可能聽起來很熟悉,而且事實上這個系統我們已經使用很長時間了。這聽起來很像在之前教程中詳細討論過的紋理,而這基本就是這樣:一個紋理。我們僅僅是對同樣的原理使用了不同的名字:其實都是使用一張覆蓋物體的圖像,讓我們能夠逐片段索引其獨立的顏色值。在光照場景中,它通常叫做一個漫反射貼圖(Diffuse Map)(3D藝術家通常都這麼叫它),它是一個表現了物體所有的漫反射顏色的紋理圖像。

爲了演示漫反射貼圖,我們將會使用下面的圖片,它是一個有鋼邊框的木箱:
在這裏插入圖片描述
在着色器中使用漫反射貼圖的方法和紋理教程中是完全一樣的。
首先,我們先設置下光源位置及相機視角,方便我們能夠更好地看到效果:

glm::vec3 lightPos= glm::vec3(0.0f, 0.0f, 1.0f);
Camera camera(glm::vec3(0.0f, 1.5f, 2.0f), glm::radians(-35.0f), glm::radians(180.0f), glm::vec3(0.0f, 1.0f, 0.0f));

在片元着色器中,我們使用使用紋理貼圖

void main() {
    outColor = texture(texSampler, fragTexCoord)* vec4(fragBaseLight, 1.0);
}

運行後可以看到:
在這裏插入圖片描述

一、環境光貼圖

我們要移除環境光材質顏色向量,因爲環境光顏色在幾乎所有情況下都等於漫反射顏色。

    //map Ambient Lighting
    vec3 ambient = ambientStrength * fragBaseLight * lightAmbient;

    vec3 result = ambient;
    outColor = texture(texSampler, fragTexCoord)* vec4(result, 1.0);
}

編譯後運行,
在這裏插入圖片描述

二、漫反射貼圖

接下來我們在片元着色器中設置漫反射:

void main() {
    //map Ambient Lighting
    vec3 ambient = ambientStrength * fragBaseLight ;

	//map Diffuse Lighting
	vec3 norm = normalize(fragNormal);
    vec3 lightDir = normalize(lightPos - fragPos);
	float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = diff * fragBaseLight;
    
   vec3 result = ambient + diffuse ;
    outColor = texture(texSampler, fragTexCoord)* vec4(result, 1.0);
}

運行如下圖:
在這裏插入圖片描述

三、高光反射貼圖

接下來我們在片元着色器中再次疊加鏡面光照:

void main() {
    //map Ambient Lighting
    vec3 ambient = ambientStrength * fragBaseLight ;

	//map Diffuse Lighting
	vec3 norm = normalize(fragNormal);
    vec3 lightDir = normalize(lightPos - fragPos);
	float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = diff * fragBaseLight;
	
	//map Specular Lighting
	vec3 viewDir = normalize(viewPos - fragPos);
    vec3 reflectDir = reflect(-lightDir, norm);
	float spec = pow(max(dot(viewDir, reflectDir), 0.0), m_shininess);
    vec3 specular = spec * fragBaseLight * lightSpecular;

    vec3 result = ambient + diffuse + specular;
    outColor = texture(texSampler, fragTexCoord)* vec4(result, 1.0);
}

細節再一次得到驚人的提升,這次箱子有了光照開始閃閃發光(字面意思也是)了。你的箱子看起來可能像這樣:
在這裏插入圖片描述

當然,您也可以加上環境光參數運行看看效果

四、金屬邊框鏡面光貼圖

你可能會注意到,鏡面高光看起來有些奇怪,因爲我們的物體大部分都是木頭,我們知道木頭不應該有這麼強的鏡面高光的。我們可以將物體的鏡面光材質設置爲vec3(0.0)來解決這個問題,但這也意味着箱子鋼製的邊框將不再能夠顯示鏡面高光了,我們知道鋼鐵應該是有一些鏡面高光的。所以,我們想要讓物體的某些部分以不同的強度顯示鏡面高光。這個問題看起來和漫反射貼圖非常相似。是巧合嗎?我想不是。

我們同樣可以使用一個專門用於鏡面高光的紋理貼圖。這也就意味着我們需要生成一個黑白的(如果你想得話也可以是彩色的)紋理,來定義物體每部分的鏡面光強度。下面是一個鏡面光貼圖(Specular Map)的例子:
在這裏插入圖片描述

鏡面高光的強度可以通過圖像每個像素的亮度來獲取。鏡面光貼圖上的每個像素都可以由一個顏色向量來表示,比如說黑色代表顏色向量vec3(0.0),灰色代表顏色向量vec3(0.5)。在片段着色器中,我們接下來會取樣對應的顏色值並將它乘以光源的鏡面強度。一個像素越「白」,乘積就會越大,物體的鏡面光分量就會越亮。

由於箱子大部分都由木頭所組成,而且木頭材質應該沒有鏡面高光,所以漫反射紋理的整個木頭部分全部都轉換成了黑色。箱子鋼製邊框的鏡面光強度是有細微變化的,鋼鐵本身會比較容易受到鏡面高光的影響,而裂縫則不會。
使用Photoshop或Gimp之類的工具,將漫反射紋理轉換爲鏡面光紋理還是比較容易的,只需要剪切掉一些部分,將圖像轉換爲黑白的,並增加亮度/對比度就好了。用戶有興趣可以自己嘗試新增一個採樣用於處理邊框嘗試。

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