轉自:http://hi.baidu.com/gltracy/item/37f5852939404af850fd87d5
我簡單地介紹一下Phong光照模型...雖然這種光照模型算是比較古老的,
但是通過合適的參數設置,仍然可以實現真實的效果。
我們的目標是渲染出以下效果,這里以Doom3的男主角為例(終於不是怪物了...)
一般而言,Phong光照模型分為三個累加階段:漫反射,鏡面反射和環境光
以下按順序說明:
1.漫反射(Diffuse)
對於表面比較粗糙的物體,基本表面的明暗就是漫反射效果,比如褲子的材質。某一個象素的明暗系數只取決於該點與光源的相對位置,而與眼睛的位置無關。
diffuse_color = base_map * ( N * V_L )* mat_diffuse * light_diffuse;
上式中base_map代表該點的基礎顏色,一般通過紋理索引;
N 是該點的法線;
V_L是該點到光源方向的單位向量;
N * V_L 即兩者的點積,注意需要用max( ( N * V_L ), 0 ),因為負的值是沒有意義的;
mat_diffuse和light_diffuse分別是材質和光源的漫反射系數,用來宏觀調制漫反射的顏色。
下圖是只有N * V_L 項的渲染結果:
2.鏡面反射(Specular)
盡管漫反射已經可以很好地表達光照,但是由於它是與視點無關的,所以多少有些欠缺生動,
而鏡面反射是視點相關的,所以會隨着眼睛位置的變化而“流動”。
specular_color = pow( v_e' * v_l, gloss ) * mat_specular * light_specular;
這里解釋一下:
v_e' 是e_v的反射向量(v_e' = reflect( e_v, n ) ),
而e_v是眼睛到該點的單位向量,
所謂反射向量,即以與該點的法線所垂直的平面做鏡面反射所得到的向量
(雖然按照原理來說,應該是光線反射,而非視線反射...都沒有問題吧);
v_l是該點到光源的單位向量;
v_e' * v_l 是兩者的點積, 同樣要用max(v_e' * v_l, 0),負的值也是不需要的;
pow是冪積,點積是底,gloss是指數;
gloss用來調制亮斑的大小,一般來說,gloss越大,光斑越細小,gloss越小,光斑分布越寬泛。
mat_specular,light_specular分別是材質和光源的鏡面反射系數,用來宏觀調制鏡面反射的顏色。
注意不是點乘,而是各項分別相乘,就是所謂的調制...
下圖是只有鏡面反射系數的渲染結果:
3.環境光(ambient)
光照不到的地方是不是就是全黑的呢,對於受空氣散射影響比較大的環境來說不是的
(比如說室外環境),這里只是簡單的對base_map調制了一下:
ambient_color = base_map * mat_amvient * light_ambient;
4.累加(accumulate)
最后就是將他們相加了:
result_color = diffuse_color + specular_color + ambient_color;
5.其他
(1)在以上式中所使用的法線是由各頂點的法線插值得到的,我們可以使用法線貼圖來編碼
各點的實際法線值,以獲得更細致的表面:
這里是將一個法線的三維向量封裝入紋理的RGB分量,另外還涉及一個世界空間到紋理空間的轉換,
這里不做描述,因為我的實際做法是錯誤的,卻換來正確的結果,所以...我也不清楚啦...
(2)使用一張單獨的圖來編碼各點的鏡面反射系數,比如有油漆的地方反射較弱,而磨光的地方則反射較強烈(見盔甲部分)
(3)以上效果都是利用光照產生的立體錯覺,
如果失去光照,立體感也將隨之消失。
對於環境光,我們應該有更好的處理方法,如利用虛擬的光源等等。
我在該渲染中使用了兩盞燈,產生的效果還不錯。
(4)說到底,phong模型對金屬表面渲染很成功,但對人物表面渲染欠佳,所以才有SSS等等更為先進的模型;但另一方面,Phong模型仍然是最基礎和重要的光照模型,當初的固定流水線采用的就是它(當然鏡面反射略有不同)
先這些吧....