在真實(shí)世界里,每個(gè)物體會(huì)對(duì)光產(chǎn)生不同的反應(yīng)。鋼看起來(lái)比陶瓷花瓶更閃閃發(fā)光喳资,一個(gè)木頭箱子不會(huì)像鋼箱子一樣對(duì)光產(chǎn)生很強(qiáng)的反射。每個(gè)物體對(duì)鏡面高光也有不同的反應(yīng)腾供。有些物體不會(huì)散射(Scatter)很多光卻會(huì)反射(Reflect)很多光仆邓,結(jié)果看起來(lái)就有一個(gè)較小的高光點(diǎn)(Highlight),有些物體散射了很多伴鳖,它們就會(huì)產(chǎn)生一個(gè)半徑更大的高光节值。如果我們想要在OpenGL中模擬多種類型的物體,我們必須為每個(gè)物體分別定義材質(zhì)(Material)屬性榜聂。
在前面的教程中搞疗,我們指定一個(gè)物體和一個(gè)光的顏色來(lái)定義物體的圖像輸出,并使之結(jié)合環(huán)境(Ambient)和鏡面強(qiáng)度(Specular Intensity)元素须肆。當(dāng)描述物體的時(shí)候匿乃,我們可以使用3種光照元素:環(huán)境光照(Ambient Lighting)、漫反射光照(Diffuse Lighting)豌汇、鏡面光照(Specular Lighting)定義一個(gè)材質(zhì)顏色幢炸。通過(guò)為每個(gè)元素指定一個(gè)顏色,我們已經(jīng)對(duì)物體的顏色輸出有了精密的控制【芗現(xiàn)在把一個(gè)鏡面高光元素添加到這三個(gè)顏色里宛徊,這是我們需要的所有材質(zhì)屬性:
#version 330 core
struct Material
{
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
uniform Material material;
在片段著色器中,我們創(chuàng)建一個(gè)結(jié)構(gòu)體(Struct)逻澳,來(lái)儲(chǔ)存物體的材質(zhì)屬性闸天。我們也可以把它們儲(chǔ)存為獨(dú)立的uniform值,但是作為一個(gè)結(jié)構(gòu)體來(lái)儲(chǔ)存可以更有條理斜做。我們首先定義結(jié)構(gòu)體的布局苞氮,然后簡(jiǎn)單聲明一個(gè)uniform變量,以新創(chuàng)建的結(jié)構(gòu)體作為它的類型陨享。
就像你所看到的葱淳,我們?yōu)槊總€(gè)馮氏光照模型的元素都定義一個(gè)顏色向量。ambient材質(zhì)向量定義了在環(huán)境光照下這個(gè)物體反射的是什么顏色抛姑;通常這是和物體顏色相同的顏色赞厕。diffuse材質(zhì)向量定義了在漫反射光照下物體的顏色。漫反射顏色被設(shè)置為(和環(huán)境光照一樣)我們需要的物體顏色定硝。specular材質(zhì)向量設(shè)置的是物體受到的鏡面光照的影響的顏色(或者可能是反射一個(gè)物體特定的鏡面高光顏色)皿桑。最后,shininess影響鏡面高光的散射/半徑蔬啡。
這四個(gè)元素定義了一個(gè)物體的材質(zhì)诲侮,通過(guò)它們我們能夠模擬很多真實(shí)世界的材質(zhì)。這里有一個(gè)列表devernay.free.fr展示了幾種材質(zhì)屬性箱蟆,這些材質(zhì)屬性模擬外部世界的真實(shí)材質(zhì)沟绪。下面的圖片展示了幾種真實(shí)世界材質(zhì)對(duì)我們的立方體的影響:
如你所見,正確地指定一個(gè)物體的材質(zhì)屬性空猜,似乎就是改變我們物體的相關(guān)屬性的比例绽慈。效果顯然很引人注目,但是對(duì)于大多數(shù)真實(shí)效果辈毯,我們最終需要更加復(fù)雜的形狀坝疼,而不單單是一個(gè)立方體。在下面的教程中谆沃,我們會(huì)討論更復(fù)雜的形狀钝凶。
讓我們?cè)囋囋谥髦袑?shí)現(xiàn)這樣的一個(gè)材質(zhì)系統(tǒng)。
設(shè)置材質(zhì)
我們?cè)谄沃髦袆?chuàng)建了一個(gè)uniform材質(zhì)結(jié)構(gòu)體唁影,所以下面我們希望改變光照計(jì)算來(lái)順應(yīng)新的材質(zhì)屬性耕陷。由于所有材質(zhì)元素都儲(chǔ)存在結(jié)構(gòu)體中,我們可以從uniform變量material取得它們:
void main()
{
// Ambient
vec3 ambient = lightColor * material.ambient;
// Diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse =lightColor * (diff * material.diffuse);
// Specular
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0f), material.shininess);
vec3 specular = lightColor * (spec * material.specular);
vec3 result = ambient + diffuse + specular;
color = vec4(result, 1.0f);
}
你可以看到据沈,我們現(xiàn)在獲得所有材質(zhì)結(jié)構(gòu)體的屬性啃炸,無(wú)論在哪兒我們都需要它們,這次通過(guò)材質(zhì)顏色的幫助卓舵,計(jì)算結(jié)果輸出的顏色南用。物體的每個(gè)材質(zhì)屬性都乘以它們對(duì)應(yīng)的光照元素。
通過(guò)設(shè)置適當(dāng)?shù)膗niform掏湾,我們可以在應(yīng)用中設(shè)置物體的材質(zhì)裹虫。當(dāng)設(shè)置uniform時(shí),GLSL中的一個(gè)結(jié)構(gòu)體并不會(huì)被認(rèn)為有什么特別之處融击。一個(gè)結(jié)構(gòu)體值扮演uniform變量的封裝體筑公,所以如果我們希望填充這個(gè)結(jié)構(gòu)體,我們就仍然必須設(shè)置結(jié)構(gòu)體中的各個(gè)元素的uniform值尊浪,但是這次帶有結(jié)構(gòu)體名字作為前綴:
// 設(shè)置uniform Material material(GLSL結(jié)構(gòu)體)
GLint matAmbientLoc = glGetUniformLocation (lightingShader.Program, "material.ambient");
GLint matDiffuseLoc = glGetUniformLocation (lightingShader.Program, "material.diffuse");
GLint matSpecularLoc = glGetUniformLocation (lightingShader.Program, "material.specular");
GLint matShininessLoc = glGetUniformLocation (lightingShader.Program, "material.shininess");
glUniform3f (matAmbientLoc, 1.0f, 0.5f, 0.31f);
glUniform3f (matDiffuseLoc, 1.0f, 0.5f, 0.31f);
glUniform3f (matSpecularLoc, 0.5f, 0.5f, 0.5f);
glUniform1f (matShininessLoc, 32.0f);
我們將ambient和diffuse元素設(shè)置成我們想要讓物體所呈現(xiàn)的顏色匣屡,設(shè)置物體的specular元素為中等亮度顏色封救;我們不希望specular元素對(duì)這個(gè)指定物體產(chǎn)生過(guò)于強(qiáng)烈的影響。我們同樣設(shè)置shininess為32捣作。我們現(xiàn)在可以簡(jiǎn)單的在應(yīng)用中影響物體的材質(zhì)誉结。
運(yùn)行程序,會(huì)得到下面這樣的結(jié)果:
看起來(lái)很奇怪不是嗎券躁?
光的屬性
這個(gè)物體太亮了惩坑。物體過(guò)亮的原因是環(huán)境、漫反射和鏡面三個(gè)顏色任何一個(gè)光源都會(huì)去全力反射也拜。我們想做一個(gè)相同的系統(tǒng)以舒,但是這次為每個(gè)光照元素指定強(qiáng)度向量。如果我們想象lightColor是vec3(1.0)慢哈,代碼看起來(lái)像是這樣:
vec3 ambient = vec3(1.0f) * material.ambient;
vec3 diffuse = vec3(1.0f) * (diff * material.diffuse);
vec3 specular = vec3(1.0f) * (spec * material.specular);
所以物體的每個(gè)材質(zhì)屬性返回了每個(gè)光照元素的全強(qiáng)度蔓钟。這些vec3(1.0)值可以各自獨(dú)立的影響各個(gè)光源,這通常就是我們想要的卵贱。我們要設(shè)置光的ambient亮度為一個(gè)小一點(diǎn)的值奋刽,從而限制環(huán)境色:
`
vec3 result = vec3(0.1f) * material.ambient;
我們可以用同樣的方式影響光源diffuse和specular的強(qiáng)度。這和我們前面教程所做的極為相似艰赞;你可以說(shuō)我們已經(jīng)創(chuàng)建了一些光的屬性來(lái)各自獨(dú)立地影響每個(gè)光照元素佣谐。我們希望為光的屬性創(chuàng)建一些與材質(zhì)結(jié)構(gòu)體相似的東西:
struct Light
{
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform Light light;
一個(gè)光源的ambient、diffuse和specular光都有不同的亮度方妖。環(huán)境光通常設(shè)置為一個(gè)比較低的亮度狭魂,因?yàn)槲覀儾幌Mh(huán)境色太過(guò)顯眼。光源的diffuse元素通常設(shè)置為我們希望光所具有的顏色党觅;經(jīng)常是一個(gè)明亮的白色雌澄。specular元素通常被設(shè)置為vec3(1.0f)類型的全強(qiáng)度發(fā)光。要記住的是我們同樣把光的位置添加到結(jié)構(gòu)體中杯瞻。
在程序中設(shè)置light.position
GLint lightPosLoc = glGetUniformLocation (lightingShader.Program, "light.position");
glUniform3f (lightPosLoc, lightPos.x, lightPos.y, lightPos.z);
就像材質(zhì)uniform一樣镐牺,需要更新片段著色器:
vec3 ambient = light.ambient * material.ambient;
vec3 diffuse =light.diffuse * (diff * material.diffuse);
vec3 specular = light.specular * (spec * material.specular);
然后我們要在應(yīng)用里設(shè)置光的亮度:
GLint lightAmbientLoc = glGetUniformLocation (lightingShader.Program, "light.ambient");
GLint lightDiffuseLoc = glGetUniformLocation (lightingShader.Program, "light.diffuse");
GLint lightSpecularLoc = glGetUniformLocation (lightingShader.Program, "light.specular");
glUniform3f (lightAmbientLoc, 0.2f, 0.2f, 0.2f);
glUniform3f (lightDiffuseLoc, 0.5f, 0.5f, 0.5f); // 讓我們把這個(gè)光調(diào)暗一點(diǎn),這樣會(huì)看起來(lái)更自然
glUniform3f (lightSpecularLoc, 1.0f, 1.0f, 1.0f);
現(xiàn)在魁莉,我們調(diào)整了光是如何影響物體所有的材質(zhì)的睬涧,我們得到一個(gè)更像前面教程的視覺輸出。這次我們完全控制了物體光照和材質(zhì):
現(xiàn)在改變物體的外觀相對(duì)簡(jiǎn)單了些旗唁。我們做點(diǎn)更有趣的事畦浓!
不同的光源顏色
目前為止,我們使用光源的顏色僅僅去改變物體各個(gè)元素的強(qiáng)度(通過(guò)選用從白到灰到黑范圍內(nèi)的顏色)检疫,并沒有影響物體的真實(shí)顏色(只是強(qiáng)度)讶请。由于現(xiàn)在能夠非常容易地訪問(wèn)光的屬性了,我們可以隨著時(shí)間改變它們的顏色來(lái)獲得一些有很意思的效果屎媳。由于所有東西都已經(jīng)在片段著色器做好了夺溢,改變光的顏色很簡(jiǎn)單论巍,我們可以立即創(chuàng)建出一些有趣的效果:
視頻演示
如你所見,不同光的顏色極大地影響了物體的顏色輸出风响。由于光的顏色直接影響物體反射的顏色(你可能想起在顏色教程中有討論過(guò))嘉汰,它對(duì)視覺輸出有顯著的影響。
利用sin和glfwGetTime改變光的環(huán)境和漫反射顏色钞诡,我們可以隨著時(shí)間流逝簡(jiǎn)單的改變光源顏色:
glm::vec3 lightColor;
lightColor.x = sin (glfwGetTime () * 2.0f);
lightColor.y = sin (glfwGetTime () * 0.7f);
lightColor.z = sin (glfwGetTime () * 1.3f);
glm::vec3 diffuseColor = lightColor * glm::vec3 (0.5f);
glm::vec3 ambientColor = diffuseColor * glm::vec3 (0.2f);
glUniform3f (lightAmbientLoc, ambientColor.x, ambientColor.y, ambientColor.z);
glUniform3f (lightDiffuseLoc, diffuseColor.x, diffuseColor.y, diffuseColor.z);
嘗試和實(shí)驗(yàn)使用這些光照和材質(zhì)值,看看它們?cè)鯓佑绊憟D像輸出的湃崩。你可以從這里找到程序的源碼荧降。
練習(xí)
你能像我們教程一開始那樣根據(jù)一些材質(zhì)的屬性來(lái)模擬一個(gè)真實(shí)世界的物體嗎? 注意材質(zhì)表中的環(huán)境光顏色與漫反射光的顏色可能不一樣攒读,因?yàn)樗麄儾]有把光照強(qiáng)度考慮進(jìn)去來(lái)模擬朵诫,你需要將光照顏色的強(qiáng)度改為vec(1.0f)
來(lái)輸出正確的結(jié)果:參考解答,我做了一個(gè)青色(Cyan)的塑料箱子薄扁。