Per Pixel Lighting Techniques
飄飄白云譯 2008-03-22
原文鏈接:http://www.nitrogen.za.org/viewtutorial.asp?id=5
這個教程用到了向量數學知識,如果你對向量數學還不是很了解,請先閱讀向量教程:read the tutorial。
光照與物體表面的相互作用可以通過將一些數學公式應用于基于per pixel(區別于基于頂點)的著色,從而模擬出真實生活中的各種材質效果。比如浮雕效果,波浪效果,油漆效果等。
在這個教程中,我們有如下假定:
第一,我們討論的是基于像素著色(per-pixel basis),每個pixel有它自己的位置向量,法線向量以及表面顏色(Surface color,在這里可以是來自紋理的顏色,也可以是RGB顏色(flat color));
第二,表面顏色(Surface color)通常是由R,G,B三部分組成,在這個教程中,我們把它當作一個向量看待;
第三,輸入表面顏色(光照處理前的表面顏色,這里的“輸入”可以理解為函數的輸入參數的“輸入”)只是普通的顏色(單純的紋理顏色或者RGB顏色),而輸出表面顏色(光照處理后的表面顏色)是光照作用于表面的合成顏色,如可以是有陰影,高光等效果的顏色。
第四,這個教程中假設每個場景中只有一個燈光。對于多燈光的場合,對每一個燈光循環進行這些運算(環境光除外)。
好了,讓我們開始講解各種光照的算法原理
Ambient Lighting 環境光
在真實生活里,有光線的房子里的物體不會是全黑的,總有一些光量子照亮物體表面,即使這個表面是背對光源的,這就是環境光的原因。我們不考慮環境光的照射方向,我們總認為場景中的物體,不論它在什么位置,總會受到一定數量的環境光照射(全局照明)。環境光照算法如下:
Inputs:
Col – 物體原表面顏色
AmbAmount – 場景中環境光的強弱程度 (介于0 到 1之間)
Outputs:
SurfaceColor – 環境光照作用之后的表面顏色
SurfaceColor = Col*AmbAmount;

環境光照效果圖:
Lambert Shading (郎伯特著色,郎伯特:物理上的亮度單位,在這里就是漫射光作用)
現在我們真正開始考慮一束光照射在物體表面上的作用過程,我們使用最常見的光照算法-------漫反射光照著色或者說郎伯特余弦定律或郎伯特著色(三個都一回事),這個算法是將入射光與表面法線向量的點積當作漫反射光照強度因子,下面我們看看環境光照與漫射光照共同作用的算法:
Inputs:
LCol – 照射在表面上的漫射光
Pos – 表面上被照射的位置
LPos – 漫射光源的位置
N -表面上被照射的位置處的法向量
Col –物體原表面顏色
AmbAmount -場景中環境光的強弱程度 (0 to 1)
Outputs:
SurfaceColor -環境光照與漫射光照共同作用之后的表面顏色
VectorToLight = Normalise(LPos - Pos);
DiffuseFactor = Dot(VectorToLight, Normal); //DiffuseFactor ranges from 0 to 1
//光線與表面法線夾角大于90度,想像下光線在表面背面射過來,正表面肯定沒有光照
if(DiffuseFactor < 0)
then DiffuseFactor = 0;
//環境光照與漫射光照共同作用
SurfaceColor = Col*AmbAmount + Col*DiffuseFactor*LCol;

環境光與漫射光共同作用效果
Specular Highlights鏡面高光
現在我們考慮物體表面有光澤的效果,這種效果是將Phong反射模型,結合前面兩個光照作用而成。這中光照效果計算需要知道觀察者在場景中的位置,而先前的環境光照與漫射光照效果計算都與觀察者所在位置無關的。
這種光照計算是這樣的,首先我們計算入射光在表面處的反射光線,然后再將反射光線與視線(觀察者的眼睛與表面觀察點的連線)之間的點積值當作反射到觀察者眼中的光照強度因子,因為表面上高亮的部分是反射光線反射到觀察者眼睛或照相機中較多的地方,這些地方的反射光線與視線之間的夾角非常小,點積值就越大。
Inputs:
ViewPos – 觀察者的位置
SpecAmount – 鏡面光強弱. (from 0 to about 200)
SpecCol – 鏡面光顏色(通常為白色).
LCol – 照射在表面上的漫射光
Pos – 表面上被照射的位置
LPos – 漫射光源的位置
N -表面上被照射的位置處的法向量
Col –物體原表面顏色
AmbAmount -場景中環境光的強弱程度 (0 to 1)
Outputs:
SurfaceColor -環境光照,漫射光照與鏡面光共同作用之后的表面顏色
DiffuseFactor = ... //經前兩個光照作用得來的顏色
DirectionToViewer = Normalise(ViewPos - Pos);
VectorToLight = Normalise(LPos - Pos);
//計算反射光
ReflectanceRay = 2 * Dot(N, VectorToLight) * N - VectorToLight;
//計算鏡面光照因子. 數學公式 SpecFac = (R dot N)^Spec
SpecularFactor = Pow(Dot(ReflectanceRay, DirectionToViewer), SpecAmount);
//環境光照,漫射光照與鏡面光共同作用
SurfaceColor = Col*AmbFactor + Col*DiffuseFactor*LCol + SpecCol*SpecularFactor;

環境光照,漫射光照與鏡面光共同作用
Note:可以在一個場景中使用多個漫射光照與鏡面光作用
Fresnel Term 菲涅爾準則
菲涅爾效果是根據觀察者的觀察表面來調整反射率來實現的。比如你從水面,油漆表面或者絲綢的正上方看,反射光澤的柔和效果基本沒有,如果側著或平著看的話,反射光澤的柔和效果就很明顯,這就是菲涅爾效果。我們簡單地通過點積操作計算表明法線與視線之間夾角的余弦值,再將這個值加權。對于較平滑表面,加權系數設置在1.0-5.0之間(油漆效果,絲綢等),對于比較凹凸的表面,加權系數設置為8.0或更高(水波,液體等)
Inputs:
ViewPos – 觀察者的位置
FresAmount – 邊緣或表面的尖銳程度. (油漆絲綢:1,液體: 2-8)
FresCol - frenel 反射光 (通常使用reflection map or 類似的東西).
LCol – 照射在表面上的漫射光
Pos – 表面上被照射的位置
LPos – 漫射光源的位置
N -表面上被照射的位置處的法向量
Col –物體原表面顏色
AmbAmount -場景中環境光的強弱程度 (0 to 1)
Outputs:
SurfaceColor -環境光照,漫射光照與鏡面光,菲涅爾反射共同作用之后的表面顏色
DiffuseFactor = ... //環境光照,漫射光照作用得來的顏色
SpecularFactor = ... //鏡面高光作用得來的顏色
DirectionToViewer = Normalise(ViewPos - Pos);
//計算fresnel因子. 我們計算視線與表面法向量間夾角的余弦值(在[-1..1]之間),然后加一,移動到區間[0..2],然后再加權。
FresnelTerm = Pow(Dot(N, DirectionToViewer)+1, FresAmount);
//確保因子的在正常范圍內
if (FresnelTerm > 1)
then FresnelTerm = 1;
//無菲涅爾反射的場合: Ambient light, Diffuse Light and Specular Light
NonReflective = Col*AmbFactor + Col*DiffuseFactor*LCol + SpecCol*SpecularFactor;
Reflective = FresCol;
//環境光照,漫射光照與鏡面光,菲涅爾反射共同作用
SurfaceColor = NonReflective*(1-FresnelTerm) + Reflective*FresnelTerm;

漫射無菲涅爾反射時效果 漫射有菲涅爾反射時效果