1. 程式人生 > >OpenGL學習腳印:Blinn-Phong光照模型

OpenGL學習腳印:Blinn-Phong光照模型

寫在前面
在前面基礎光照部分,我們學習了Phong Shading模型,Blinn-Phong模型對Phong模型的鏡面光成分進行了改進,雖然在物理上解釋沒有Phong好,但是能更好地模擬光照。本節程式碼可以在我的github下載

Phong不能處理的情況

我們知道,Phong模型在計算鏡面光係數為:

float   specFactor = pow(max(dot(reflectDir, viewDir), 0.0), 32); // 32為鏡面高光係數

這裡的計算由反射向量和觀察向量決定,當兩者的夾角θ超過90時,截斷為0.0,則沒有了鏡面光成分。因此Phong模型能處理的是下面的左圖中(θ

90)的情況,而對於右圖中(θ>90)的情況則鏡面光成分計算為0(來自Advanced-Lighting)。
這裡寫圖片描述

而右圖的這種情況實際上是存在的,將鏡面光成分取為0,沒有很好地體現實際光照情況。例如下面的圖表示的是,鏡面光係數為1.0,法向量為(0.0,1.0,0.0)的平面位置在-0.5,光源在原點時,觀察者在(0,0,4.0)位置時,光照展示的情形:

phong模型的邊緣問題

這裡我們看到,Phong的鏡面光成分,在邊緣時立馬變暗,這種對比太明顯,不符合實際情形。

為什麼會產生這樣一個光線明暗分明的情形? 我嘗試這樣推導,對此不感興趣地可以跳過。

首先記表面位置為fragPos=(x,0.5,z), 光源位置為ligh

tPos=(0.0,0.0,0.0),則光照向量為:

L=(lightPosfragPos)=(x,0.5,z)(light direction)
法向量為:
N=(0.0,1.0,0.0)(surface normal)
根據reflect函式的計算原理,得到反射向量為:
R=L2.0dot(N,L)N=(x,0.5,z)2.0(0.5)(0.0,1.0,0.0)=(x,0.5,z)(surface reflection)

設觀察點位置為(x,y,z),則觀察向量為:

V=(x,y,z)fragPos=(xx,y+0.5,zz)(viewer direction)

那麼反射向量和觀察向量的點積為:

dot(R,V)=(xxx2+0.5y+0.25+zzz2)=[(xx2)2+(zz2)20.25(x2+z2+1+2y)]
δ=0.25(x2+z2+1+2y)

0dot(R,V)1,得到:

δ1(xx2)2+(zz2)2δ
由此可以看出,位置在平面y=-0.5上的點,以適當位置觀察時,會形成兩個同心圓,在兩個同心圓之間的部分則滿足0dot(R,V)1,這部分有鏡面光,其餘部分截斷為0.0,立馬變暗,因此有這種明暗對比。
也就是說當觀察向量和反射向量超過90度,這種截斷引起了明顯的明暗對比,這種情形在Blinn-Phong中得到改善。

Blinn-Phong

Blinn-Phong模型鏡面光的計算,採用了半形向量(half-angle vector),這個向量是光照向量L和觀察向量V的取中向量,如下圖所示(來自Blinn-Phong Model):

半形向量

計算為: H=L+V||L+V||

當觀察向量與反射向量越接近,那麼半形向量與法向量N越接近,觀察者看到的鏡面光成分越強。

對比Phong和Blinn-Phong計算鏡面光係數為:

 vec3   viewDir = normalize(viewPos - fs_in.FragPos);
float   specFactor = 0.0;
if(blinn)  // 使用Blinn-P