Opengl-光照-顏色(其實要想做出好看的東西這章最重要了)
前言
前面的基礎章節說了下Opengl入門的一些基礎知識,通過這些基礎知識你可以製作Camera或者讓物體移動啊又或者放一張圖片上去當做紋理貼圖豐富物體的顯示
但是其實不論怎麼折騰你會發現略微呆板,沒有鮮活的感覺。為什麼呢?就是因為我們其實想讓顯示的東西像真實世界一樣,然而卻忽略了世界上最重要的東西-光!是的沒有錯,生活中你睜開眼閉上眼都可以感受到光的存在,看到任何東西都會因為光的原因有所感觸,反觀我們做出來的不論是箱子也好還是帶著貼圖的箱子又或者渲染的箱子,如果我們想讓它更逼真,我們就要有光(佛說要有光放在這?)
相應的同第一章一樣我所寫的還儘量會是一些思想以及總結,可以說是我個人的回顧,又或者說是幫助理解不了一些概念的同學儘量理解明白一些東西。同理肯定是結合我最推薦的文章
顏色
現在來想一件事,其實計算機裡或者說虛擬世界裡並沒有光,那肯定要問了,沒有光怎麼辦?那不是完了?但是非常聰明的前人或者說藝術家們通過在計算機中模擬光來實現我們在真實世界見到的光。有一句話我非常想說:Opengl其實就是在模擬真實世界到虛擬世界中的一個辦法集合。圖形學也是在做這件事
現在回憶一下,至今為止我們看到的螢幕,我們玩的遊戲。展現出的影象圖片顏色,或者說一些3A大作裡面靚麗的世界,都是我們通過模擬光照來讓你的眼鏡感受到了鮮活的感覺.那麼我們如何模擬光照的呢?其實就是通過顏色。有一句話大家一定要好好理解下:我們在現實生活中看到某一物體的顏色並不是這個物體真正擁有的顏色,而是它所反射的(Reflected)顏色。換句話說,那些不能被物體所吸收(Absorb)的顏色(被拒絕的顏色)就是我們能夠感知到的物體的顏色。
舉個列子,我們是如何在計算機中模擬光照和現實顏色的
glm::vec3 lightColor(1.0f, 1.0f, 1.0f);
glm::vec3 toyColor(1.0f, 0.5f, 0.31f);
glm::vec3 result = lightColor * toyColor; // = (1.0f, 0.5f, 0.31f);
可以看到我們定義了光顏色(1,1,1)也就是一個白光[1最大,拉滿了],然後玩具顏色(1,0.5,0.31)要想知道最終結果就是相乘就好了沒有什麼特別的。為什麼?反射學定律啊,就是這樣啊。。別鑽牛角尖老哥。就像顏色為啥可以這麼表示你不是也不知道麼?不是理解的很好麼。
舉個列子:你看到的樹葉是綠色的,那麼是因為這個樹葉反射了綠色的顏色到你的眼睛裡。
從這個例子我們可以瞭解到,其實我們模擬光,並不需要多麼複雜的操作,按照反射或者折射的現象,我們只需要給物體定義一個顏色,然後通過著色器比如頂點啊畫素啊處理好顯示到螢幕上,這個時候綠色的物體就會被我們看到,那我們也就感覺到這個物體是綠色的了。
雖然說了儘量不寫程式碼,只講思想,但是感覺稍微貼點程式碼可能理解的會更清楚。
頂點著色器
#version 330 core
layout (location = 0) in vec3 aPos;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
}
片段著色器
#version 330 core
out vec4 FragColor;
uniform vec3 objectColor;
uniform vec3 lightColor;
void main()
{
FragColor = vec4(lightColor * objectColor, 1.0);
}
相信這兩個著色器大家都比較熟悉,前面基礎章節也都解釋過這些著色器了基本都是固定的格式,這裡的變化只不過多了uniform 關於uniform的使用前面都介紹過了這裡不多贅述。
可以看到我們通過外部傳入兩個變數一個物體顏色,一個光的顏色最後改變了內建變數FragColor也就是片段(畫素)的顏色,然後你從顯示器上看到的也就是你傳入的顏色和物體顏色相乘的結果了。
在外部程式碼中傳入uniform部分:
lightingShader.setVec3("objectColor", 1.0f, 0.5f, 0.31f);
lightingShader.setVec3("lightColor", 1.0f, 1.0f, 1.0f);
關於資料方面:
unsigned int lightVAO;
glGenVertexArrays(1, &lightVAO);
glBindVertexArray(lightVAO);
// 只需要繫結VBO不用再次設定VBO的資料,因為箱子的VBO資料中已經包含了正確的立方體頂點資料
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// 設定燈立方體的頂點屬性(對我們的燈來說僅僅只有位置資料)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
最終跑出來的結果應該是像這樣子的
可以看到白色的那個正是我們設定的光源