OGL(教程17)——環境光
原文地址:http://ogldev.atspace.co.uk/www/tutorial17/tutorial17.html
背景知識:
光照在3D圖形學中很重要。如果把光照加入到模型渲染中會增加場景的逼真程度。使用模擬,是因為它不能100%模擬現實世界。現實的光,是由大量的粒子稱之為光子構成的,表現出粒子特性和波特性。如果你試圖計算每個光子的影響,你的計算機無法做到這個。
因此,幾個光照模型被開發出來,他們模擬光的核心效果,當光照照射到物體的時候。光照模型跟著計算機的升級而變得越來越複雜。在下一節教程開始,我們將會學習基礎的光照模型,他們很簡答。但是對於所有的大氣層中的場景都影響很大。
最基本的光照模型是Ambient/Diffuse/Specular。環境光是光照的一種型別,當你走出室內你就會看到環境光。即使是太陽落下了,它的光射線撞擊到不同的物體,不同的物體就會被看到,即使是在影子裡。由於光在每個它撞擊到物體之間進行多次反射,他不是一條筆直的路徑。甚一個燈泡在室內,看起來像是太陽的感覺。把它的環境的光向四周發射,如果房間足夠大,每個東西都是均等的。環境光模擬的是沒有起點、沒有方向、對每個物體都有相同效果。
漫反射重點強調的是,光是從哪個角度照射到物體表面的,強調的是照射物體的亮度。當光和物體的一個面撞擊,那麼此面就比另外一個面亮一些。我們僅僅看到太陽傳播環境光,沒有特定的方向。但是,太陽也有漫反射屬性。當他撞擊到高的建築物是,你會看到建築物的一面比另外一面亮。漫反射光的最重要的屬性是方向。
鏡面光,是物體的另外一個屬性,而不是光本身的。它使得物體某個部分看起來特別刺眼。這個還和視角的方向有關。金屬物體經常有一些反射屬性,比如一輛車在晴天會有邊緣刺眼的效果。計算鏡面反射的時候要考慮光的入射方向,以及觀察者的觀察方向。
在3D應用中,你清楚不需要建立環境光、漫反射、鏡面反射。反而,你使用光源,比如戶外的太陽,室內的燈泡,或者是巖洞中的聚光燈。這些光源型別會把環境光、漫反射、鏡面反射的效果結合起來。比如,聚光燈,能夠照射特定範圍的物體,在範圍之外的則不會照亮。
在接下來的教程中,我們開發幾種有用的光源型別,學習基本的光照模型。
我們從平行光開始。一個平行光有方向,但是沒有起點。這就意味著,所有的光射線都是平行的。光的方向用一個向量表示,所有的物體都是使用這個向量計算光,而不考慮起點位置。太陽就是平行光。如果你試圖計算兩個相鄰的建築和太陽光的夾角的話,兩個角度是幾乎相同的。因為太陽在150百萬千米之外。因此,我們只考慮其方向。
平行光的另外一個重要的屬性是,它的亮度和距離無關。這個亮度是一個常量值。而點光源,則是距離越遠,其亮度越低。
下圖展示了平行光:
我們已經看到太陽既有環境光也有漫反射屬性。我們本節將介紹如何計算環境光,而漫反射下一節再計算。
上一節中我們介紹瞭如何從一個貼圖中取樣顏色。顏色又三個通道(RGB),每個通道都有一個位元組。那麼意味著,顏色範圍在0到255之間。不同的顏色通道結合起來,就得到不同的顏色。都為0則為黑色,都為255則為白色。其他的東西都在黑白之間。可以對分量做等比縮放,其亮度根據縮放值變暗或者變亮。
當白光撞擊到表面,表面就會反射白色。這個亮度或明或暗,取決於光源的亮度。但是反射都是同樣的基礎色。如果光源是純紅(255,0,0),那麼反射的顏色只能是紅色。這是因為光源沒有綠色和藍色用來反射。如果物體表面是純藍,那麼結果是黑色。基礎的原則是,光源只能反射物體的顏色,但是不能對其著色。
我們定義光源的顏色在[0,1]之間。把這個值乘以物體的顏色,就得到了反射的顏色。但是,我們需要考慮環境光強度。因此,環境光強度會被單做一個因子,用來乘以每個分量。這個就是最終顏色。等式如下:
在本節的教程中,你可以按住a和s鍵來增加和減少環境光的強度。這個只是平行光的環境光部分。這個在下一節的漫反射中會有所變化。目前,我們從任何角度看物體都是一樣的。
環境光的效果大多不被使用。因為,它看起來很假,和過於簡單,其實現不太符合實際。使用高階的方法,例如全域性光照,因為燈光被物體反射又撞擊其他的物體,這些間接光也會被考慮進去。我們不能完全避免環境光,因為要避免某個物體的表面完全黑的情況。因此在最後,可以通引數用來微調下。
程式碼註釋:
我們的程式碼例子,變得越來越複雜。本節,為了實現環境光,我們做了很多的重構工作。我們把程式碼放在更合適的位置,最主要的變化是:
- 封裝了shader的管理類。這個包含了編譯和連結。從現在開始,我們會在在Technique的子類中實現效果。
- 移動GLUT的初始化和回撥管理到GLUTBackend元件中。這個元件註冊自身以及接受來自GLUT的回撥,然後把他們轉向c++應用,這些回撥取樣介面程式設計。
- 把main檔案中的全域性的函式和變數移動到單獨類中。
本節的大多數程式碼,處理燈光的程式碼,不是新的,只是重新組織了下。因此新的標頭檔案需要重新定義下。
(glut_backend.h:24)
void GLUTBackendInit(int argc, char** argv);
bool GLUTBackendCreateWindow(unsigned int Width, unsigned int Height, unsigned int bpp, bool isFullScreen, const char* pTitle);
GLUT的特定的程式碼大多數移動到GLUT backend元件中,這個是GLUT很方便得到初始化,並且建立一個視窗。
(glut_backend.h:28)
void GLUTBackendRun(ICallbacks* pCallbacks);
在GLUT初始化,一個視窗被建立之後,下一步就是執行上面的主迴圈。