OpenGL光照模型–四種光照效果

NO IMAGE

OpenGL光照模型

          為了能看出3D效果,給場景中新增光源。如果沒有光照,繪出的球看上去和一個二維平面上圓沒什麼差別,如下圖,左邊為有光照效果的球體,右邊為同一個球體但沒有設定光源,看上去就沒有立體效果,因此OpenGL 光照效果對顯示3D效果非常明顯。

在OpenGL
光照模型中光源和光照效果可以細分為紅,綠,藍三個部分,光源由紅,綠,藍強度來定義,而物體表面材料由其反射紅,綠,藍的程度和方向來定義。OpenGL 光照模型使用的計算公式是對於現實世界光照的一個近似但效果非常好並適合快速計算。

OpenGL 光照模型中定義的光源可以分別控制,開啟或關閉,OpenGL ES支援最多八個光源。

OpenGL 光照模型中最終的光照效果可以分為四個組成部分:Emitted(光源), ambient(環境光),diffuse(漫射光)和specular(鏡面反射光),最終結果由這四種光疊加而成。

Emitted : 一般只發光物體或者光源,這種光不受其它光源的影響。

ambient: 指光線經過多次反射後已經無法得知其方向(可以看作來自所有方向),可以成為環境光,該光源如果射到某個平面,其反射方向為所有方向。Ambient 不依賴於光源的方向。

diffuse:當一束平行的入射光線射到粗糙的表面時,因面上凹凸不平,所以入射線雖然互相平行,由於各點的法線方向不一致,造成反射光線向不同的方向無規則地反射,這種反射稱之為“漫反射”或“漫射”。這個反射的光則稱為漫射光。漫射光射到某個平面時,其反射方向也為所有方向。diffuse 只依賴於光源的方向和法線的方向。

specular : 一般指物體被光源直射的高亮區域,也可以成為鏡面反射區,如金屬。specular依賴於光源的方向,法線的方向和視角的方向。

儘管光源可能只傳送某一頻率的光線,但ambient,diffuse和specular可能不同。比如使用白光照射一堵紅牆,散射的光線可能為紅色。OpenGL允許為光源分別設定紅,綠,藍三個元素的值。

最終決定所看到物體的顏色除了光源的顏色和方向外,還取決於物體本身的顏色,比如紅色的光照在紅色的物體和藍色的物體,最終看到的物體一個還是紅色,一個為黑色。OpenGL 中對物體材料(Material)的顏色是通過其反射紅,綠,藍的比例來定義的。 和光源一樣,物體的顏色也可以有不同的ambient,diffuse和specular,表現為反射這些光的比例。ambient,diffuse反射通常為同樣的顏色,而specular常常表現為白色或灰色光,如使用白光照射一個紅色的球,球的大部分割槽域顯示為紅色,而高亮區域為白色。

1.Lambert模型(漫反射)

環境光:

Iambdiff = Kd*Ia

其中Ia 表示環境光強度,Kd(0<K<1)為材質對環境光的反射係數,Iambdiff是漫反射體與環境光互動反射的光強。

方向光:

Ildiff = Kd * Il * Cos(θ)

其中Il是點光源強度,θ是入射光方向與頂點法線的夾角,稱入射角(0<=A<=90°),Ildiff是漫反射體與方向光互動反射的光強,若 N為頂點單位法向量,L表示從頂點指向光源的單位向量(注意頂點指向光源),則Cos(θ)等價於dot(N,L),故又有:

Ildiff = Kd * Il * dot(N,L)

最後綜合環境光和方向光源,Lambert光照模型可以寫成:

Idiff = Iambdiff Ildiff = Kd * Ia Kd * Il * dot(N,L)

2.Phong模型(鏡面反射)

Phong模型認為鏡面反射的光強與反射光線和視線的夾角相關:

Ispec = Ks * Il * ( dot(V,R) )^Ns

其中Ks 為鏡面反射係數,Ns是高光指數,V表示從頂點到視點的觀察方向,R代表反射光方向。由於反射光的方向R可以通過入射光方向L(從頂點指向光源)和物體的法向量求出,
R L = 2 * dot(N, L) * N  即 R = 2 * dot(N,L) * N – L

所以最終的計算式為:

Ispec = Ks * Il * ( dot(V, (2 * dot(N,L) * N – L ) )^Ns

3.Blinn-Phong光照模型(修正鏡面光)

Blinn-Phong是一個基於Phong模型修正的模型,其公式為:

Ispec = Ks * Il * ( dot(N,H) )^Ns

其中N是入射點的單位法向量,H是光入射方向L和視點方向V的中間向量,通常也稱之為半形向量(半形向量被廣泛用於各類光照模型,原因不但在於半形向量蘊含的資訊價值,也在於半形向量是很簡單的計算:H = (L V) / |L V|  )。

4.Rendering Equation(全域性光照模型)

Rendering Equation 是Kajia在1986年提出的,

Lo(X, Wo) = Le(X, Wo) ∫fr(X, Wi, Wo) Li(X, Wi) dot(N, Wi) dWi

其中X表示入射點,Lo(X, Wo)即從物體表面X點,沿方向Wo反射的光強,Le(X, Wo)表示從物體表面X以方向Wo 發射出去的光強,該值僅對自發光體有效,fr(X, Wi, Wo)為,入射光線方向為Wi, 照射到點X上,然後從Wo方向發射出去的BRDF值,Li(X, Wi)為入射方向為Wi照射到點X上的入射光強,N表示點X處的法向量,然後對入射方向進行積分(因為光線入射的方向是四面八方的,積分的意義是對每個方向進行一遍計算後相加),計算的結果就是全域性光照的輻射率。

對於單個點光源照射到不會自發光的物體上,公式可以簡化成:

Lo(X, Wo) = fr(X, Wi, Wo) Li(X, Wi) dot(N, Wi)

這個公式非常有用,通常會將該公式分解為漫反射表示式和鏡面表示式之和。對於漫反射表面,BRDF可以忽略不計,因為它總是返回某個恆定值,所以可以寫成如下形式:

Lo(X, Wo) = Idiff frs(X, Wi, Wo) Li(X, Wi) dot(N, Wi)

其中Idiff表示漫反射分量,使用公式的計算方法,frs(X, Wi, Wo)表示鏡面反射的BRDF函式,前面的Phong高光模型,其實是rendering equation在單一光源下針對理想鏡面反射的特定推導,對於Phong高光而言:

frs(X, Wi, Wo) = Ks (dot(N, H)^Ns  / dot(N, Wi)

//————————————————————————————————————————-

幾種光照模型的比較

Lambert 模型能夠較好地表現粗糙表面上的光照現象,如石灰牆,紙張等等,但是在渲染金屬材質製成的物體時,則會顯得呆板,表現不出光澤,主要原因是其沒有考慮到鏡面反射效果,所以Phong模型對其進行了很好的補充。由於Blinn-phng光照模型混合了Lambert的漫射部分和標準的高光,渲染效果有時會比 Phong高光更柔和,有些人認為phong光照模型比blinn-phong更加真實,實際上也是如此,Blinn-phong渲染效果要更加柔和一些,但是由於Blinn-phong的光照模型省去了計算反射光線方向向量的兩個乘法運算,速度更快,因此成為許多CG軟體中預設的光找渲染方法,此外它也繼承在了大多數圖形晶片中,用以產生實時的快速渲染。在OpenGL和Direct3D渲染管線中,Blinn-Phong就是預設的渲染模型。
Rendering Equation是基於物理光學的模型,其對於觀察方向上的輻射率進行了本質上的量化,Phong模型只是其特定BRDF的推導。

以下給出Blinn-Phong的CG片段,用於參考實現:

struct VertexScreen
{
    float4 oPosition : POSITION;
    float4 objectPos : TEXCOORD0;
    float4 objectNormal : TEXCOORD1;
};

void main_f(VertexScreen posIn,
    out float4 color : COLOR,
    uniform float4x4 worldMatrix,
    uniform float4x4 worldMatrix_IT,
    uniform float3 globalAmbient,
    uniform float3 eyePosition,
    uniform float3 lightPosition,
    uniform float3 lightColor,
    uniform float3 Kd,
    uniform float3 Ks,
    uniform float shininess)
{
    float3 worldPos = mul(worldMatrix, posIn.objectPos).xyz;
    float3 N = mul(worldMatrix_IT, posIn.objectNormal).xyz;
    N = normalize(N);

    //計算入射光方向\視線方向\半形向量
    float3 L = normalize(lightPosition – worldPos);
    float3 V = normalize(eyePosition – worldPos);
    float3 H = normalize(L V);

    // 計算漫反射分量
    float3 diffuseColor = Kd * globalAmbient Kd*lightColor*max(dot(N, L), 0);
 
    //計算鏡面反射分量
    float3 specularColor = Ks * lightColor*pow(max(dot(N, H), 0), shininess);
    color.xyz = diffuseColor specularColor;
    color.w = 1;
}