1,
漫射光和環(huán)境光的主要不同是漫射光的特性依賴光線的方向,而環(huán)境光完全忽略光的方向。當(dāng)只有環(huán)境光時整個場景是被均勻照亮的,而漫射光使物體朝向它的那一面比其他背向光的面要更亮。漫射光還增加了一個光強(qiáng)度的變化現(xiàn)象,光的強(qiáng)度大小還取決于光線和物體表面的角度。
漫射光模型是建立在蘭伯特余弦定律上的,光線的強(qiáng)度和觀察者視線與物體表面法線夾角的余弦值成正比(夾角越大光強(qiáng)度越?。W⒁膺@里略有變化,我么使用的是光線的方向而不是觀察者視線(在鏡面發(fā)射中用到)的方向。
為了計算光的強(qiáng)度我們要引入光線和物體表面法線(蘭伯特定律中更加通用的概念叫做'directionaly proportional')夾角的余弦值作為一個參數(shù)變量??聪旅孢@幅圖:
圖中四條光線以不同的角度照到表面上(光線僅僅是方向不同),綠色的箭頭是表面法向量,從表面垂直往外發(fā)出。
光線A的強(qiáng)度是大的,因為光線A和法線的夾角為0,余弦值為大的1,
也就是這個光線的強(qiáng)度(三個通道的三個0-1的值)和表面顏色相乘每個顏色通道都是乘以1,這是漫射光強(qiáng)度大的情況了。
光線B以一定角度(0-90之間)照射到表面,這個角度就是光線和法線的夾角,那么夾角的余弦值應(yīng)該在0-1之間,表面顏色值最后要和這個角度的余弦值相乘,那么得到的光的強(qiáng)度一定是比光線A要弱的。
對于光線C和D情況又不同了。C從表面的一側(cè)入射,光線和表面的夾角為0,和法線垂直,對應(yīng)的余弦值為0,這會導(dǎo)致光線C對表面照亮沒有任何效果。光線D從表面的背面入射和法線成鈍角,余弦值為負(fù)比0還小甚至小到-1。所以光線D和C一樣都對物體表面沒有照亮作用。
2,
可以看到表面法線對漫射光的計算很重要。上面的例子是很簡化的:表面是平坦的直線只需要考慮一條法線。而真實世界中的物體有無數(shù)的多邊形組成,每個多邊形的法線和附近的多邊形基本都不一樣。例如:
因為一個多邊形面上分布的任意法向量都是一樣的,足以用其中一個代表來計算頂點著色器中的漫射光。
一個三角形上的三個頂點會有相同的顏色而且整個三角形面的顏色都相同,但這樣看上去效果并不好,每個多邊形之間的顏色值都不一樣這樣我們會看到多邊形之間邊界的顏色變化不平滑。
因此這個明顯是需要進(jìn)行優(yōu)化的:頂點法線。
頂點法線是共用一個頂點的所有三角形法線的平均值。事實上我們并沒有在頂點著色器中計算漫射光顏色,而只是將頂點法線作為一個成員屬性傳給片段著色器。
光柵器會得到三個不同的法向量并對其之間進(jìn)行插值運算。
片段著色器將會對每個像素計算其特定的插值法向量對應(yīng)的顏色值。
這樣使用那個插值后得到的每個像素特定法向量,我們對漫射光的計算可以達(dá)到像素級別。效果是光照效果在每個相鄰三角形面之間會平滑的變化。這種技術(shù)叫做Phong著色(Phong Shading)。下面是頂點法線插值后的樣子:
3,
頂點和他們的法線都定義在本地坐標(biāo)系空間,并且都在頂點著色器中被我們提供給shader的WVP矩陣進(jìn)行了變換,然后到裁剪空間。
然而,在世界坐標(biāo)系中來定義光線的方向才是最合理的,畢竟光線的方向決定于世界空間中某個地方的光源將光線投射到某個方向(甚至太陽都是在世界空間中,只是距離極遠(yuǎn))。所以,在計算之前,我們首先要將法線向量變換到世界坐標(biāo)系空間。
4,
5,
輸入定點、紋理、法線。
6,
初始化光
_gl_shardes->gluniform_light(_directionallight);
gDirectionalLight.Direction光源向量不是單位化的。如果對所有像素的同一個向量都進(jìn)行反復(fù)單位化會很浪費GPU資源。
因此我們只要保證應(yīng)用程序傳遞的向量在draw call之前被單位化即可。
direction.Normalize();
glUniform3f(_dirlight_directionlocation, direction.x, direction.y, direction.z);